// This is somewhat gross but can be pared down a bit once the publisher and this // are fully smooshed together func newUpdater( stats metrics.Scope, clk clock.Clock, dbMap ocspDB, ca core.CertificateAuthority, pub core.Publisher, sac core.StorageAuthority, config cmd.OCSPUpdaterConfig, logConfigs []cmd.LogDescription, issuerPath string, log blog.Logger, ) (*OCSPUpdater, error) { if config.NewCertificateBatchSize == 0 || config.OldOCSPBatchSize == 0 || config.MissingSCTBatchSize == 0 { return nil, fmt.Errorf("Loop batch sizes must be non-zero") } if config.NewCertificateWindow.Duration == 0 || config.OldOCSPWindow.Duration == 0 || config.MissingSCTWindow.Duration == 0 { return nil, fmt.Errorf("Loop window sizes must be non-zero") } if config.OCSPStaleMaxAge.Duration == 0 { // Default to 30 days config.OCSPStaleMaxAge = cmd.ConfigDuration{Duration: time.Hour * 24 * 30} } if config.ParallelGenerateOCSPRequests == 0 { // Default to 1 config.ParallelGenerateOCSPRequests = 1 } logs := make([]*ctLog, len(logConfigs)) for i, logConfig := range logConfigs { l, err := newLog(logConfig) if err != nil { return nil, err } logs[i] = l } updater := OCSPUpdater{ stats: stats, clk: clk, dbMap: dbMap, cac: ca, log: log, sac: sac, pubc: pub, logs: logs, ocspMinTimeToExpiry: config.OCSPMinTimeToExpiry.Duration, ocspStaleMaxAge: config.OCSPStaleMaxAge.Duration, oldestIssuedSCT: config.OldestIssuedSCT.Duration, parallelGenerateOCSPRequests: config.ParallelGenerateOCSPRequests, } // Setup loops updater.loops = []*looper{ { clk: clk, stats: stats.NewScope("NewCertificates"), batchSize: config.NewCertificateBatchSize, tickDur: config.NewCertificateWindow.Duration, tickFunc: updater.newCertificateTick, name: "NewCertificates", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }, { clk: clk, stats: stats.NewScope("OldOCSPResponses"), batchSize: config.OldOCSPBatchSize, tickDur: config.OldOCSPWindow.Duration, tickFunc: updater.oldOCSPResponsesTick, name: "OldOCSPResponses", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }, // The missing SCT loop doesn't need to know about failureBackoffFactor or // failureBackoffMax as it doesn't make any calls to the CA { clk: clk, stats: stats.NewScope("MissingSCTReceipts"), batchSize: config.MissingSCTBatchSize, tickDur: config.MissingSCTWindow.Duration, tickFunc: updater.missingReceiptsTick, name: "MissingSCTReceipts", }, } if config.RevokedCertificateBatchSize != 0 && config.RevokedCertificateWindow.Duration != 0 { updater.loops = append(updater.loops, &looper{ clk: clk, stats: stats, batchSize: config.RevokedCertificateBatchSize, tickDur: config.RevokedCertificateWindow.Duration, tickFunc: updater.revokedCertificatesTick, name: "RevokedCertificates", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }) } // TODO(#1050): Remove this gate and the nil ccu checks below if config.AkamaiBaseURL != "" { issuer, err := core.LoadCert(issuerPath) ccu, err := akamai.NewCachePurgeClient( config.AkamaiBaseURL, config.AkamaiClientToken, config.AkamaiClientSecret, config.AkamaiAccessToken, config.AkamaiPurgeRetries, config.AkamaiPurgeRetryBackoff.Duration, log, stats, ) if err != nil { return nil, err } updater.ccu = ccu updater.issuer = issuer } return &updater, nil }
// This is somewhat gross but can be pared down a bit once the publisher and this // are fully smooshed together func newUpdater( stats statsd.Statter, clk clock.Clock, dbMap *gorp.DbMap, ca core.CertificateAuthority, pub core.Publisher, sac core.StorageAuthority, config cmd.OCSPUpdaterConfig, numLogs int, issuerPath string, ) (*OCSPUpdater, error) { if config.NewCertificateBatchSize == 0 || config.OldOCSPBatchSize == 0 || config.MissingSCTBatchSize == 0 { return nil, fmt.Errorf("Loop batch sizes must be non-zero") } if config.NewCertificateWindow.Duration == 0 || config.OldOCSPWindow.Duration == 0 || config.MissingSCTWindow.Duration == 0 { return nil, fmt.Errorf("Loop window sizes must be non-zero") } log := blog.GetAuditLogger() updater := OCSPUpdater{ stats: stats, clk: clk, dbMap: dbMap, cac: ca, log: log, sac: sac, pubc: pub, numLogs: numLogs, ocspMinTimeToExpiry: config.OCSPMinTimeToExpiry.Duration, oldestIssuedSCT: config.OldestIssuedSCT.Duration, } // Setup loops updater.loops = []*looper{ &looper{ clk: clk, stats: stats, batchSize: config.NewCertificateBatchSize, tickDur: config.NewCertificateWindow.Duration, tickFunc: updater.newCertificateTick, name: "NewCertificates", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }, &looper{ clk: clk, stats: stats, batchSize: config.OldOCSPBatchSize, tickDur: config.OldOCSPWindow.Duration, tickFunc: updater.oldOCSPResponsesTick, name: "OldOCSPResponses", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }, // The missing SCT loop doesn't need to know about failureBackoffFactor or // failureBackoffMax as it doesn't make any calls to the CA &looper{ clk: clk, stats: stats, batchSize: config.MissingSCTBatchSize, tickDur: config.MissingSCTWindow.Duration, tickFunc: updater.missingReceiptsTick, name: "MissingSCTReceipts", }, } if config.RevokedCertificateBatchSize != 0 && config.RevokedCertificateWindow.Duration != 0 { updater.loops = append(updater.loops, &looper{ clk: clk, stats: stats, batchSize: config.RevokedCertificateBatchSize, tickDur: config.RevokedCertificateWindow.Duration, tickFunc: updater.revokedCertificatesTick, name: "RevokedCertificates", failureBackoffFactor: config.SignFailureBackoffFactor, failureBackoffMax: config.SignFailureBackoffMax.Duration, }) } // TODO(#1050): Remove this gate and the nil ccu checks below if config.AkamaiBaseURL != "" { issuer, err := core.LoadCert(issuerPath) ccu, err := akamai.NewCachePurgeClient( config.AkamaiBaseURL, config.AkamaiClientToken, config.AkamaiClientSecret, config.AkamaiAccessToken, config.AkamaiPurgeRetries, config.AkamaiPurgeRetryBackoff.Duration, log, stats, ) if err != nil { return nil, err } updater.ccu = ccu updater.issuer = issuer } return &updater, nil }
func main() { app := cli.NewApp() app.Name = "akamai-purger" app.Usage = "Purge a resource from the Akamai CDN cache" app.Version = cmd.Version() app.Author = "Boulder contributors" app.Email = "*****@*****.**" app.Flags = []cli.Flag{ cli.StringFlag{ Name: "config", Value: "config.json", EnvVar: "BOULDER_CONFIG", Usage: "Path to Boulder JSON configuration file", }, cli.StringFlag{ Name: "url", Usage: "URL to purge from CDN", }, } app.Action = func(c *cli.Context) { configFileName := c.GlobalString("config") url := c.GlobalString("url") if url == "" || configFileName == "" { fmt.Println("Both -url -config (or BOULDER_CONFIG) are required") return } configJSON, err := ioutil.ReadFile(configFileName) if err != nil { fmt.Printf("Failed to read config file: %s\n", err) return } var config cmd.Config err = json.Unmarshal(configJSON, &config) // Set up logging stats, auditlogger := cmd.StatsAndLogging(config.Statsd, config.Syslog) auditlogger.Info(app.Version) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() akamaiClient, err := akamai.NewCachePurgeClient( config.OCSPUpdater.AkamaiBaseURL, config.OCSPUpdater.AkamaiClientToken, config.OCSPUpdater.AkamaiClientSecret, config.OCSPUpdater.AkamaiAccessToken, config.OCSPUpdater.AkamaiPurgeRetries, config.OCSPUpdater.AkamaiPurgeRetryBackoff.Duration, auditlogger, stats, ) cmd.FailOnError(err, "Failed to create Akamai CachePurgeClient") err = akamaiClient.Purge([]string{url}) cmd.FailOnError(err, "Failed to purge requested resource") } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }