func main() { yes := flag.Bool("yes", false, "Skips the purge confirmation") configPath := flag.String("config", "config.json", "Path to Boulder configuration file") flag.Parse() configJSON, err := ioutil.ReadFile(*configPath) if err != nil { fmt.Fprintf(os.Stderr, "Failed to read config file '%s': %s\n", *configPath, err) os.Exit(1) } var config eapConfig err = json.Unmarshal(configJSON, &config) cmd.FailOnError(err, "Failed to parse config") // Set up logging stats, auditlogger := cmd.StatsAndLogging(config.ExpiredAuthzPurger.Statsd, config.ExpiredAuthzPurger.Syslog) auditlogger.Info(cmd.Version()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() // Configure DB dbURL, err := config.ExpiredAuthzPurger.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, config.ExpiredAuthzPurger.DBConfig.MaxDBConns) cmd.FailOnError(err, "Could not connect to database") go sa.ReportDbConnCount(dbMap, metrics.NewStatsdScope(stats, "AuthzPurger")) purger := &expiredAuthzPurger{ stats: stats, log: auditlogger, clk: cmd.Clock(), db: dbMap, batchSize: int64(config.ExpiredAuthzPurger.BatchSize), } if config.ExpiredAuthzPurger.GracePeriod.Duration == 0 { fmt.Fprintln(os.Stderr, "Grace period is 0, refusing to purge all pending authorizations") os.Exit(1) } purgeBefore := purger.clk.Now().Add(-config.ExpiredAuthzPurger.GracePeriod.Duration) _, err = purger.purgeAuthzs(purgeBefore, *yes) cmd.FailOnError(err, "Failed to purge authorizations") }
func main() { app := cli.NewApp() app.Name = "policy-loader" app.Usage = "Loads/dumps rules into/from the policy database" 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: "rule-file", Value: "rules.json", EnvVar: "BOULDER_POLICY_RULES", Usage: "Path to Boulder policy whitelist and blacklist rule file", }, } app.Commands = append(app.Commands, []cli.Command{ cli.Command{ Name: "dump-rules", Usage: "Write out whitelist and blacklist from database to a rule file", Action: func(c *cli.Context) { padb, ruleFile := setupFromContext(c) ruleSet, err := padb.DumpRules() cmd.FailOnError(err, "Couldn't retrieve whitelist rules") var rules struct { Blacklist []string Whitelist []string } for _, r := range ruleSet.Blacklist { rules.Blacklist = append(rules.Blacklist, r.Host) } for _, r := range ruleSet.Whitelist { rules.Whitelist = append(rules.Whitelist, r.Host) } rulesJSON, err := json.Marshal(rules) cmd.FailOnError(err, "Couldn't marshal rule list") err = ioutil.WriteFile(ruleFile, rulesJSON, os.ModePerm) cmd.FailOnError(err, "Failed to write the rule file") fmt.Printf("# Saved rule list to %s\n", ruleFile) }, }, cli.Command{ Name: "load-rules", Usage: "Load whitelist and blacklist into database from a rule file", Action: func(c *cli.Context) { padb, ruleFile := setupFromContext(c) rulesJSON, err := ioutil.ReadFile(ruleFile) cmd.FailOnError(err, "Couldn't read configuration file") rules := policy.RawRuleSet{} err = json.Unmarshal(rulesJSON, &rules) cmd.FailOnError(err, "Couldn't unmarshal rules list") rs := policy.RuleSet{} for _, r := range rules.Blacklist { rs.Blacklist = append(rs.Blacklist, policy.BlacklistRule{ Host: strings.ToLower(r), }) } for _, r := range rules.Whitelist { rs.Whitelist = append(rs.Whitelist, policy.WhitelistRule{ Host: strings.ToLower(r), }) } err = padb.LoadRules(rs) cmd.FailOnError(err, "Couldn't load rules") fmt.Println("# Loaded whitelist and blacklist into database") }, }, }...) app.Run(os.Args) }
func main() { app := cli.NewApp() app.Name = "single-ocsp" app.Usage = `Creates a single OCSP response. According to the BRs, the OCSP responses for intermediate certificate must be issued once per year. So there's a need to issue OCSP responses for these certificates, but it doesn't make sense to use all the infrastructure that the "ocsp-updater" tool requires. This tool allows an administrator to manually generate an OCSP response for an intermediate certificate. ` app.Version = cmd.Version() app.Author = "Boulder contributors" app.Email = "*****@*****.**" app.Flags = []cli.Flag{ cli.StringFlag{ Name: "issuer", Usage: "Issuer certificate (DER)", }, cli.StringFlag{ Name: "responder", Usage: "OCSP responder certificate (DER)", }, cli.StringFlag{ Name: "target", Usage: "Certificate whose status is being reported (DER)", }, cli.StringFlag{ Name: "template", Usage: `OCSP template file (JSON), e.g.: { "Status": 0, // Good "ThisUpdate": "2015-08-26T00:00:00Z", "NextUpdate": "2016-08-26T00:00:00Z" } { "Status": 1, // Revoked "ThisUpdate": "2015-08-26T00:00:00Z", "NextUpdate": "2016-08-26T00:00:00Z", "RevokedAt": "2015-08-20T00:00:00Z", "RevocationReason": 1 // Key compromise } `, }, cli.StringFlag{ Name: "pkcs11", Usage: `PKCS#11 configuration (JSON), e.g.: { "Module": "/Library/OpenSC/lib/opensc-pkcs11.so", "Token": "Yubico Yubikey NEO CCID", "Label": "PIV AUTH key", "PIN": "123456" } `, }, cli.StringFlag{ Name: "out", Usage: "File to which the OCSP response will be written", }, } app.Action = func(c *cli.Context) { issuer, responder, target, template, pkcs11, err := readFiles(c) cmd.FailOnError(err, "Failed to read files") // Instantiate the private key from PKCS11 priv, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel) cmd.FailOnError(err, "Failed to load PKCS#11 key") // Populate the remaining fields in the template template.SerialNumber = target.SerialNumber template.Certificate = responder // Sign the OCSP response responseBytes, err := ocsp.CreateResponse(issuer, responder, template, priv) cmd.FailOnError(err, "Failed to sign OCSP response") // Write the OCSP response to stdout outFile := c.GlobalString("out") if len(outFile) == 0 { cmd.FailOnError(fmt.Errorf(""), "No output file provided") } err = ioutil.WriteFile(outFile, responseBytes, 0666) cmd.FailOnError(err, "Failed to write output file") } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }
func main() { app := cli.NewApp() app.Name = "admin-revoker" app.Usage = "Revokes issued certificates" 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.BoolFlag{ Name: "deny", Usage: "Add certificate DNS names to the denied list", }, } app.Commands = []cli.Command{ { Name: "serial-revoke", Usage: "Revoke a single certificate by the hex serial number", Action: func(c *cli.Context) { // 1: serial, 2: reasonCode (3: deny flag) serial := c.Args().First() reasonCode, err := strconv.Atoi(c.Args().Get(1)) cmd.FailOnError(err, "Reason code argument must be a integer") deny := c.GlobalBool("deny") cac, auditlogger, dbMap, _ := setupContext(c) tx, err := dbMap.Begin() if err != nil { tx.Rollback() } cmd.FailOnError(err, "Couldn't begin transaction") err = revokeBySerial(serial, core.RevocationCode(reasonCode), deny, cac, auditlogger, tx) if err != nil { tx.Rollback() } cmd.FailOnError(err, "Couldn't revoke certificate") err = tx.Commit() cmd.FailOnError(err, "Couldn't cleanly close transaction") }, }, { Name: "reg-revoke", Usage: "Revoke all certificates associated with a registration ID", Action: func(c *cli.Context) { // 1: registration ID, 2: reasonCode (3: deny flag) regID, err := strconv.ParseInt(c.Args().First(), 10, 64) cmd.FailOnError(err, "Registration ID argument must be a integer") reasonCode, err := strconv.Atoi(c.Args().Get(1)) cmd.FailOnError(err, "Reason code argument must be a integer") deny := c.GlobalBool("deny") cac, auditlogger, dbMap, sac := setupContext(c) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() tx, err := dbMap.Begin() if err != nil { tx.Rollback() } cmd.FailOnError(err, "Couldn't begin transaction") _, err = sac.GetRegistration(regID) if err != nil { cmd.FailOnError(err, "Couldn't fetch registration") } err = revokeByReg(regID, core.RevocationCode(reasonCode), deny, cac, auditlogger, tx) if err != nil { tx.Rollback() } cmd.FailOnError(err, "Couldn't revoke certificate") err = tx.Commit() cmd.FailOnError(err, "Couldn't cleanly close transaction") }, }, { Name: "list-reasons", Usage: "List all revocation reason codes", Action: func(c *cli.Context) { var codes core.RevocationCodes for k := range core.RevocationReasons { codes = append(codes, k) } sort.Sort(codes) fmt.Printf("Revocation reason codes\n-----------------------\n\n") for _, k := range codes { fmt.Printf("%d: %s\n", k, core.RevocationReasons[k]) } }, }, } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }
func main() { app := cli.NewApp() app.Name = "cert-checker" app.Usage = "Checks validity of issued certificates stored in the database" app.Version = cmd.Version() app.Author = "Boulder contributors" app.Email = "*****@*****.**" app.Flags = []cli.Flag{ cli.IntFlag{ Name: "workers", Value: runtime.NumCPU(), Usage: "The number of concurrent workers used to process certificates", }, cli.BoolFlag{ Name: "unexpired-only", Usage: "Only check currently unexpired certificates", }, cli.BoolFlag{ Name: "bad-results-only", Usage: "Only collect and display bad results", }, cli.StringFlag{ Name: "db-connect", Usage: "SQL URI if not provided in the configuration file", }, cli.StringFlag{ Name: "check-period", Value: "2160h", Usage: "How far back to check", }, cli.StringFlag{ Name: "config", Value: "config.json", Usage: "Path to configuration file", }, } app.Action = func(c *cli.Context) { configPath := c.GlobalString("config") if configPath == "" { fmt.Fprintln(os.Stderr, "--config is required") os.Exit(1) } configBytes, err := ioutil.ReadFile(configPath) cmd.FailOnError(err, "Failed to read config file") var config cmd.Config err = json.Unmarshal(configBytes, &config) cmd.FailOnError(err, "Failed to parse config file") stats, err := metrics.NewStatter(config.Statsd.Server, config.Statsd.Prefix) cmd.FailOnError(err, "Failed to create StatsD client") syslogger, err := syslog.Dial("", "", syslog.LOG_INFO|syslog.LOG_LOCAL0, "") cmd.FailOnError(err, "Failed to dial syslog") logger, err := blog.New(syslogger, 0, 0) cmd.FailOnError(err, "Failed to construct logger") err = blog.Set(logger) cmd.FailOnError(err, "Failed to set audit logger") if connect := c.GlobalString("db-connect"); connect != "" { config.CertChecker.DBConnect = connect } if workers := c.GlobalInt("workers"); workers != 0 { config.CertChecker.Workers = workers } config.CertChecker.UnexpiredOnly = c.GlobalBool("valid-only") config.CertChecker.BadResultsOnly = c.GlobalBool("bad-results-only") if cp := c.GlobalString("check-period"); cp != "" { config.CertChecker.CheckPeriod.Duration, err = time.ParseDuration(cp) cmd.FailOnError(err, "Failed to parse check period") } // Validate PA config and set defaults if needed cmd.FailOnError(config.PA.CheckChallenges(), "Invalid PA configuration") saDbURL, err := config.CertChecker.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") saDbMap, err := sa.NewDbMap(saDbURL, config.CertChecker.DBConfig.MaxDBConns) cmd.FailOnError(err, "Could not connect to database") go sa.ReportDbConnCount(saDbMap, metrics.NewStatsdScope(stats, "CertChecker")) pa, err := policy.New(config.PA.Challenges) cmd.FailOnError(err, "Failed to create PA") err = pa.SetHostnamePolicyFile(config.CertChecker.HostnamePolicyFile) cmd.FailOnError(err, "Failed to load HostnamePolicyFile") checker := newChecker( saDbMap, clock.Default(), pa, config.CertChecker.CheckPeriod.Duration, ) fmt.Fprintf(os.Stderr, "# Getting certificates issued in the last %s\n", config.CertChecker.CheckPeriod) // Since we grab certificates in batches we don't want this to block, when it // is finished it will close the certificate channel which allows the range // loops in checker.processCerts to break go func() { err = checker.getCerts(config.CertChecker.UnexpiredOnly) cmd.FailOnError(err, "Batch retrieval of certificates failed") }() fmt.Fprintf(os.Stderr, "# Processing certificates using %d workers\n", config.CertChecker.Workers) wg := new(sync.WaitGroup) for i := 0; i < config.CertChecker.Workers; i++ { wg.Add(1) go func() { s := checker.clock.Now() checker.processCerts(wg, config.CertChecker.BadResultsOnly) stats.TimingDuration("certChecker.processingLatency", time.Since(s), 1.0) }() } wg.Wait() fmt.Fprintf( os.Stderr, "# Finished processing certificates, sample: %d, good: %d, bad: %d\n", len(checker.issuedReport.Entries), checker.issuedReport.GoodCerts, checker.issuedReport.BadCerts, ) err = checker.issuedReport.dump() cmd.FailOnError(err, "Failed to dump results: %s\n") } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }
func main() { ctx := context.Background() app := cli.NewApp() app.Name = "admin-revoker" app.Usage = "Revokes issued certificates" 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", }, } app.Commands = []cli.Command{ { Name: "serial-revoke", Usage: "Revoke a single certificate by the hex serial number", Action: func(c *cli.Context) { // 1: serial, 2: reasonCode serial := c.Args().First() reasonCode, err := strconv.Atoi(c.Args().Get(1)) cmd.FailOnError(err, "Reason code argument must be an integer") cac, logger, dbMap, _, _ := setupContext(c) tx, err := dbMap.Begin() if err != nil { cmd.FailOnError(sa.Rollback(tx, err), "Couldn't begin transaction") } err = revokeBySerial(ctx, serial, core.RevocationCode(reasonCode), cac, logger, tx) if err != nil { cmd.FailOnError(sa.Rollback(tx, err), "Couldn't revoke certificate") } err = tx.Commit() cmd.FailOnError(err, "Couldn't cleanly close transaction") }, }, { Name: "reg-revoke", Usage: "Revoke all certificates associated with a registration ID", Action: func(c *cli.Context) { // 1: registration ID, 2: reasonCode regID, err := strconv.ParseInt(c.Args().First(), 10, 64) cmd.FailOnError(err, "Registration ID argument must be an integer") reasonCode, err := strconv.Atoi(c.Args().Get(1)) cmd.FailOnError(err, "Reason code argument must be an integer") cac, logger, dbMap, sac, _ := setupContext(c) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer logger.AuditPanic() tx, err := dbMap.Begin() if err != nil { cmd.FailOnError(sa.Rollback(tx, err), "Couldn't begin transaction") } _, err = sac.GetRegistration(ctx, regID) if err != nil { cmd.FailOnError(err, "Couldn't fetch registration") } err = revokeByReg(ctx, regID, core.RevocationCode(reasonCode), cac, logger, tx) if err != nil { cmd.FailOnError(sa.Rollback(tx, err), "Couldn't revoke certificate") } err = tx.Commit() cmd.FailOnError(err, "Couldn't cleanly close transaction") }, }, { Name: "list-reasons", Usage: "List all revocation reason codes", Action: func(c *cli.Context) { var codes revocationCodes for k := range core.RevocationReasons { codes = append(codes, k) } sort.Sort(codes) fmt.Printf("Revocation reason codes\n-----------------------\n\n") for _, k := range codes { fmt.Printf("%d: %s\n", k, core.RevocationReasons[k]) } }, }, { Name: "auth-revoke", Usage: "Revoke all pending/valid authorizations for a domain", Action: func(c *cli.Context) { domain := c.Args().First() _, logger, _, sac, stats := setupContext(c) ident := core.AcmeIdentifier{Value: domain, Type: core.IdentifierDNS} authsRevoked, pendingAuthsRevoked, err := sac.RevokeAuthorizationsByDomain(ctx, ident) cmd.FailOnError(err, fmt.Sprintf("Failed to revoke authorizations for %s", ident.Value)) logger.Info(fmt.Sprintf( "Revoked %d pending authorizations and %d final authorizations\n", authsRevoked, pendingAuthsRevoked, )) stats.Inc("admin-revoker.revokedAuthorizations", authsRevoked, 1.0) stats.Inc("admin-revoker.revokedPendingAuthorizations", pendingAuthsRevoked, 1.0) }, }, } err := app.Run(os.Args) cmd.FailOnError(err, "Failed to run application") }
func main() { app := cli.NewApp() app.Name = "orphan-finder" app.Usage = "Reads orphaned certificates from a boulder-ca log or a der file and add them to the database" 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", }, } app.Commands = []cli.Command{ { Name: "parse-ca-log", Usage: "Parses boulder-ca logs to add multiple orphaned certificates", Flags: []cli.Flag{ cli.StringFlag{ Name: "log-file", Usage: "Path to boulder-ca log file to parse", }, }, Action: func(c *cli.Context) { stats, logger, sa := setup(c) logPath := c.String("log-file") if logPath == "" { fmt.Println("log file path must be provided") os.Exit(1) } logData, err := ioutil.ReadFile(logPath) cmd.FailOnError(err, "Failed to read log file") orphansFound := int64(0) orphansAdded := int64(0) for _, line := range strings.Split(string(logData), "\n") { found, added := parseLogLine(sa, logger, line) if found { orphansFound++ if added { orphansAdded++ } } } logger.Info(fmt.Sprintf("Found %d orphans and added %d to the database\n", orphansFound, orphansAdded)) stats.Inc("orphaned-certificates.found", orphansFound, 1.0) stats.Inc("orphaned-certificates.added", orphansAdded, 1.0) stats.Inc("orphaned-certificates.adding-failed", orphansFound-orphansAdded, 1.0) }, }, { Name: "parse-der", Usage: "Parses a single orphaned DER certificate file and adds it to the database", Flags: []cli.Flag{ cli.StringFlag{ Name: "der-file", Usage: "Path to DER certificate file", }, cli.IntFlag{ Name: "regID", Usage: "Registration ID of user who requested the certificate", }, }, Action: func(c *cli.Context) { _, _, sa := setup(c) derPath := c.String("der-file") if derPath == "" { fmt.Println("der file path must be provided") os.Exit(1) } regID := c.Int("regID") if regID == 0 { fmt.Println("--regID must be non-zero") os.Exit(1) } der, err := ioutil.ReadFile(derPath) cmd.FailOnError(err, "Failed to read DER file") err = checkDER(sa, der) cmd.FailOnError(err, "Pre-AddCertificate checks failed") _, err = sa.AddCertificate(der, int64(regID)) cmd.FailOnError(err, "Failed to add certificate to database") }, }, } app.Run(os.Args) }
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") }