ip_limit_allowlist.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package job
  2. import (
  3. "net/netip"
  4. "strings"
  5. )
  6. // ipLimitAllowlist holds the operator's trusted addresses and networks. An IP
  7. // that matches is neither counted towards a client's IP limit nor banned:
  8. // counting it would still cut the office or campus NAT the entry exists to
  9. // protect, which is the whole point of the setting (#5378).
  10. type ipLimitAllowlist struct {
  11. prefixes []netip.Prefix
  12. addrs []netip.Addr
  13. }
  14. // parseIpLimitAllowlist reads the comma-separated form the settings validator
  15. // enforces, each entry either a CIDR or a bare address. Entries that do not
  16. // parse are skipped rather than failing the scan: the validator rejects them on
  17. // save, so anything reaching here is either valid or a hand-edited database.
  18. func parseIpLimitAllowlist(raw string) ipLimitAllowlist {
  19. var list ipLimitAllowlist
  20. for _, field := range strings.Split(raw, ",") {
  21. field = strings.TrimSpace(field)
  22. if field == "" {
  23. continue
  24. }
  25. if prefix, err := netip.ParsePrefix(field); err == nil {
  26. list.prefixes = append(list.prefixes, prefix.Masked())
  27. continue
  28. }
  29. if addr, err := netip.ParseAddr(field); err == nil {
  30. list.addrs = append(list.addrs, addr.Unmap())
  31. }
  32. }
  33. return list
  34. }
  35. func (l ipLimitAllowlist) empty() bool {
  36. return len(l.prefixes) == 0 && len(l.addrs) == 0
  37. }
  38. func (l ipLimitAllowlist) contains(ip string) bool {
  39. if l.empty() {
  40. return false
  41. }
  42. addr, err := netip.ParseAddr(strings.TrimSpace(ip))
  43. if err != nil {
  44. return false
  45. }
  46. addr = addr.Unmap()
  47. for _, allowed := range l.addrs {
  48. if allowed == addr {
  49. return true
  50. }
  51. }
  52. for _, prefix := range l.prefixes {
  53. if prefix.Contains(addr) {
  54. return true
  55. }
  56. }
  57. return false
  58. }
  59. // split separates the entries an allowlist protects from the ones the limit
  60. // still applies to, preserving the caller's ordering in both.
  61. func (l ipLimitAllowlist) split(entries []IPWithTimestamp) (limited, allowed []IPWithTimestamp) {
  62. if l.empty() {
  63. return entries, nil
  64. }
  65. limited = make([]IPWithTimestamp, 0, len(entries))
  66. for _, entry := range entries {
  67. if l.contains(entry.IP) {
  68. allowed = append(allowed, entry)
  69. continue
  70. }
  71. limited = append(limited, entry)
  72. }
  73. return limited, allowed
  74. }