示例#1
0
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")
}
示例#2
0
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)
}
示例#3
0
文件: main.go 项目: patf/boulder
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")
}
示例#4
0
文件: main.go 项目: devpaul/boulder
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")
}
示例#5
0
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")
}
示例#6
0
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")
}
示例#7
0
文件: main.go 项目: ajvb/boulder
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)
}
示例#8
0
文件: main.go 项目: patf/boulder
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")
}