...

Source file src/github.com/letsencrypt/boulder/sa/rate_limits.go

Documentation: github.com/letsencrypt/boulder/sa

     1  package sa
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/letsencrypt/boulder/db"
     9  	sapb "github.com/letsencrypt/boulder/sa/proto"
    10  	"github.com/weppos/publicsuffix-go/publicsuffix"
    11  )
    12  
    13  // baseDomain returns the eTLD+1 of a domain name for the purpose of rate
    14  // limiting. For a domain name that is itself an eTLD, it returns its input.
    15  func baseDomain(name string) string {
    16  	eTLDPlusOne, err := publicsuffix.Domain(name)
    17  	if err != nil {
    18  		// publicsuffix.Domain will return an error if the input name is itself a
    19  		// public suffix. In that case we use the input name as the key for rate
    20  		// limiting. Since all of its subdomains will have separate keys for rate
    21  		// limiting (e.g. "foo.bar.publicsuffix.com" will have
    22  		// "bar.publicsuffix.com", this means that domains exactly equal to a
    23  		// public suffix get their own rate limit bucket. This is important
    24  		// because otherwise they might be perpetually unable to issue, assuming
    25  		// the rate of issuance from their subdomains was high enough.
    26  		return name
    27  	}
    28  	return eTLDPlusOne
    29  }
    30  
    31  // addCertificatesPerName adds 1 to the rate limit count for the provided
    32  // domains, in a specific time bucket. It must be executed in a transaction, and
    33  // the input timeToTheHour must be a time rounded to an hour.
    34  func (ssa *SQLStorageAuthority) addCertificatesPerName(ctx context.Context, db db.SelectExecer, names []string, timeToTheHour time.Time) error {
    35  	// De-duplicate the base domains.
    36  	baseDomainsMap := make(map[string]bool)
    37  	var qmarks []string
    38  	var values []interface{}
    39  	for _, name := range names {
    40  		base := baseDomain(name)
    41  		if !baseDomainsMap[base] {
    42  			baseDomainsMap[base] = true
    43  			values = append(values, base, timeToTheHour, 1)
    44  			qmarks = append(qmarks, "(?, ?, ?)")
    45  		}
    46  	}
    47  
    48  	_, err := db.ExecContext(ctx, `INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES `+
    49  		strings.Join(qmarks, ", ")+` ON DUPLICATE KEY UPDATE count=count+1;`,
    50  		values...)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  // countCertificates returns the count of certificates issued for a domain's
    59  // eTLD+1 (aka base domain), during a given time range.
    60  func (ssa *SQLStorageAuthorityRO) countCertificates(ctx context.Context, dbMap db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) {
    61  	latest := time.Unix(0, timeRange.LatestNS)
    62  	var results []struct {
    63  		Count int64
    64  		Time  time.Time
    65  	}
    66  	_, err := dbMap.Select(
    67  		ctx,
    68  		&results,
    69  		`SELECT count, time FROM certificatesPerName
    70  		 WHERE eTLDPlusOne = :baseDomain AND
    71  		 time > :earliest AND
    72  		 time <= :latest`,
    73  		map[string]interface{}{
    74  			"baseDomain": baseDomain(domain),
    75  			"earliest":   time.Unix(0, timeRange.EarliestNS),
    76  			"latest":     latest,
    77  		})
    78  	if err != nil {
    79  		if db.IsNoRows(err) {
    80  			return 0, time.Time{}, nil
    81  		}
    82  		return 0, time.Time{}, err
    83  	}
    84  	// Set earliest to the latest possible time, so that we can find the
    85  	// earliest certificate in the results.
    86  	var earliest = latest
    87  	var total int64
    88  	for _, r := range results {
    89  		total += r.Count
    90  		if r.Time.Before(earliest) {
    91  			earliest = r.Time
    92  		}
    93  	}
    94  	if total <= 0 && earliest == latest {
    95  		// If we didn't find any certificates, return a zero time.
    96  		return total, time.Time{}, nil
    97  	}
    98  	return total, earliest, nil
    99  }
   100  
   101  // addNewOrdersRateLimit adds 1 to the rate limit count for the provided ID, in
   102  // a specific time bucket. It must be executed in a transaction, and the input
   103  // timeToTheMinute must be a time rounded to a minute.
   104  func addNewOrdersRateLimit(ctx context.Context, dbMap db.SelectExecer, regID int64, timeToTheMinute time.Time) error {
   105  	_, err := dbMap.ExecContext(ctx, `INSERT INTO newOrdersRL
   106  		(regID, time, count)
   107  		VALUES (?, ?, 1)
   108  		ON DUPLICATE KEY UPDATE count=count+1;`,
   109  		regID,
   110  		timeToTheMinute,
   111  	)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	return nil
   116  }
   117  
   118  // countNewOrders returns the count of orders created in the given time range
   119  // for the given registration ID.
   120  func countNewOrders(ctx context.Context, dbMap db.Selector, req *sapb.CountOrdersRequest) (*sapb.Count, error) {
   121  	var counts []int64
   122  	_, err := dbMap.Select(
   123  		ctx,
   124  		&counts,
   125  		`SELECT count FROM newOrdersRL
   126  		WHERE regID = :regID AND
   127  		time > :earliest AND
   128  		time <= :latest`,
   129  		map[string]interface{}{
   130  			"regID":    req.AccountID,
   131  			"earliest": time.Unix(0, req.Range.EarliestNS),
   132  			"latest":   time.Unix(0, req.Range.LatestNS),
   133  		},
   134  	)
   135  	if err != nil {
   136  		if db.IsNoRows(err) {
   137  			return &sapb.Count{Count: 0}, nil
   138  		}
   139  		return nil, err
   140  	}
   141  	var total int64
   142  	for _, count := range counts {
   143  		total += count
   144  	}
   145  	return &sapb.Count{Count: total}, nil
   146  }
   147  

View as plain text