github.com/letsencrypt/boulder@v0.20251208.0/ratelimits/utilities.go (about) 1 package ratelimits 2 3 import ( 4 "fmt" 5 "net/netip" 6 "strings" 7 8 "github.com/weppos/publicsuffix-go/publicsuffix" 9 10 "github.com/letsencrypt/boulder/core" 11 "github.com/letsencrypt/boulder/identifier" 12 ) 13 14 // joinWithColon joins the provided args with a colon. 15 func joinWithColon(args ...string) string { 16 return strings.Join(args, ":") 17 } 18 19 // coveringIdentifiers returns the set of "covering" identifiers used to enforce 20 // the CertificatesPerDomain rate limit. For DNS names, this is the eTLD+1 as 21 // determined by the Public Suffix List; exact public suffix matches are 22 // preserved. For IP addresses, the covering prefix is /32 for IPv4 and /64 for 23 // IPv6. This groups requests by registered domain or address block to match the 24 // scope of the limit. The result is deduplicated and lowercased. If the 25 // identifier type is unsupported, an error is returned. 26 func coveringIdentifiers(idents identifier.ACMEIdentifiers) ([]string, error) { 27 var covers []string 28 for _, ident := range idents { 29 cover, err := coveringIdentifier(CertificatesPerDomain, ident) 30 if err != nil { 31 return nil, err 32 } 33 covers = append(covers, cover) 34 } 35 return core.UniqueLowerNames(covers), nil 36 } 37 38 // coveringIdentifier returns the "covering" identifier used to enforce the 39 // CertificatesPerDomain, CertificatesPerDomainPerAccount, and 40 // NewRegistrationsPerIPv6Range rate limits. For DNS names, this is the eTLD+1 41 // as determined by the Public Suffix List; exact public suffix matches are 42 // preserved. For IP addresses, the covering prefix depends on the limit: 43 // 44 // - CertificatesPerDomain and CertificatesPerDomainPerAccount: 45 // - /32 for IPv4 46 // - /64 for IPv6 47 // 48 // - NewRegistrationsPerIPv6Range: 49 // - /48 for IPv6 only 50 // 51 // This groups requests by registered domain or address block to match the scope 52 // of each limit. The result is deduplicated and lowercased. If the identifier 53 // type or limit is unsupported, an error is returned. 54 func coveringIdentifier(limit Name, ident identifier.ACMEIdentifier) (string, error) { 55 switch ident.Type { 56 case identifier.TypeDNS: 57 domain, err := publicsuffix.Domain(ident.Value) 58 if err != nil { 59 if err.Error() == fmt.Sprintf("%s is a suffix", ident.Value) { 60 // If the public suffix is the domain itself, that's fine. 61 // Include the original name in the result. 62 return ident.Value, nil 63 } 64 return "", err 65 } 66 return domain, nil 67 case identifier.TypeIP: 68 ip, err := netip.ParseAddr(ident.Value) 69 if err != nil { 70 return "", err 71 } 72 prefix, err := coveringIPPrefix(limit, ip) 73 if err != nil { 74 return "", err 75 } 76 return prefix.String(), nil 77 } 78 return "", fmt.Errorf("unsupported identifier type: %s", ident.Type) 79 } 80 81 // coveringIPPrefix returns the "covering" IP prefix used to enforce the 82 // CertificatesPerDomain, CertificatesPerDomainPerAccount, and 83 // NewRegistrationsPerIPv6Range rate limits. The prefix length depends on the 84 // limit and IP version: 85 // 86 // - CertificatesPerDomain and CertificatesPerDomainPerAccount: 87 // - /32 for IPv4 88 // - /64 for IPv6 89 // 90 // - NewRegistrationsPerIPv6Range: 91 // - /48 for IPv6 only 92 // 93 // This groups requests by address block to match the scope of each limit. If 94 // the limit does not require a covering prefix, an error is returned. 95 func coveringIPPrefix(limit Name, addr netip.Addr) (netip.Prefix, error) { 96 switch limit { 97 case CertificatesPerDomain, CertificatesPerDomainPerAccount: 98 var bits int 99 if addr.Is4() { 100 bits = 32 101 } else { 102 bits = 64 103 } 104 prefix, err := addr.Prefix(bits) 105 if err != nil { 106 return netip.Prefix{}, fmt.Errorf("building covering prefix for %s: %w", addr, err) 107 } 108 return prefix, nil 109 110 case NewRegistrationsPerIPv6Range: 111 if !addr.Is6() { 112 return netip.Prefix{}, fmt.Errorf("limit %s requires an IPv6 address, got %s", limit, addr) 113 } 114 prefix, err := addr.Prefix(48) 115 if err != nil { 116 return netip.Prefix{}, fmt.Errorf("building covering prefix for %s: %w", addr, err) 117 } 118 return prefix, nil 119 } 120 return netip.Prefix{}, fmt.Errorf("limit %s does not require a covering prefix", limit) 121 }