...

Source file src/github.com/letsencrypt/boulder/ratelimits/names.go

Documentation: github.com/letsencrypt/boulder/ratelimits

     1  package ratelimits
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/letsencrypt/boulder/policy"
    10  )
    11  
    12  // Name is an enumeration of all rate limit names. It is used to intern rate
    13  // limit names as strings and to provide a type-safe way to refer to rate
    14  // limits.
    15  //
    16  // IMPORTANT: If you add a new limit Name, you MUST add it to the 'nameToString'
    17  // mapping and idValidForName function below.
    18  type Name int
    19  
    20  const (
    21  	// Unknown is the zero value of Name and is used to indicate an unknown
    22  	// limit name.
    23  	Unknown Name = iota
    24  
    25  	// NewRegistrationsPerIPAddress uses bucket key 'enum:ipAddress'.
    26  	NewRegistrationsPerIPAddress
    27  
    28  	// NewRegistrationsPerIPv6Range uses bucket key 'enum:ipv6rangeCIDR'. The
    29  	// address range must be a /48. RFC 3177, which was published in 2001,
    30  	// advised operators to allocate a /48 block of IPv6 addresses for most end
    31  	// sites. RFC 6177, which was published in 2011 and obsoletes RFC 3177,
    32  	// advises allocating a smaller /56 block. We've chosen to use the larger
    33  	// /48 block for our IPv6 rate limiting. See:
    34  	//   1. https://tools.ietf.org/html/rfc3177#section-3
    35  	//   2. https://datatracker.ietf.org/doc/html/rfc6177#section-2
    36  	NewRegistrationsPerIPv6Range
    37  
    38  	// NewOrdersPerAccount uses bucket key 'enum:regId'.
    39  	NewOrdersPerAccount
    40  
    41  	// FailedAuthorizationsPerAccount uses bucket key 'enum:regId', where regId
    42  	// is the registration id of the account.
    43  	FailedAuthorizationsPerAccount
    44  
    45  	// CertificatesPerDomainPerAccount uses bucket key 'enum:regId:domain',
    46  	// where name is the a name in a certificate issued to the account matching
    47  	// regId.
    48  	CertificatesPerDomainPerAccount
    49  
    50  	// CertificatesPerFQDNSetPerAccount uses bucket key 'enum:regId:fqdnSet',
    51  	// where nameSet is a set of names in a certificate issued to the account
    52  	// matching regId.
    53  	CertificatesPerFQDNSetPerAccount
    54  )
    55  
    56  // isValid returns true if the Name is a valid rate limit name.
    57  func (n Name) isValid() bool {
    58  	return n > Unknown && n < Name(len(nameToString))
    59  }
    60  
    61  // String returns the string representation of the Name. It allows Name to
    62  // satisfy the fmt.Stringer interface.
    63  func (n Name) String() string {
    64  	if !n.isValid() {
    65  		return nameToString[Unknown]
    66  	}
    67  	return nameToString[n]
    68  }
    69  
    70  // nameToString is a map of Name values to string names.
    71  var nameToString = map[Name]string{
    72  	Unknown:                          "Unknown",
    73  	NewRegistrationsPerIPAddress:     "NewRegistrationsPerIPAddress",
    74  	NewRegistrationsPerIPv6Range:     "NewRegistrationsPerIPv6Range",
    75  	NewOrdersPerAccount:              "NewOrdersPerAccount",
    76  	FailedAuthorizationsPerAccount:   "FailedAuthorizationsPerAccount",
    77  	CertificatesPerDomainPerAccount:  "CertificatesPerDomainPerAccount",
    78  	CertificatesPerFQDNSetPerAccount: "CertificatesPerFQDNSetPerAccount",
    79  }
    80  
    81  // validIPAddress validates that the provided string is a valid IP address.
    82  func validIPAddress(id string) error {
    83  	ip := net.ParseIP(id)
    84  	if ip == nil {
    85  		return fmt.Errorf("invalid IP address, %q must be an IP address", id)
    86  	}
    87  	return nil
    88  }
    89  
    90  // validIPv6RangeCIDR validates that the provided string is formatted is an IPv6
    91  // CIDR range with a /48 mask.
    92  func validIPv6RangeCIDR(id string) error {
    93  	_, ipNet, err := net.ParseCIDR(id)
    94  	if err != nil {
    95  		return fmt.Errorf(
    96  			"invalid CIDR, %q must be an IPv6 CIDR range", id)
    97  	}
    98  	ones, _ := ipNet.Mask.Size()
    99  	if ones != 48 {
   100  		// This also catches the case where the range is an IPv4 CIDR, since an
   101  		// IPv4 CIDR can't have a /48 subnet mask - the maximum is /32.
   102  		return fmt.Errorf(
   103  			"invalid CIDR, %q must be /48", id)
   104  	}
   105  	return nil
   106  }
   107  
   108  // validateRegId validates that the provided string is a valid ACME regId.
   109  func validateRegId(id string) error {
   110  	_, err := strconv.ParseUint(id, 10, 64)
   111  	if err != nil {
   112  		return fmt.Errorf("invalid regId, %q must be an ACME registration Id", id)
   113  	}
   114  	return nil
   115  }
   116  
   117  // validateRegIdDomain validates that the provided string is formatted
   118  // 'regId:domain', where regId is an ACME registration Id and domain is a single
   119  // domain name.
   120  func validateRegIdDomain(id string) error {
   121  	parts := strings.SplitN(id, ":", 2)
   122  	if len(parts) != 2 {
   123  		return fmt.Errorf(
   124  			"invalid regId:domain, %q must be formatted 'regId:domain'", id)
   125  	}
   126  	if validateRegId(parts[0]) != nil {
   127  		return fmt.Errorf(
   128  			"invalid regId, %q must be formatted 'regId:domain'", id)
   129  	}
   130  	if policy.ValidDomain(parts[1]) != nil {
   131  		return fmt.Errorf(
   132  			"invalid domain, %q must be formatted 'regId:domain'", id)
   133  	}
   134  	return nil
   135  }
   136  
   137  // validateRegIdFQDNSet validates that the provided string is formatted
   138  // 'regId:fqdnSet', where regId is an ACME registration Id and fqdnSet is a
   139  // comma-separated list of domain names.
   140  func validateRegIdFQDNSet(id string) error {
   141  	parts := strings.SplitN(id, ":", 2)
   142  	if len(parts) != 2 {
   143  		return fmt.Errorf(
   144  			"invalid regId:fqdnSet, %q must be formatted 'regId:fqdnSet'", id)
   145  	}
   146  	if validateRegId(parts[0]) != nil {
   147  		return fmt.Errorf(
   148  			"invalid regId, %q must be formatted 'regId:fqdnSet'", id)
   149  	}
   150  	domains := strings.Split(parts[1], ",")
   151  	if len(domains) == 0 {
   152  		return fmt.Errorf(
   153  			"invalid fqdnSet, %q must be formatted 'regId:fqdnSet'", id)
   154  	}
   155  	for _, domain := range domains {
   156  		if policy.ValidDomain(domain) != nil {
   157  			return fmt.Errorf(
   158  				"invalid domain, %q must be formatted 'regId:fqdnSet'", id)
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  func validateIdForName(name Name, id string) error {
   165  	switch name {
   166  	case NewRegistrationsPerIPAddress:
   167  		// 'enum:ipaddress'
   168  		return validIPAddress(id)
   169  
   170  	case NewRegistrationsPerIPv6Range:
   171  		// 'enum:ipv6rangeCIDR'
   172  		return validIPv6RangeCIDR(id)
   173  
   174  	case NewOrdersPerAccount, FailedAuthorizationsPerAccount:
   175  		// 'enum:regId'
   176  		return validateRegId(id)
   177  
   178  	case CertificatesPerDomainPerAccount:
   179  		// 'enum:regId:domain'
   180  		return validateRegIdDomain(id)
   181  
   182  	case CertificatesPerFQDNSetPerAccount:
   183  		// 'enum:regId:fqdnSet'
   184  		return validateRegIdFQDNSet(id)
   185  
   186  	case Unknown:
   187  		fallthrough
   188  
   189  	default:
   190  		// This should never happen.
   191  		return fmt.Errorf("unknown limit enum %q", name)
   192  	}
   193  }
   194  
   195  // stringToName is a map of string names to Name values.
   196  var stringToName = func() map[string]Name {
   197  	m := make(map[string]Name, len(nameToString))
   198  	for k, v := range nameToString {
   199  		m[v] = k
   200  	}
   201  	return m
   202  }()
   203  
   204  // limitNames is a slice of all rate limit names.
   205  var limitNames = func() []string {
   206  	names := make([]string, len(nameToString))
   207  	for _, v := range nameToString {
   208  		names = append(names, v)
   209  	}
   210  	return names
   211  }()
   212  
   213  // nameToEnumString converts the integer value of the Name enumeration to its
   214  // string representation.
   215  func nameToEnumString(s Name) string {
   216  	return strconv.Itoa(int(s))
   217  }
   218  
   219  // bucketKey returns the key used to store a rate limit bucket.
   220  func bucketKey(name Name, id string) string {
   221  	return nameToEnumString(name) + ":" + id
   222  }
   223  

View as plain text