diff --git a/README.md b/README.md index 26aa8ec6965..4a252462a34 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ Boulder is divided into the following main components: 4. Certificate Authority 5. Storage Authority 6. Publisher -7. OCSP Updater -8. OCSP Responder +7. OCSP Responder +8. CRL Updater This component model lets us separate the function of the CA by security context. The Web Front End, Validation Authority, OCSP Responder and @@ -43,18 +43,14 @@ Registration Authority. All components talk to the SA for storage, so most lines indicating SA RPCs are not shown here. ```text - +--------- OCSP Updater - | | - v | - CA -> Publisher | - ^ | - | v + CA ---------> Publisher + ^ + | Subscriber -> WFE --> RA --> SA --> MariaDB | ^ Subscriber server <- VA <----+ | | - Browser ------------------> OCSP Responder - + Browser -------------------> OCSP Responder ``` Internally, the logic of the system is based around five types of objects: diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index b13c9d900cd..1ca575e4313 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -54,8 +54,7 @@ type Config struct { // The maximum number of subjectAltNames in a single certificate MaxNames int `validate:"required,min=1,max=100"` - // LifespanOCSP is how long OCSP responses are valid for. It should be - // longer than the minTimeToExpiry field for the OCSP Updater. Per the BRs, + // LifespanOCSP is how long OCSP responses are valid for. Per the BRs, // Section 4.9.10, it MUST NOT be more than 10 days. LifespanOCSP config.Duration diff --git a/cmd/boulder/main.go b/cmd/boulder/main.go index e6cb388975a..4fd8776274e 100644 --- a/cmd/boulder/main.go +++ b/cmd/boulder/main.go @@ -29,7 +29,6 @@ import ( _ "github.com/letsencrypt/boulder/cmd/nonce-service" _ "github.com/letsencrypt/boulder/cmd/notify-mailer" _ "github.com/letsencrypt/boulder/cmd/ocsp-responder" - _ "github.com/letsencrypt/boulder/cmd/ocsp-updater" _ "github.com/letsencrypt/boulder/cmd/orphan-finder" _ "github.com/letsencrypt/boulder/cmd/reversed-hostname-checker" _ "github.com/letsencrypt/boulder/cmd/rocsp-tool" diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go index 4eac889e6f7..ff250d06e9d 100644 --- a/cmd/ocsp-responder/main.go +++ b/cmd/ocsp-responder/main.go @@ -54,25 +54,6 @@ type Config struct { // upstream's timeout when making request to ocsp-responder. Timeout config.Duration `validate:"-"` - // The worst-case freshness of a response during normal operations. - // - // This controls behavior when both Redis and MariaDB backends are - // configured. If a MariaDB response is older than this, ocsp-responder - // will try to serve a fresher response from Redis, waiting for a Redis - // response if necessary. - // - // This is related to OCSPMinTimeToExpiry in ocsp-updater's config, - // and both are related to the mandated refresh times in the BRs and - // root programs (minus a safety margin). - // - // This should be configured slightly higher than ocsp-updater's - // OCSPMinTimeToExpiry, to account for the time taken to sign - // responses once they pass that threshold. For instance, a good value - // would be: OCSPMinTimeToExpiry + OldOCSPWindow. - // - // This has a default value of 61h. - ExpectedFreshness config.Duration `validate:"-"` - // How often a response should be signed when using Redis/live-signing // path. This has a default value of 60h. LiveSigningPeriod config.Duration `validate:"-"` @@ -123,6 +104,11 @@ type Config struct { // LogSampleRate sets how frequently error logs should be emitted. This // avoids flooding the logs during outages. 1 out of N log lines will be emitted. LogSampleRate int `validate:"min=0"` + + // Deprecated: ExpectedFreshness is no longer used now that we do not read + // OCSP Response bytes from the database. + // TODO(#6775): Remove this. + ExpectedFreshness config.Duration `validate:"-"` } Syslog cmd.SyslogConfig diff --git a/cmd/ocsp-updater/main.go b/cmd/ocsp-updater/main.go deleted file mode 100644 index 1dd11802045..00000000000 --- a/cmd/ocsp-updater/main.go +++ /dev/null @@ -1,143 +0,0 @@ -package notmain - -import ( - "flag" - "os" - "strings" - - capb "github.com/letsencrypt/boulder/ca/proto" - "github.com/letsencrypt/boulder/cmd" - "github.com/letsencrypt/boulder/config" - "github.com/letsencrypt/boulder/db" - "github.com/letsencrypt/boulder/features" - bgrpc "github.com/letsencrypt/boulder/grpc" - ocsp_updater "github.com/letsencrypt/boulder/ocsp/updater" - "github.com/letsencrypt/boulder/sa" - "github.com/letsencrypt/validator/v10" -) - -type Config struct { - OCSPUpdater struct { - DebugAddr string `validate:"required,hostname_port"` - TLS cmd.TLSConfig - DB cmd.DBConfig - ReadOnlyDB cmd.DBConfig - - // OldOCSPWindow controls how frequently ocsp-updater signs a batch - // of responses. - OldOCSPWindow config.Duration - // OldOCSPBatchSize controls the maximum number of responses - // ocsp-updater will sign every OldOCSPWindow. - OldOCSPBatchSize int `validate:"required"` - - // The worst-case freshness of a response during normal operations. - // This is related to to ExpectedFreshness in ocsp-responder's config, - // and both are related to the mandated refresh times in the BRs and - // root programs (minus a safety margin). - OCSPMinTimeToExpiry config.Duration - - // ParallelGenerateOCSPRequests determines how many requests to the CA - // may be inflight at once. - ParallelGenerateOCSPRequests int `validate:"required"` - - // TODO(#5933): Replace this with a unifed RetryBackoffConfig - SignFailureBackoffFactor float64 `validate:"required"` - SignFailureBackoffMax config.Duration - - // SerialSuffixShards is a whitespace-separated list of single hex - // digits. When searching for work to do, ocsp-updater will query - // for only those certificates end in one of the specified hex digits. - SerialSuffixShards string `validate:"suffixshards"` - - OCSPGeneratorService *cmd.GRPCClientConfig - - Features map[string]bool - } - - Syslog cmd.SyslogConfig -} - -func main() { - configFile := flag.String("config", "", "File path to the configuration file for this service") - flag.Parse() - if *configFile == "" { - flag.Usage() - os.Exit(1) - } - - var c Config - err := cmd.ReadConfigFile(*configFile, &c) - cmd.FailOnError(err, "Reading JSON config file into config structure") - - conf := c.OCSPUpdater - err = features.Set(conf.Features) - cmd.FailOnError(err, "Failed to set feature flags") - - scope, logger := cmd.StatsAndLogging(c.Syslog, conf.DebugAddr) - defer logger.AuditPanic() - logger.Info(cmd.VersionString()) - - readWriteDb, err := sa.InitWrappedDb(conf.DB, scope, logger) - cmd.FailOnError(err, "Failed to initialize database client") - - var readOnlyDb *db.WrappedMap - readOnlyDbDSN, _ := conf.ReadOnlyDB.URL() - if readOnlyDbDSN == "" { - readOnlyDb = readWriteDb - } else { - readOnlyDb, err = sa.InitWrappedDb(conf.ReadOnlyDB, scope, logger) - cmd.FailOnError(err, "Failed to initialize read-only database client") - } - - clk := cmd.Clock() - - tlsConfig, err := c.OCSPUpdater.TLS.Load() - cmd.FailOnError(err, "TLS config") - - caConn, err := bgrpc.ClientSetup(c.OCSPUpdater.OCSPGeneratorService, tlsConfig, scope, clk) - cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to CA") - ogc := capb.NewOCSPGeneratorClient(caConn) - - var serialSuffixes []string - if c.OCSPUpdater.SerialSuffixShards != "" { - serialSuffixes = strings.Fields(c.OCSPUpdater.SerialSuffixShards) - } - - updater, err := ocsp_updater.New( - scope, - clk, - readWriteDb, - readOnlyDb, - serialSuffixes, - ogc, - conf.OldOCSPBatchSize, - conf.OldOCSPWindow.Duration, - conf.SignFailureBackoffMax.Duration, - conf.SignFailureBackoffFactor, - conf.OCSPMinTimeToExpiry.Duration, - conf.ParallelGenerateOCSPRequests, - logger, - ) - cmd.FailOnError(err, "Failed to create updater") - - go cmd.CatchSignals(logger, nil) - for { - updater.Tick() - } -} - -// SuffixShardsVal implements validator.Func to validate the SerialSuffixShards -// field of the Config struct. -func SuffixShardsVal(fl validator.FieldLevel) bool { - err := ocsp_updater.SerialSuffixShardsValid(strings.Fields(fl.Field().String())) - return (err == nil) -} - -func init() { - cmd.RegisterCommand("ocsp-updater", main, &cmd.ConfigValidator{ - Config: &Config{}, - Validators: map[string]validator.Func{ - "suffixshards": SuffixShardsVal, - }, - }) -} diff --git a/core/objects.go b/core/objects.go index 27fe2429ad8..8b23e4e16e9 100644 --- a/core/objects.go +++ b/core/objects.go @@ -406,47 +406,44 @@ type Certificate struct { } // CertificateStatus structs are internal to the server. They represent the -// latest data about the status of the certificate, required for OCSP updating -// and for validating that the subscriber has accepted the certificate. +// latest data about the status of the certificate, required for generating new +// OCSP responses and determining if a certificate has been revoked. type CertificateStatus struct { ID int64 `db:"id"` Serial string `db:"serial"` // status: 'good' or 'revoked'. Note that good, expired certificates remain - // with status 'good' but don't necessarily get fresh OCSP responses. + // with status 'good' but don't necessarily get fresh OCSP responses. Status OCSPStatus `db:"status"` // ocspLastUpdated: The date and time of the last time we generated an OCSP - // response. If we have never generated one, this has the zero value of - // time.Time, i.e. Jan 1 1970. + // response. If we have never generated one, this has the zero value of + // time.Time, i.e. Jan 1 1970. OCSPLastUpdated time.Time `db:"ocspLastUpdated"` // revokedDate: If status is 'revoked', this is the date and time it was - // revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970. + // revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970. RevokedDate time.Time `db:"revokedDate"` // revokedReason: If status is 'revoked', this is the reason code for the - // revocation. Otherwise it is zero (which happens to be the reason - // code for 'unspecified'). + // revocation. Otherwise it is zero (which happens to be the reason + // code for 'unspecified'). RevokedReason revocation.Reason `db:"revokedReason"` LastExpirationNagSent time.Time `db:"lastExpirationNagSent"` // The encoded and signed OCSP response. + // + // Deprecated: We are phasing out storing OCSP Response bytes in the database, + // so CertificateStatus objects should not be expected to have a populated + // OCSPResponse field anymore. OCSPResponse []byte `db:"ocspResponse"` - // For performance reasons[0] we duplicate the `Expires` field of the - // `Certificates` object/table in `CertificateStatus` to avoid a costly `JOIN` - // later on just to retrieve this `Time` value. This helps both the OCSP - // updater and the expiration-mailer stay performant. - // - // Similarly, we add an explicit `IsExpired` boolean to `CertificateStatus` - // table that the OCSP updater so that the database can create a meaningful - // index on `(isExpired, ocspLastUpdated)` without a `JOIN` on `certificates`. - // For more detail see Boulder #1864[0]. - // - // [0]: https://github.com/letsencrypt/boulder/issues/1864 + // NotAfter and IsExpired are convenience columns which allow expensive + // queries to quickly filter out certificates that we don't need to care about + // anymore. These are particularly useful for the expiration mailer and CRL + // updater. See https://github.com/letsencrypt/boulder/issues/1864. NotAfter time.Time `db:"notAfter"` IsExpired bool `db:"isExpired"` diff --git a/docs/load-testing.md b/docs/load-testing.md deleted file mode 100644 index feaa29d609f..00000000000 --- a/docs/load-testing.md +++ /dev/null @@ -1,58 +0,0 @@ -# Load testing the OCSP signing components. - -Here are instructions on how to realistically load test the OCSP signing -components of Boulder, exercising the pkcs11key, boulder-ca, and -ocsp-updater components. - -Set up a SoftHSM instance running pkcs11-daemon on some remote host with more -CPUs than your local machine. Easiest way to do this is to clone the Boulder -repo, and on the remote machine run: - - remote-machine$ docker compose run -p 5657:5657 bhsm - -Check that the port is open: - - local-machine$ nc -zv remote-machine 5657 - Connection to remote-machine 5657 port [tcp/*] succeeded! - -Edit docker-compose.yml to change these in the "boulder" section's "env": - - PKCS11_PROXY_SOCKET: tcp://remote-machine:5657 - FAKE_DNS: 172.17.0.1 - -Run the pkcs11key benchmark to check raw signing speed at various settings for SESSIONS: - - local-machine$ docker compose run -e SESSIONS=4 -e MODULE=/usr/local/lib/softhsm/libsofthsm2.so --entrypoint /go/src/github.com/letsencrypt/pkcs11key/test.sh boulder - -Initialize the tokens for use by Boulder: - - local-machine$ docker compose run --entrypoint "softhsm --module /usr/local/lib/softhsm/libsofthsm2.so --init-token --pin 5678 --so-pin 1234 --slot 0 --label intermediate" boulder - local-machine$ docker compose run --entrypoint "softhsm --module /usr/local/lib/softhsm/libsofthsm2.so --init-token --pin 5678 --so-pin 1234 --slot 1 --label root" boulder - -Configure Boulder to always consider all OCSP responses instantly stale, so it -will sign new ones as fast as it can. Edit "ocspMinTimeToExpiry" in -test/config/ocsp-updater.json (or test/config-next/ocsp-updater.json): - - "ocspMinTimeToExpiry": "0h", - -Run a local Boulder instance: - - local-machine$ docker compose up - -Issue a bunch of certificates with chisel.py, ideally a few thousand -(corresponding to the default batch size of 5000 in ocsp-updater.json, to make -sure each batch is maxed out): - - local-machine$ while true; do python test/chisel.py $(openssl rand -hex 4).com ; done - -Use the local Prometheus instance to graph the number of complete gRPC calls: - -http://localhost:9090/graph?g0.range_input=5m&g0.expr=irate(grpc_client_handled_total%7Bgrpc_method%3D%22GenerateOCSP%22%7D%5B1m%5D)&g0.tab=0 - -If you vary the NumSessions config value in test/config/ca.json, you should see -the signing speed vary linearly, up to the number of cores in the remote -machine. Note that hyperthreaded cores look like 2 cores but may only perform -as 1 (needs testing). - -Keep in mind that round-trip time between your local machine and your HSM -machine greatly impact signing speed. diff --git a/ocsp/updater/testdata/test-cert-b.pem b/ocsp/updater/testdata/test-cert-b.pem deleted file mode 100644 index 3ad010fb188..00000000000 --- a/ocsp/updater/testdata/test-cert-b.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgITAP945y6GOlWTJzEwwYCxxvyiNDANBgkqhkiG9w0BAQsF -ADAfMR0wGwYDVQQDDBRoMnBweSBoMmNrZXIgZmFrZSBDQTAeFw0xODA1MjQxNzQ5 -MThaFw0yMDEwMTgxNzQ5MThaMBAxDjAMBgNVBAMTBXMuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmgMPWXMet57pY8Usb9sVOsg7A3hpWch7VAE -LlyBw/g7SXJt6arRn2w4UChS/oIw6s+VI9YO8AeKaBN4jBG9VF7q5vYGrOkHxEye -ZVh/cjuRzMf8siexyjRrRDrqqZmg4/t3O/FiC5qrHabKoEfERGosXUq7mwm/XuW7 -GAUovbrY9CjQYu+4yWqRlTSlcnSdAY4EYfNE1akowAsLi1iCfkasmy0PlXEenskh -GkU6mYeRubeqVzIVp28MjqXd2zQ5ybzdNNG4OUURu5z4ZeuAAvbZYHGNt27kqQmW -GlBnqewrhRAVFWIxiSC4Xb1AgQdgoQD0O9mwW7kDfLTKn4UyyQIDAQABo4IBhTCC -AYEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQHimy1vewsMOONppMya/B90IYUbjAf -BgNVHSMEGDAWgBT7eE8S+WAVgyyfF380GbMuNupBiTBkBggrBgEFBQcBAQRYMFYw -IgYIKwYBBQUHMAGGFmh0dHA6Ly8xMjcuMC4wLjE6NDAwMi8wMAYIKwYBBQUHMAKG -JGh0dHA6Ly9ib3VsZGVyOjQ0MzAvYWNtZS9pc3N1ZXItY2VydDAQBgNVHREECTAH -ggVzLmNvbTAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vZXhhbXBsZS5jb20vY3Js -MGEGA1UdIARaMFgwCAYGZ4EMAQIBMEwGAyoDBDBFMCIGCCsGAQUFBwIBFhZodHRw -Oi8vZXhhbXBsZS5jb20vY3BzMB8GCCsGAQUFBwICMBMMEURvIFdoYXQgVGhvdSBX -aWx0MA0GCSqGSIb3DQEBCwUAA4IBAQCSdKzs3Rav+P+8fa7x6SW4OU6NTVBdPF8O -+tQChZuVOBB6kBE9Zi04urKJ/qoK0N+QFukRjO+O1dLs2eGfQL07cFUfMclopVna -cHoTtOzuWvMHn4lgx+QeobuNgc/pBnFelGyFp2M3RRgdYZk/JVAF3OBtkSbgIGCS -1/8iO4qhW4OffJGTvqb+YWIPen2lWX7mNXNZYblOlqJXEanrBRjED4rFr0tgtQdz -YAjEoM9sGHFLy9PAcs4qP5tYfqD4B3sJ0hjSHn4+mcBHtQpkT6/fsqP9fTz3u0q7 -v7CcXf5XYaU1On5y0wYNgg6RsFOoOvsqRacP2v8iT4QULxfGJ3yN ------END CERTIFICATE----- diff --git a/ocsp/updater/testdata/test-cert.pem b/ocsp/updater/testdata/test-cert.pem deleted file mode 100644 index 3e603cb111a..00000000000 --- a/ocsp/updater/testdata/test-cert.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgITAP9qPF6Ypd4636BCjXm7LRhxCzANBgkqhkiG9w0BAQsF -ADAfMR0wGwYDVQQDDBRoMnBweSBoMmNrZXIgZmFrZSBDQTAeFw0xODA1MjQxNzQ4 -MDRaFw0yMDEwMTgxNzQ4MDRaMBAxDjAMBgNVBAMTBWsuY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAso7yw8mzhIRyHb4HIvdF1oAz1+58trJcw6ig -bmYSauDbjVWFosJ0PiL4obTWNftmGxYDqRR3ssjlSUaSos+hHaI30qIT2pauQKc/ -oKxJqlWBKxeIco0DJ7SUieuKVIUWbqV9QJGlPzLoGPzUCQEyYCCE+GZv7IbbOLDc -RfZLidkb3nTVkogmWhin2WNlicHxdrSaAsSj/Sw+UIPeLnDbrFO+aMivvJdjkoct -F4iLSRsFxk+jadBWJlZo0GcPoGaCe6eZiYxDupTmioIqdwScbwUTFtv0kQsuGSsE -UA3fvCl9hQ/fQfsQB987o+o+iINVMpNOUptMBxdK2PGul5zuMwIDAQABo4IBhTCC -AYEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS7G5+r83EGZokhTAwcy9XufuLJxjAf -BgNVHSMEGDAWgBT7eE8S+WAVgyyfF380GbMuNupBiTBkBggrBgEFBQcBAQRYMFYw -IgYIKwYBBQUHMAGGFmh0dHA6Ly8xMjcuMC4wLjE6NDAwMi8wMAYIKwYBBQUHMAKG -JGh0dHA6Ly9ib3VsZGVyOjQ0MzAvYWNtZS9pc3N1ZXItY2VydDAQBgNVHREECTAH -ggVrLmNvbTAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vZXhhbXBsZS5jb20vY3Js -MGEGA1UdIARaMFgwCAYGZ4EMAQIBMEwGAyoDBDBFMCIGCCsGAQUFBwIBFhZodHRw -Oi8vZXhhbXBsZS5jb20vY3BzMB8GCCsGAQUFBwICMBMMEURvIFdoYXQgVGhvdSBX -aWx0MA0GCSqGSIb3DQEBCwUAA4IBAQA2IQr6zV+ptlO+6wXjVctBRpgbrwDZA+kn -dnCCYYTfyPkPGk4pCzC3qPNB0Hat9CR75TBCEYh0QBRIENPyVJyAFln5Kc4tzmC7 -9oX8n+MbaAh26yUcTp9t4ngpVOkrhCQYi6raFv/rE8LP52+p7YazaoYSL4LYqv9L -/nYPx70fXz5/D1r0+Kdd/mrznOROUpGxzIo+VJalv2DeGwLDtXsbA0YdTZRJJ2bE -pb2s63UWaoDUlsYTm6oeAAJYIxpXR2E3B9PKGLsCuQxvDgmpHD3oTg1Yd5Tv18qc -xXpiKJvw9iBVyYj8ncNb8vn1VzLWMdYPBt7rOV91pon3yUcx0pGy ------END CERTIFICATE----- diff --git a/ocsp/updater/updater.go b/ocsp/updater/updater.go deleted file mode 100644 index f6d47106846..00000000000 --- a/ocsp/updater/updater.go +++ /dev/null @@ -1,476 +0,0 @@ -package updater - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "sync" - "time" - - "github.com/jmhodges/clock" - "github.com/prometheus/client_golang/prometheus" - - capb "github.com/letsencrypt/boulder/ca/proto" - "github.com/letsencrypt/boulder/core" - "github.com/letsencrypt/boulder/db" - blog "github.com/letsencrypt/boulder/log" - "github.com/letsencrypt/boulder/sa" -) - -// ocspDb is an interface collecting the methods that the read/write parts of -// OCSPUpdater rely on. This allows the tests to swap out the db implementation. -type ocspDb interface { - db.MappedExecutor - Exec(query string, args ...interface{}) (sql.Result, error) -} - -// failCounter provides a concurrent safe counter. -type failCounter struct { - mu sync.Mutex - count int -} - -func (c *failCounter) Add(i int) { - c.mu.Lock() - defer c.mu.Unlock() - c.count += i -} - -func (c *failCounter) Reset() { - c.mu.Lock() - defer c.mu.Unlock() - c.count = 0 -} - -func (c *failCounter) Value() int { - c.mu.Lock() - defer c.mu.Unlock() - return c.count -} - -func SerialSuffixShardsValid(serialSuffixes []string) error { - for _, s := range serialSuffixes { - if len(s) != 1 || strings.ToLower(s) != s { - return fmt.Errorf("serial suffixes must all be one lowercase character, got %q, expected %q", s, strings.ToLower(s)) - } - c := s[0] - if !(c >= '0' && c <= '9' || c >= 'a' && c <= 'f') { - return errors.New("valid range for suffixes is [0-9a-f]") - } - } - return nil -} - -// OCSPUpdater contains the useful objects for the Updater -type OCSPUpdater struct { - log blog.Logger - clk clock.Clock - - db ocspDb - readOnlyDb db.MappedExecutor - - ogc capb.OCSPGeneratorClient - - batchSize int - - tickWindow time.Duration - maxBackoff time.Duration - backoffFactor float64 - readFailures failCounter - - serialSuffixes []string - queryBody string - - // Used to calculate how far back stale OCSP responses should be looked for - ocspMinTimeToExpiry time.Duration - // Maximum number of individual OCSP updates to attempt in parallel. Making - // these requests in parallel allows us to get higher total throughput. - parallelGenerateOCSPRequests int - - tickHistogram *prometheus.HistogramVec - stalenessHistogram prometheus.Histogram - genStoreHistogram prometheus.Histogram - generatedCounter *prometheus.CounterVec - storedCounter *prometheus.CounterVec - markExpiredCounter *prometheus.CounterVec - findStaleOCSPCounter *prometheus.CounterVec -} - -func New( - stats prometheus.Registerer, - clk clock.Clock, - db *db.WrappedMap, - readOnlyDb *db.WrappedMap, - serialSuffixes []string, - ogc capb.OCSPGeneratorClient, - batchSize int, - windowSize time.Duration, - retryBackoffMax time.Duration, - retryBackoffFactor float64, - ocspMinTimeToExpiry time.Duration, - parallelGenerateOCSPRequests int, - log blog.Logger, -) (*OCSPUpdater, error) { - if batchSize == 0 { - return nil, errors.New("loop batch sizes must be non-zero") - } - if windowSize == 0 { - return nil, errors.New("loop window sizes must be non-zero") - } - if parallelGenerateOCSPRequests == 0 { - // Default to 1 - parallelGenerateOCSPRequests = 1 - } - - err := SerialSuffixShardsValid(serialSuffixes) - if err != nil { - return nil, err - } - - var queryBody strings.Builder - queryBody.WriteString("WHERE ocspLastUpdated < ? AND NOT isExpired ") - if len(serialSuffixes) > 0 { - fmt.Fprintf(&queryBody, "AND RIGHT(serial, 1) IN ( %s ) ", - getQuestionsForShardList(len(serialSuffixes)), - ) - } - queryBody.WriteString("ORDER BY ocspLastUpdated ASC LIMIT ?") - - genStoreHistogram := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "ocsp_updater_generate_and_store", - Help: "A histogram of latencies of OCSP generation and storage latencies", - }) - stats.MustRegister(genStoreHistogram) - generatedCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "ocsp_updater_generated", - Help: "A counter of OCSP response generation calls labeled by result", - }, []string{"result"}) - stats.MustRegister(generatedCounter) - storedCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "ocsp_updater_stored", - Help: "A counter of OCSP response storage calls labeled by result", - }, []string{"result"}) - stats.MustRegister(storedCounter) - tickHistogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Name: "ocsp_updater_ticks", - Help: "A histogram of ocsp-updater tick latencies labelled by result and whether the tick was considered longer than expected", - Buckets: []float64{0.01, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000}, - }, []string{"result", "long"}) - stats.MustRegister(tickHistogram) - stalenessHistogram := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "ocsp_status_staleness", - Help: "How long past the refresh time a status is when we try to refresh it. Will always be > 0, but must stay well below 12 hours.", - Buckets: []float64{10, 100, 1000, 10000, 21600, 32400, 36000, 39600, 43200, 54000, 64800, 75600, 86400, 108000, 129600, 172800}, - }) - stats.MustRegister(stalenessHistogram) - markExpiredCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "mark_expired", - Help: "A counter of mark expired calls labeled by result", - }, []string{"result"}) - stats.MustRegister(markExpiredCounter) - findStaleOCSPCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "find_stale_ocsp", - Help: "A counter of query for stale OCSP responses labeled by result", - }, []string{"result"}) - stats.MustRegister(findStaleOCSPCounter) - - updater := OCSPUpdater{ - log: log, - clk: clk, - db: db, - readOnlyDb: readOnlyDb, - ogc: ogc, - batchSize: batchSize, - tickWindow: windowSize, - maxBackoff: retryBackoffMax, - backoffFactor: retryBackoffFactor, - readFailures: failCounter{}, - serialSuffixes: serialSuffixes, - queryBody: queryBody.String(), - ocspMinTimeToExpiry: ocspMinTimeToExpiry, - parallelGenerateOCSPRequests: parallelGenerateOCSPRequests, - tickHistogram: tickHistogram, - stalenessHistogram: stalenessHistogram, - genStoreHistogram: genStoreHistogram, - generatedCounter: generatedCounter, - storedCounter: storedCounter, - markExpiredCounter: markExpiredCounter, - findStaleOCSPCounter: findStaleOCSPCounter, - } - - return &updater, nil -} - -func getQuestionsForShardList(count int) string { - return strings.TrimRight(strings.Repeat("?,", count), ",") -} - -// findStaleOCSPResponses sends a goroutine to fetch rows of stale OCSP -// responses from the database and returns results on a channel. -func (updater *OCSPUpdater) findStaleOCSPResponses(ctx context.Context, oldestLastUpdatedTime time.Time, batchSize int) <-chan *sa.CertStatusMetadata { - // staleStatusesOut channel contains all stale ocsp responses that need - // updating. - staleStatusesOut := make(chan *sa.CertStatusMetadata) - - args := make([]interface{}, 0) - args = append(args, oldestLastUpdatedTime) - - // If serialSuffixes is unset, this will be deliberately a no-op. - for _, c := range updater.serialSuffixes { - args = append(args, c) - } - args = append(args, batchSize) - - go func() { - defer close(staleStatusesOut) - - selector, err := db.NewMappedSelector[sa.CertStatusMetadata](updater.readOnlyDb) - if err != nil { - updater.log.AuditErrf("failed to initialize database map: %s", err) - updater.findStaleOCSPCounter.WithLabelValues("failed").Inc() - updater.readFailures.Add(1) - return - } - - rows, err := selector.Query(ctx, updater.queryBody, args...) - - // If error, log and increment retries for backoff. Else no - // error, proceed to push statuses to channel. - if err != nil { - updater.log.AuditErrf("failed to find stale OCSP responses: %s", err) - updater.findStaleOCSPCounter.WithLabelValues("failed").Inc() - updater.readFailures.Add(1) - return - } - defer func() { - err := rows.Close() - if err != nil { - updater.log.AuditErrf("closing query rows: %s", err) - } - }() - - for rows.Next() { - meta, err := rows.Get() - if err != nil { - updater.log.AuditErrf("failed to scan metadata status row: %s", err) - updater.findStaleOCSPCounter.WithLabelValues("failed").Inc() - updater.readFailures.Add(1) - return - } - staleness := oldestLastUpdatedTime.Sub(meta.OCSPLastUpdated).Seconds() - updater.stalenessHistogram.Observe(staleness) - select { - case <-ctx.Done(): - err := ctx.Err() - if err != nil { - updater.log.AuditErrf("context done reading rows: %s", err) - } - return - case staleStatusesOut <- meta: - } - } - - // Ensure the query wasn't interrupted before it could complete. - err = rows.Err() - if err != nil { - updater.log.AuditErrf("finishing row scan: %s", err) - updater.findStaleOCSPCounter.WithLabelValues("failed").Inc() - updater.readFailures.Add(1) - return - } - - updater.findStaleOCSPCounter.WithLabelValues("success").Inc() - updater.readFailures.Reset() - }() - - return staleStatusesOut -} - -func statusFromMetaAndResp(meta *sa.CertStatusMetadata, resp []byte) *core.CertificateStatus { - return &core.CertificateStatus{ - ID: meta.ID, - Serial: meta.Serial, - Status: meta.Status, - OCSPLastUpdated: meta.OCSPLastUpdated, - RevokedDate: meta.RevokedDate, - RevokedReason: meta.RevokedReason, - LastExpirationNagSent: meta.LastExpirationNagSent, - OCSPResponse: resp, - NotAfter: meta.NotAfter, - IsExpired: meta.IsExpired, - IssuerNameID: meta.IssuerID, - } -} - -// generateResponse signs an new OCSP response for a given certStatus row. -func (updater *OCSPUpdater) generateResponse(ctx context.Context, meta *sa.CertStatusMetadata) (*core.CertificateStatus, error) { - if meta.IssuerID == 0 { - return nil, errors.New("cert status has 0 IssuerID") - } - ocspReq := capb.GenerateOCSPRequest{ - Serial: meta.Serial, - IssuerID: meta.IssuerID, - Status: string(meta.Status), - Reason: int32(meta.RevokedReason), - RevokedAt: meta.RevokedDate.UnixNano(), - } - - ocspResponse, err := updater.ogc.GenerateOCSP(ctx, &ocspReq) - if err != nil { - return nil, err - } - - meta.OCSPLastUpdated = updater.clk.Now() - return statusFromMetaAndResp(meta, ocspResponse.Response), nil -} - -// storeResponse stores a given CertificateStatus in the database. -func (updater *OCSPUpdater) storeResponse(ctx context.Context, status *core.CertificateStatus) error { - // Update the certificateStatus table with the new OCSP response, the status - // WHERE is used make sure we don't overwrite a revoked response with a one - // containing a 'good' status. - _, err := updater.db.WithContext(ctx).Exec( - `UPDATE certificateStatus - SET ocspResponse=?,ocspLastUpdated=? - WHERE id=? - AND status=?`, - status.OCSPResponse, - status.OCSPLastUpdated, - status.ID, - string(status.Status), - ) - - return err -} - -// markExpired updates a given CertificateStatus to have `isExpired` set. -func (updater *OCSPUpdater) markExpired(ctx context.Context, meta *sa.CertStatusMetadata) error { - _, err := updater.db.WithContext(ctx).Exec( - `UPDATE certificateStatus - SET isExpired = TRUE - WHERE id = ?`, - meta.ID, - ) - return err -} - -// processExpired is a pipeline step to process a channel of -// `sa.CertStatusMetadata` and set `isExpired` in the database. -func (updater *OCSPUpdater) processExpired(ctx context.Context, staleStatusesIn <-chan *sa.CertStatusMetadata) <-chan *sa.CertStatusMetadata { - tickStart := updater.clk.Now() - staleStatusesOut := make(chan *sa.CertStatusMetadata) - go func() { - defer close(staleStatusesOut) - for meta := range staleStatusesIn { - if !meta.IsExpired && tickStart.After(meta.NotAfter) { - err := updater.markExpired(ctx, meta) - if err != nil { - // Update error counters and log - updater.log.AuditErrf("Failed to set certificate expired: %s", err) - updater.markExpiredCounter.WithLabelValues("failed").Inc() - } else { - updater.markExpiredCounter.WithLabelValues("success").Inc() - } - } - select { - case <-ctx.Done(): - return - case staleStatusesOut <- meta: - } - } - }() - - return staleStatusesOut -} - -// generateOCSPResponses is the final stage of a pipeline. It takes a -// channel of `core.CertificateStatus` and sends a goroutine for each to -// obtain a new OCSP response and update the status in the database. -func (updater *OCSPUpdater) generateOCSPResponses(ctx context.Context, staleStatusesIn <-chan *sa.CertStatusMetadata) { - // Use the semaphore pattern from - // https://github.com/golang/go/wiki/BoundingResourceUse to send a number of - // GenerateOCSP / storeResponse requests in parallel, while limiting the total number of - // outstanding requests. The number of outstanding requests equals the - // capacity of the channel. - sem := make(chan int, updater.parallelGenerateOCSPRequests) - wait := func() { - sem <- 1 // Block until there's capacity. - } - done := func(start time.Time) { - <-sem // Indicate there's more capacity. - updater.genStoreHistogram.Observe(time.Since(start).Seconds()) - } - - // Work runs as a goroutine per ocsp response to obtain a new ocsp - // response and store it in the database. - work := func(meta *sa.CertStatusMetadata) { - defer done(updater.clk.Now()) - - status, err := updater.generateResponse(ctx, meta) - if err != nil { - updater.log.AuditErrf("Failed to generate OCSP response: %s", err) - updater.generatedCounter.WithLabelValues("failed").Inc() - return - } - updater.generatedCounter.WithLabelValues("success").Inc() - - err = updater.storeResponse(ctx, status) - if err != nil { - updater.log.AuditErrf("Failed to store OCSP response: %s", err) - updater.storedCounter.WithLabelValues("failed").Inc() - return - } - updater.storedCounter.WithLabelValues("success").Inc() - } - - // Consume the stale statuses channel and send off a sign/store request - // for each stale response. - for status := range staleStatusesIn { - wait() - go work(status) - } - - // Block until the sem channel reaches its full capacity again, - // indicating each goroutine has completed. - for i := 0; i < updater.parallelGenerateOCSPRequests; i++ { - wait() - } -} - -func (updater *OCSPUpdater) Tick() { - start := updater.clk.Now() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - oldestLastUpdatedTime := updater.clk.Now().Add(-updater.ocspMinTimeToExpiry) - - // Run pipeline - updater.generateOCSPResponses(ctx, updater.processExpired(ctx, updater.findStaleOCSPResponses(ctx, oldestLastUpdatedTime, updater.batchSize))) - - end := updater.clk.Now() - took := end.Sub(start) - long, state := "false", "success" - if took > updater.tickWindow { - long = "true" - } - - // Set sleep duration to the configured tickWindow. - sleepDur := start.Add(updater.tickWindow).Sub(end) - - // Set sleep duration higher to backoff starting the next tick and - // reading from the database if the last read failed. - readFails := updater.readFailures.Value() - if readFails > 0 { - sleepDur = core.RetryBackoff( - readFails, - updater.tickWindow, - updater.maxBackoff, - updater.backoffFactor, - ) - } - updater.tickHistogram.WithLabelValues(state, long).Observe(took.Seconds()) - updater.clk.Sleep(sleepDur) -} diff --git a/ocsp/updater/updater_test.go b/ocsp/updater/updater_test.go deleted file mode 100644 index a360ec2c5f9..00000000000 --- a/ocsp/updater/updater_test.go +++ /dev/null @@ -1,659 +0,0 @@ -package updater - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "errors" - "math/big" - "reflect" - "strings" - "sync" - "testing" - "time" - - "github.com/go-gorp/gorp/v3" - "github.com/jmhodges/clock" - capb "github.com/letsencrypt/boulder/ca/proto" - "github.com/letsencrypt/boulder/core" - "github.com/letsencrypt/boulder/db" - bgrpc "github.com/letsencrypt/boulder/grpc" - blog "github.com/letsencrypt/boulder/log" - "github.com/letsencrypt/boulder/metrics" - "github.com/letsencrypt/boulder/sa" - sapb "github.com/letsencrypt/boulder/sa/proto" - "github.com/letsencrypt/boulder/sa/satest" - "github.com/letsencrypt/boulder/test" - isa "github.com/letsencrypt/boulder/test/inmem/sa" - "github.com/letsencrypt/boulder/test/vars" - "github.com/prometheus/client_golang/prometheus" - "google.golang.org/grpc" -) - -var ctx = context.Background() - -type mockOCSP struct { - sleepTime time.Duration -} - -func (ca *mockOCSP) GenerateOCSP(_ context.Context, req *capb.GenerateOCSPRequest, _ ...grpc.CallOption) (*capb.OCSPResponse, error) { - time.Sleep(ca.sleepTime) - return &capb.OCSPResponse{Response: []byte{1, 2, 3}}, nil -} - -var log = blog.UseMock() - -func setup(t *testing.T) (*OCSPUpdater, sapb.StorageAuthorityClient, *db.WrappedMap, clock.FakeClock, func()) { - dbMap, err := sa.NewDbMap(vars.DBConnSA, sa.DbSettings{}) - test.AssertNotError(t, err, "Failed to create dbMap") - readOnlyDb, err := sa.NewDbMap(vars.DBConnSAOcspUpdateRO, sa.DbSettings{}) - test.AssertNotError(t, err, "Failed to create dbMap") - cleanUp := test.ResetBoulderTestDatabase(t) - sa.SetSQLDebug(dbMap, log) - - fc := clock.NewFake() - fc.Add(1 * time.Hour) - - ssa, err := sa.NewSQLStorageAuthority(dbMap, dbMap, nil, 1, 0, fc, log, metrics.NoopRegisterer) - test.AssertNotError(t, err, "Failed to create SA") - - updater, err := New( - metrics.NoopRegisterer, - fc, - dbMap, - readOnlyDb, - strings.Fields("0 1 2 3 4 5 6 7 8 9 a b c d e f"), - &mockOCSP{}, - 1, - time.Second, - time.Minute, - 1.5, - 0, - 0, - blog.NewMock(), - ) - test.AssertNotError(t, err, "Failed to create newUpdater") - - return updater, isa.SA{Impl: ssa}, dbMap, fc, cleanUp -} - -func nowNano(fc clock.Clock) int64 { - return fc.Now().UnixNano() -} - -func TestStalenessHistogram(t *testing.T) { - updater, sac, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sac) - parsedCertA, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sac.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCertA.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - parsedCertB, err := core.LoadCert("testdata/test-cert-b.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sac.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCertB.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert-b.pem") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - // We should have 2 stale responses now. - metas := updater.findStaleOCSPResponses(ctx, earliest, 10) - var metaSlice []*sa.CertStatusMetadata - for status := range metas { - metaSlice = append(metaSlice, status) - } - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(metaSlice), 2) - - test.AssertMetricWithLabelsEquals(t, updater.stalenessHistogram, prometheus.Labels{}, 2) -} - -func TestGenerateAndStoreOCSPResponse(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - metas := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(metas), 1) - meta := <-metas - - status, err := updater.generateResponse(ctx, meta) - test.AssertNotError(t, err, "Couldn't generate OCSP response") - err = updater.storeResponse(context.Background(), status) - test.AssertNotError(t, err, "Couldn't store certificate status") -} - -// findStaleOCSPResponsesBuffered runs findStaleOCSPResponses and returns -// it as a buffered channel. This is helpful for tests that want to test -// the length of the channel. -func findStaleOCSPResponsesBuffered(ctx context.Context, updater *OCSPUpdater, earliest time.Time) <-chan *sa.CertStatusMetadata { - out := make(chan *sa.CertStatusMetadata, 10) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - defer close(out) - metas := updater.findStaleOCSPResponses(ctx, earliest, 10) - for meta := range metas { - out <- meta - } - }() - wg.Wait() - return out -} - -func TestGenerateOCSPResponses(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCertA, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCertA.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - parsedCertB, err := core.LoadCert("testdata/test-cert-b.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCertB.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert-b.pem") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - // We should have 2 stale responses now. - statuses := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 2) - - // Hacky test of parallelism: Make each request to the CA take 1 second, and - // produce 2 requests to the CA. If the pair of requests complete in about a - // second, they were made in parallel. - // Note that this test also tests the basic functionality of - // generateOCSPResponses. - start := time.Now() - updater.ogc = &mockOCSP{time.Second} - updater.parallelGenerateOCSPRequests = 10 - updater.generateOCSPResponses(ctx, statuses) - elapsed := time.Since(start) - if elapsed > 1500*time.Millisecond { - t.Errorf("generateOCSPResponses took too long, expected it to make calls in parallel.") - } - - // generateOCSPResponses should have updated the ocspLastUpdate for each - // cert, so there shouldn't be any stale responses anymore. - statuses = findStaleOCSPResponsesBuffered(ctx, updater, earliest) - - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 0) -} - -func TestFindStaleOCSPResponses(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - // With no rows in the CertificateStatus table we shouldn't get an error. - statuses := findStaleOCSPResponsesBuffered(ctx, updater, fc.Now()) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 0) - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - // We should have 1 stale response now. - statuses = findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 1) - status := <-statuses - - // Generate and store an updated response, which will update the - // ocspLastUpdate field for this cert. - meta, err := updater.generateResponse(ctx, status) - test.AssertNotError(t, err, "Couldn't generate OCSP response") - err = updater.storeResponse(context.Background(), meta) - test.AssertNotError(t, err, "Couldn't store OCSP response") - - // We should have 0 stale responses now. - statuses = findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 0) -} - -func TestFindStaleOCSPResponsesRevokedReason(t *testing.T) { - updater, sa, dbMap, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - // Set a revokedReason to ensure it gets written into the OCSPResponse. - _, err = dbMap.Exec( - "UPDATE certificateStatus SET revokedReason = 1 WHERE serial = ?", - core.SerialToString(parsedCert.SerialNumber)) - test.AssertNotError(t, err, "Couldn't update revokedReason") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - statuses := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 1) - status := <-statuses - test.AssertEquals(t, int(status.RevokedReason), 1) -} - -func TestPipelineTick(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - updater.ocspMinTimeToExpiry = 1 * time.Hour - earliest := fc.Now().Add(-time.Hour) - updater.generateOCSPResponses(ctx, updater.processExpired(ctx, updater.findStaleOCSPResponses(ctx, earliest, 10))) - test.AssertEquals(t, updater.readFailures.Value(), 0) - - certs := findStaleOCSPResponsesBuffered(ctx, updater, fc.Now().Add(-updater.ocspMinTimeToExpiry)) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(certs), 0) -} - -// TestProcessExpired checks that the `processExpired` pipeline step -// updates the `IsExpired` field opportunistically as it encounters -// certificates that are expired but whose certificate status rows do not -// have `IsExpired` set, and that expired certs don't show up as having -// stale responses. -func TestProcessExpired(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - serial := core.SerialToString(parsedCert.SerialNumber) - - // Add a new test certificate - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - // The certificate isn't expired, so the certificate status should have - // a false `IsExpired` and it should show up as stale. - statusPB, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) - test.AssertNotError(t, err, "Couldn't get the certificateStatus from the database") - cs, err := bgrpc.PBToCertStatus(statusPB) - test.AssertNotError(t, err, "Count't convert the certificateStatus from a PB") - - test.AssertEquals(t, cs.IsExpired, false) - statuses := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 1) - - // Advance the clock to the point that the certificate we added is now expired - fc.Set(parsedCert.NotAfter.Add(2 * time.Hour)) - earliest = fc.Now().Add(-time.Hour) - updater.ocspMinTimeToExpiry = 1 * time.Hour - - // Run pipeline to find stale responses, mark expired, and generate new response. - updater.generateOCSPResponses(ctx, updater.processExpired(ctx, updater.findStaleOCSPResponses(ctx, earliest, 10))) - - // Since we advanced the fakeclock beyond our test certificate's NotAfter we - // expect the certificate status has been updated to have a true `IsExpired` - statusPB, err = sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial}) - test.AssertNotError(t, err, "Couldn't get the certificateStatus from the database") - cs, err = bgrpc.PBToCertStatus(statusPB) - test.AssertNotError(t, err, "Count't convert the certificateStatus from a PB") - - test.AssertEquals(t, cs.IsExpired, true) - statuses = findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 0) -} - -func TestStoreResponseGuard(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - parsedCert, err := core.LoadCert("testdata/test-cert.pem") - test.AssertNotError(t, err, "Couldn't read test certificate") - _, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: parsedCert.Raw, - RegID: reg.Id, - Ocsp: nil, - Issued: nowNano(fc), - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert.pem") - - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - metas := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(metas), 1) - meta := <-metas - - serialStr := core.SerialToString(parsedCert.SerialNumber) - reason := int64(0) - revokedDate := fc.Now().UnixNano() - _, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{ - Serial: serialStr, - Reason: reason, - Date: revokedDate, - Response: []byte("fakeocspbytes"), - }) - test.AssertNotError(t, err, "Failed to revoked certificate") - - // Attempt to update OCSP response where status.Status is good but stored status - // is revoked, this should fail silently - status := statusFromMetaAndResp(meta, []byte("newfakeocspbytes")) - err = updater.storeResponse(context.Background(), status) - test.AssertNotError(t, err, "Failed to update certificate status") - - // Make sure the OCSP response hasn't actually changed - unchangedStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: core.SerialToString(parsedCert.SerialNumber)}) - test.AssertNotError(t, err, "Failed to get certificate status") - test.AssertEquals(t, string(unchangedStatus.OcspResponse), "fakeocspbytes") - - // Changing the status to the stored status should allow the update to occur - status.Status = core.OCSPStatusRevoked - err = updater.storeResponse(context.Background(), status) - test.AssertNotError(t, err, "Failed to updated certificate status") - - // Make sure the OCSP response has been updated - changedStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: core.SerialToString(parsedCert.SerialNumber)}) - test.AssertNotError(t, err, "Failed to get certificate status") - test.AssertEquals(t, string(changedStatus.OcspResponse), "newfakeocspbytes") -} - -func TestGenerateOCSPResponsePrecert(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - - reg := satest.CreateWorkingRegistration(t, sa) - - // Create a throw-away self signed certificate with some names - serial, testCert := test.ThrowAwayCert(t, 5) - - // Use AddPrecertificate to set up a precertificate, serials, and - // certificateStatus row for the testcert. - ocspResp := []byte{0, 0, 1} - regID := reg.Id - issuedTime := fc.Now().UnixNano() - _, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{ - Der: testCert.Raw, - RegID: regID, - Ocsp: ocspResp, - Issued: issuedTime, - IssuerNameID: 1, - }) - test.AssertNotError(t, err, "Couldn't add test-cert2.der") - - // Jump time forward by 2 hours so the ocspLastUpdate value will be older than - // the earliest lastUpdate time we care about. - fc.Set(fc.Now().Add(2 * time.Hour)) - earliest := fc.Now().Add(-time.Hour) - - // There should be one stale ocsp response found for the precert - certs := findStaleOCSPResponsesBuffered(ctx, updater, earliest) - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(certs), 1) - cert := <-certs - test.AssertEquals(t, cert.Serial, serial) - - // Directly call generateResponse again with the same result. It should not - // error and should instead update the precertificate's OCSP status even - // though no certificate row exists. - _, err = updater.generateResponse(ctx, cert) - test.AssertNotError(t, err, "generateResponse for precert errored") -} - -type mockOCSPRecordIssuer struct { - gotIssuer bool -} - -func (ca *mockOCSPRecordIssuer) GenerateOCSP(_ context.Context, req *capb.GenerateOCSPRequest, _ ...grpc.CallOption) (*capb.OCSPResponse, error) { - ca.gotIssuer = req.IssuerID != 0 && req.Serial != "" - return &capb.OCSPResponse{Response: []byte{1, 2, 3}}, nil -} - -func TestIssuerInfo(t *testing.T) { - updater, sa, _, fc, cleanUp := setup(t) - defer cleanUp() - m := mockOCSPRecordIssuer{} - updater.ogc = &m - reg := satest.CreateWorkingRegistration(t, sa) - - k, err := rsa.GenerateKey(rand.Reader, 512) - test.AssertNotError(t, err, "rsa.GenerateKey failed") - template := &x509.Certificate{ - SerialNumber: big.NewInt(1), - DNSNames: []string{"example.com"}, - } - certA, err := x509.CreateCertificate(rand.Reader, template, template, &k.PublicKey, k) - test.AssertNotError(t, err, "x509.CreateCertificate failed") - - now := fc.Now().UnixNano() - id := int64(1234) - _, err = sa.AddPrecertificate(context.Background(), &sapb.AddCertificateRequest{ - Der: certA, - RegID: reg.Id, - Ocsp: []byte{1, 2, 3}, - Issued: now, - IssuerNameID: id, - }) - test.AssertNotError(t, err, "sa.AddPrecertificate failed") - - fc.Add(time.Hour * 24 * 4) - statuses := findStaleOCSPResponsesBuffered(ctx, updater, fc.Now().Add(-time.Hour)) - - test.AssertEquals(t, updater.readFailures.Value(), 0) - test.AssertEquals(t, len(statuses), 1) - status := <-statuses - test.AssertEquals(t, status.IssuerID, id) - - _, err = updater.generateResponse(context.Background(), status) - test.AssertNotError(t, err, "generateResponse failed") - test.Assert(t, m.gotIssuer, "generateResponse didn't send issuer information and serial") -} - -type brokenDB struct{} - -func (bdb *brokenDB) TableFor(_ reflect.Type, _ bool) (*gorp.TableMap, error) { - return nil, errors.New("broken") -} - -func (bdb *brokenDB) WithContext(_ context.Context) gorp.SqlExecutor { - return nil -} - -func TestTickSleep(t *testing.T) { - updater, _, dbMap, fc, cleanUp := setup(t) - defer cleanUp() - - // Test that when findStaleResponses fails the failure counter is - // incremented and the clock moved forward by more than - // updater.tickWindow - updater.readOnlyDb = &brokenDB{} - updater.readFailures.Add(2) - before := fc.Now() - updater.Tick() - test.AssertEquals(t, updater.readFailures.Value(), 3) - took := fc.Since(before) - test.Assert(t, took > updater.tickWindow, "Clock didn't move forward enough") - - // Test when findStaleResponses works the failure counter is reset to - // zero and the clock only moves by updater.tickWindow - updater.readOnlyDb = dbMap - before = fc.Now() - updater.Tick() - test.AssertEquals(t, updater.readFailures.Value(), 0) - took = fc.Since(before) - test.AssertEquals(t, took, updater.tickWindow) - -} - -func TestFindOCSPResponsesSleep(t *testing.T) { - updater, _, dbMap, fc, cleanUp := setup(t) - defer cleanUp() - m := &brokenDB{} - updater.readOnlyDb = m - - // Test when updateOCSPResponses fails the failure counter is incremented - // and the clock moved forward by more than updater.tickWindow - updater.readFailures.Add(2) - before := fc.Now() - updater.Tick() - test.AssertEquals(t, updater.readFailures.Value(), 3) - took := fc.Since(before) - test.Assert(t, took > updater.tickWindow, "Clock didn't move forward enough") - - // Test when updateOCSPResponses works the failure counter is reset to zero - // and the clock only moves by updater.tickWindow - updater.readOnlyDb = dbMap - before = fc.Now() - updater.Tick() - test.AssertEquals(t, updater.readFailures.Value(), 0) - took = fc.Since(before) - test.AssertEquals(t, took, updater.tickWindow) - -} - -func mkNewUpdaterWithStrings(t *testing.T, shards []string) (*OCSPUpdater, error) { - dbMap, err := sa.NewDbMap(vars.DBConnSA, sa.DbSettings{}) - test.AssertNotError(t, err, "Failed to create dbMap") - sa.SetSQLDebug(dbMap, log) - - fc := clock.NewFake() - - updater, err := New( - metrics.NoopRegisterer, - fc, - dbMap, - dbMap, - shards, - &mockOCSP{}, - 1, - time.Second, - time.Minute, - 1.5, - 0, - 0, - blog.NewMock(), - ) - return updater, err -} - -func TestUpdaterConfiguration(t *testing.T) { - _, err := mkNewUpdaterWithStrings(t, strings.Fields("0 1 2 3 4 5 6 7 8 9 a B c d e f")) - test.AssertError(t, err, "No uppercase allowed") - - _, err = mkNewUpdaterWithStrings(t, strings.Fields("0 1 g")) - test.AssertError(t, err, "No letters > f allowed") - - _, err = mkNewUpdaterWithStrings(t, strings.Fields("0 *")) - test.AssertError(t, err, "No special chars allowed") - - _, err = mkNewUpdaterWithStrings(t, strings.Fields("0 -1")) - test.AssertError(t, err, "No negative numbers allowed") - - _, err = mkNewUpdaterWithStrings(t, strings.Fields("wazzup 0 a b c")) - test.AssertError(t, err, "No multi-letter shards allowed") - - _, err = mkNewUpdaterWithStrings(t, []string{}) - test.AssertNotError(t, err, "Empty should be valid, meaning use old queries") -} - -func TestGetQuestionsForShardList(t *testing.T) { - test.AssertEquals(t, getQuestionsForShardList(2), "?,?") - test.AssertEquals(t, getQuestionsForShardList(1), "?") - test.AssertEquals(t, getQuestionsForShardList(16), "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?") -} diff --git a/sa/db-users/boulder_sa.sql b/sa/db-users/boulder_sa.sql index 93dfc02c653..8e4d6d23246 100644 --- a/sa/db-users/boulder_sa.sql +++ b/sa/db-users/boulder_sa.sql @@ -10,8 +10,6 @@ CREATE USER IF NOT EXISTS 'revoker'@'localhost'; CREATE USER IF NOT EXISTS 'importer'@'localhost'; CREATE USER IF NOT EXISTS 'mailer'@'localhost'; CREATE USER IF NOT EXISTS 'cert_checker'@'localhost'; -CREATE USER IF NOT EXISTS 'ocsp_update'@'localhost'; -CREATE USER IF NOT EXISTS 'ocsp_update_ro'@'localhost'; CREATE USER IF NOT EXISTS 'test_setup'@'localhost'; CREATE USER IF NOT EXISTS 'badkeyrevoker'@'localhost'; CREATE USER IF NOT EXISTS 'proxysql'@'localhost'; @@ -56,15 +54,6 @@ GRANT SELECT ON incidents TO 'sa_ro'@'localhost'; -- OCSP Responder GRANT SELECT ON certificateStatus TO 'ocsp_resp'@'localhost'; --- OCSP Generator Tool (Updater) -GRANT SELECT ON certificates TO 'ocsp_update'@'localhost'; -GRANT SELECT,UPDATE ON certificateStatus TO 'ocsp_update'@'localhost'; -GRANT SELECT ON precertificates TO 'ocsp_update'@'localhost'; - -GRANT SELECT ON certificates TO 'ocsp_update_ro'@'localhost'; -GRANT SELECT ON certificateStatus TO 'ocsp_update_ro'@'localhost'; -GRANT SELECT ON precertificates TO 'ocsp_update_ro'@'localhost'; - -- Revoker Tool GRANT SELECT ON registrations TO 'revoker'@'localhost'; GRANT SELECT ON certificates TO 'revoker'@'localhost'; diff --git a/test/config-next/ca-a.json b/test/config-next/ca-a.json index 9521525484a..1b301ed13c7 100644 --- a/test/config-next/ca-a.json +++ b/test/config-next/ca-a.json @@ -18,7 +18,6 @@ }, "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] diff --git a/test/config-next/ca-b.json b/test/config-next/ca-b.json index c6d2a44ca8d..9e9c9fc1e5c 100644 --- a/test/config-next/ca-b.json +++ b/test/config-next/ca-b.json @@ -18,7 +18,6 @@ }, "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] diff --git a/test/config-next/log-validator.json b/test/config-next/log-validator.json index 1d3d8aaac9c..bff0ca1f7d5 100644 --- a/test/config-next/log-validator.json +++ b/test/config-next/log-validator.json @@ -17,7 +17,6 @@ "/var/log/crl-storer.log", "/var/log/crl-updater.log", "/var/log/nonce-service.log", - "/var/log/ocsp-responder.log", - "/var/log/ocsp-updater.log" + "/var/log/ocsp-responder.log" ] } diff --git a/test/config-next/ocsp-updater.json b/test/config-next/ocsp-updater.json deleted file mode 100644 index 7ab3a971f49..00000000000 --- a/test/config-next/ocsp-updater.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "ocspUpdater": { - "db": { - "dbConnectFile": "test/secrets/ocsp_updater_dburl", - "maxOpenConns": 10 - }, - "readOnlyDB": { - "dbConnectFile": "test/secrets/ocsp_updater_ro_dburl", - "maxOpenConns": 100 - }, - "oldOCSPWindow": "2s", - "oldOCSPBatchSize": 5000, - "parallelGenerateOCSPRequests": 10, - "ocspMinTimeToExpiry": "72h", - "signFailureBackoffFactor": 1.2, - "signFailureBackoffMax": "30m", - "serialSuffixShards": "0 1 2 3 4 5 6 7 8 9 a b c d e f", - "debugAddr": ":8006", - "tls": { - "caCertFile": "test/grpc-creds/minica.pem", - "certFile": "test/grpc-creds/ocsp-updater.boulder/cert.pem", - "keyFile": "test/grpc-creds/ocsp-updater.boulder/key.pem" - }, - "ocspGeneratorService": { - "dnsAuthority": "10.55.55.10", - "srvLookup": { - "service": "ca", - "domain": "service.consul" - }, - "timeout": "15s", - "hostOverride": "ca.boulder" - }, - "features": {} - }, - "syslog": { - "stdoutlevel": 6, - "sysloglevel": -1 - } -} diff --git a/test/config-next/publisher.json b/test/config-next/publisher.json index b8e1f258dda..620a256ab82 100644 --- a/test/config-next/publisher.json +++ b/test/config-next/publisher.json @@ -27,7 +27,6 @@ "services": { "Publisher": { "clientNames": [ - "ocsp-updater.boulder", "ra.boulder" ] }, diff --git a/test/config-next/rocsp-tool.json b/test/config-next/rocsp-tool.json index b566e76659a..ab5363a97b0 100644 --- a/test/config-next/rocsp-tool.json +++ b/test/config-next/rocsp-tool.json @@ -2,7 +2,7 @@ "rocspTool": { "debugAddr": ":9101", "redis": { - "username": "ocsp-updater", + "username": "rocsp-tool", "passwordFile": "test/secrets/rocsp_tool_password", "shardAddrs": { "shard1": "10.33.33.2:4218", diff --git a/test/config/ca-a.json b/test/config/ca-a.json index d6110531710..d2633664395 100644 --- a/test/config/ca-a.json +++ b/test/config/ca-a.json @@ -18,7 +18,6 @@ }, "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] @@ -41,7 +40,6 @@ "services": { "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] diff --git a/test/config/ca-b.json b/test/config/ca-b.json index c9deea37cc1..a2cb52d7240 100644 --- a/test/config/ca-b.json +++ b/test/config/ca-b.json @@ -18,7 +18,6 @@ }, "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] @@ -41,7 +40,6 @@ "services": { "ca.OCSPGenerator": { "clientNames": [ - "ocsp-updater.boulder", "orphan-finder.boulder", "ra.boulder" ] diff --git a/test/config/log-validator.json b/test/config/log-validator.json index 1d3d8aaac9c..bff0ca1f7d5 100644 --- a/test/config/log-validator.json +++ b/test/config/log-validator.json @@ -17,7 +17,6 @@ "/var/log/crl-storer.log", "/var/log/crl-updater.log", "/var/log/nonce-service.log", - "/var/log/ocsp-responder.log", - "/var/log/ocsp-updater.log" + "/var/log/ocsp-responder.log" ] } diff --git a/test/config/ocsp-updater.json b/test/config/ocsp-updater.json deleted file mode 100644 index 7423725a6b2..00000000000 --- a/test/config/ocsp-updater.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "ocspUpdater": { - "db": { - "dbConnectFile": "test/secrets/ocsp_updater_dburl", - "maxOpenConns": 10 - }, - "readOnlyDB": { - "dbConnectFile": "test/secrets/ocsp_updater_ro_dburl", - "maxOpenConns": 100 - }, - "oldOCSPWindow": "2s", - "oldOCSPBatchSize": 5000, - "parallelGenerateOCSPRequests": 10, - "ocspMinTimeToExpiry": "72h", - "signFailureBackoffFactor": 1.2, - "signFailureBackoffMax": "30m", - "serialSuffixShards": "0 1 2 3 4 5 6 7 8 9 a b c d e f", - "debugAddr": ":8006", - "tls": { - "caCertFile": "test/grpc-creds/minica.pem", - "certFile": "test/grpc-creds/ocsp-updater.boulder/cert.pem", - "keyFile": "test/grpc-creds/ocsp-updater.boulder/key.pem" - }, - "ocspGeneratorService": { - "serverAddress": "ca.service.consul:9093", - "timeout": "15s", - "hostOverride": "ca.boulder" - }, - "features": {} - }, - "syslog": { - "stdoutlevel": 6, - "sysloglevel": 6 - } -} diff --git a/test/config/publisher.json b/test/config/publisher.json index 661fe48d8da..dbe14872f85 100644 --- a/test/config/publisher.json +++ b/test/config/publisher.json @@ -26,7 +26,6 @@ "address": ":9091", "clientNames": [ "health-checker.boulder", - "ocsp-updater.boulder", "ra.boulder" ] }, diff --git a/test/config/rocsp-tool.json b/test/config/rocsp-tool.json index b566e76659a..ab5363a97b0 100644 --- a/test/config/rocsp-tool.json +++ b/test/config/rocsp-tool.json @@ -2,7 +2,7 @@ "rocspTool": { "debugAddr": ":9101", "redis": { - "username": "ocsp-updater", + "username": "rocsp-tool", "passwordFile": "test/secrets/rocsp_tool_password", "shardAddrs": { "shard1": "10.33.33.2:4218", diff --git a/test/create_db.sh b/test/create_db.sh index 919b34d7097..5db9370a2ce 100755 --- a/test/create_db.sh +++ b/test/create_db.sh @@ -54,7 +54,7 @@ mysql ${dbconn} -e "SET GLOBAL binlog_format = 'MIXED';" # MariaDB sets the default @@max_connections value to 100. The SA alone is # configured to use up to 100 connections. We increase the max connections here -# to give headroom for other components (ocsp-updater for example). +# to give headroom for other components (ocsp-responder for example). mysql ${dbconn} -e "SET GLOBAL max_connections = 500;" for db in $DBS; do diff --git a/test/grpc-creds/generate.sh b/test/grpc-creds/generate.sh index bb8fee209df..ca674f5fdb3 100755 --- a/test/grpc-creds/generate.sh +++ b/test/grpc-creds/generate.sh @@ -9,7 +9,7 @@ command -v minica >/dev/null 2>&1 || { exit 1; } -for SERVICE in admin-revoker expiration-mailer ocsp-updater ocsp-responder \ +for SERVICE in admin-revoker expiration-mailer ocsp-responder \ orphan-finder wfe akamai-purger bad-key-revoker crl-updater crl-storer \ health-checker; do minica -domains "${SERVICE}.boulder" diff --git a/test/prometheus/prometheus.yml b/test/prometheus/prometheus.yml index 488d0aa5bae..76bf1c6f4ea 100644 --- a/test/prometheus/prometheus.yml +++ b/test/prometheus/prometheus.yml @@ -11,7 +11,6 @@ scrape_configs: - boulder:8003 - boulder:8004 - boulder:8005 - - boulder:8006 - boulder:8007 - boulder:8008 - boulder:8009 diff --git a/test/proxysql/proxysql.cnf b/test/proxysql/proxysql.cnf index f352c70050c..f918aa4538d 100644 --- a/test/proxysql/proxysql.cnf +++ b/test/proxysql/proxysql.cnf @@ -92,12 +92,6 @@ mysql_users = { username = "cert_checker"; }, - { - username = "ocsp_update"; - }, - { - username = "ocsp_update_ro"; - }, { username = "test_setup"; }, @@ -127,16 +121,6 @@ mysql_query_rules = username = "sa_ro"; timeout = 4900; }, - { - rule_id = 14; - username = "ocsp_update"; - timeout = 600000; - }, - { - rule_id = 15; - username = "ocsp_update_ro"; - timeout = 60000; - }, { rule_id = 16; username = "badkeyrevoker"; diff --git a/test/redis.config b/test/redis.config index 4a07ed80fde..8b2df8fab3b 100644 --- a/test/redis.config +++ b/test/redis.config @@ -20,7 +20,7 @@ rename-command SHUTDOWN "" rename-command SPOP "" rename-command SREM "" user default off -user ocsp-updater on +@all ~* >e4e9ce7845cb6adbbc44fb1d9deb05e6b4dc1386 +user rocsp-tool on +@all ~* >e4e9ce7845cb6adbbc44fb1d9deb05e6b4dc1386 user ocsp-responder on +@all ~* >0e5a4c8b5faaf3194c8ad83c3dd9a0dd8a75982b user boulder-ra on +@all ~* >b3b2fcbbf46fe39fd522c395a51f84d93a98ff2f user replication-user on +@all ~* >435e9c4225f08813ef3af7c725f0d30d263b9cd3 diff --git a/test/secrets/ocsp_updater_dburl b/test/secrets/ocsp_updater_dburl deleted file mode 100644 index 66f034985b4..00000000000 --- a/test/secrets/ocsp_updater_dburl +++ /dev/null @@ -1 +0,0 @@ -ocsp_update@tcp(boulder-proxysql:6033)/boulder_sa_integration?readTimeout=800ms&writeTimeout=800ms&timeout=100ms diff --git a/test/secrets/ocsp_updater_ro_dburl b/test/secrets/ocsp_updater_ro_dburl deleted file mode 100644 index de3978827da..00000000000 --- a/test/secrets/ocsp_updater_ro_dburl +++ /dev/null @@ -1 +0,0 @@ -ocsp_update_ro@tcp(boulder-proxysql:6033)/boulder_sa_integration?readTimeout=800ms&writeTimeout=800ms&timeout=100ms diff --git a/test/startservers.py b/test/startservers.py index bb35cb3bd11..a85339777dd 100644 --- a/test/startservers.py +++ b/test/startservers.py @@ -81,10 +81,6 @@ 9667, None, ('./bin/boulder', 'crl-storer', '--config', os.path.join(config_dir, 'crl-storer.json')), ('s3-test-srv',)), - Service('ocsp-updater', - 8006, None, - ('./bin/boulder', 'ocsp-updater', '--config', os.path.join(config_dir, 'ocsp-updater.json')), - ('boulder-ca-a', 'boulder-ca-b')), Service('crl-updater', 8021, None, ('./bin/boulder', 'crl-updater', '--config', os.path.join(config_dir, 'crl-updater.json')), diff --git a/test/v2_integration.py b/test/v2_integration.py index 05a55e2517d..c5197f5d61b 100644 --- a/test/v2_integration.py +++ b/test/v2_integration.py @@ -1242,9 +1242,6 @@ def test_auth_deactivation_v2(): def test_ocsp(): cert_file = temppath('test_ocsp.pem') chisel2.auth_and_issue([random_domain()], cert_output=cert_file.name) - - # As OCSP-Updater is generating responses independently of the CA we sit in a loop - # checking OCSP until we either see a good response or we timeout (5s). verify_ocsp(cert_file.name, "/hierarchy/intermediate-cert-rsa-a.pem", "http://localhost:4002", "good") def test_ct_submission(): diff --git a/test/vars/vars.go b/test/vars/vars.go index 2083163c321..22dd51bd3bc 100644 --- a/test/vars/vars.go +++ b/test/vars/vars.go @@ -13,8 +13,6 @@ var ( DBConnSAMailer = fmt.Sprintf(dbURL, "mailer", "boulder_sa_test") // DBConnSAFullPerms is the sa database connection with full perms DBConnSAFullPerms = fmt.Sprintf(dbURL, "test_setup", "boulder_sa_test") - // DBConnSAOcspUpdateRO is the sa ocsp_update_ro database connection - DBConnSAOcspUpdateRO = fmt.Sprintf(dbURL, "ocsp_update_ro", "boulder_sa_test") // DBInfoSchemaRoot is the root user and the information_schema connection. DBInfoSchemaRoot = fmt.Sprintf(dbURL, "root", "information_schema") // DBConnIncidents is the incidents database connection.