func main() { dryRun := flag.Bool("dryRun", true, "Whether to do a dry run.") sleep := flag.Duration("sleep", 60*time.Second, "How long to sleep between batches.") batchSize := flag.Uint("batchSize", 1000, "Number of certificates to process between sleeps.") numBatches := flag.Uint("numBatches", 999999, "Stop processing after N batches.") type config struct { NotAfterBackFiller struct { cmd.DBConfig } Statsd cmd.StatsdConfig Syslog cmd.SyslogConfig } configFile := flag.String("config", "", "File containing a JSON config.") flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n\n", usageIntro) fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() if *configFile == "" { flag.Usage() os.Exit(1) } configData, err := ioutil.ReadFile(*configFile) cmd.FailOnError(err, fmt.Sprintf("Reading %q", *configFile)) var cfg config err = json.Unmarshal(configData, &cfg) cmd.FailOnError(err, "Unmarshaling config") stats, log := cmd.StatsAndLogging(cfg.Statsd, cfg.Syslog) defer log.AuditPanic() dbURL, err := cfg.NotAfterBackFiller.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, 10) cmd.FailOnError(err, "Could not connect to database") go sa.ReportDbConnCount(dbMap, metrics.NewStatsdScope(stats, "NotAfterBackfiller")) b := backfiller{ dbMap: dbMap, log: log, clk: cmd.Clock(), dryRun: *dryRun, batchSize: *batchSize, numBatches: *numBatches, sleep: *sleep, } err = b.processForever() cmd.FailOnError(err, "Could not process certificate batches") }
func main() { outFile := flag.String("outfile", "", "File to write contacts to (defaults to stdout).") grace := flag.Duration("grace", 2*24*time.Hour, "Include contacts with certificates that expired in < grace ago") type config struct { ContactExporter struct { cmd.DBConfig cmd.PasswordConfig } } configFile := flag.String("config", "", "File containing a JSON config.") flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n\n", usageIntro) fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() if *outFile == "" || *configFile == "" { flag.Usage() os.Exit(1) } _, log := cmd.StatsAndLogging(cmd.StatsdConfig{}, cmd.SyslogConfig{StdoutLevel: 7}) configData, err := ioutil.ReadFile(*configFile) cmd.FailOnError(err, fmt.Sprintf("Reading %q", *configFile)) var cfg config err = json.Unmarshal(configData, &cfg) cmd.FailOnError(err, "Unmarshaling config") dbURL, err := cfg.ContactExporter.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, 10) cmd.FailOnError(err, "Could not connect to database") exporter := contactExporter{ log: log, dbMap: dbMap, clk: cmd.Clock(), grace: *grace, } contacts, err := exporter.findContacts() cmd.FailOnError(err, "Could not find contacts") err = writeContacts(contacts, *outFile) cmd.FailOnError(err, fmt.Sprintf("Could not write contacts to outfile %q", *outFile)) }
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() { outFile := flag.String("outfile", "", "File to write contacts to (defaults to stdout).") type config struct { ContactExporter struct { cmd.DBConfig cmd.PasswordConfig } } configFile := flag.String("config", "", "File containing a JSON config.") flag.Parse() if outFile == nil { flag.Usage() os.Exit(1) } _, log := cmd.StatsAndLogging(cmd.StatsdConfig{}, cmd.SyslogConfig{StdoutLevel: 7}) configData, err := ioutil.ReadFile(*configFile) cmd.FailOnError(err, fmt.Sprintf("Reading %s", *configFile)) var cfg config err = json.Unmarshal(configData, &cfg) cmd.FailOnError(err, "Unmarshaling config") dbURL, err := cfg.ContactExporter.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, 10) cmd.FailOnError(err, "Could not connect to database") exporter := contactExporter{ log: log, dbMap: dbMap, clk: cmd.Clock(), } contacts, err := exporter.findContacts() cmd.FailOnError(err, "Could not find contacts") err = writeContacts(contacts, *outFile) cmd.FailOnError(err, fmt.Sprintf("Could not write contacts to outfile '%s'", *outFile)) }
func main() { app := cmd.NewAppShell("expiration-mailer", "Sends certificate expiration emails") app.App.Flags = append(app.App.Flags, cli.IntFlag{ Name: "cert_limit", Value: 100, EnvVar: "CERT_LIMIT", Usage: "Count of certificates to process per expiration period", }) app.Config = func(c *cli.Context, config cmd.Config) cmd.Config { if c.GlobalInt("cert_limit") > 0 { config.Mailer.CertLimit = c.GlobalInt("cert_limit") } return config } app.Action = func(c cmd.Config, stats metrics.Statter, logger blog.Logger) { go cmd.DebugServer(c.Mailer.DebugAddr) // Configure DB dbURL, err := c.Mailer.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL) cmd.FailOnError(err, "Could not connect to database") amqpConf := c.Mailer.AMQP sac, err := rpc.NewStorageAuthorityClient(clientName, amqpConf, stats) cmd.FailOnError(err, "Failed to create SA client") // Load email template emailTmpl, err := ioutil.ReadFile(c.Mailer.EmailTemplate) cmd.FailOnError(err, fmt.Sprintf("Could not read email template file [%s]", c.Mailer.EmailTemplate)) tmpl, err := template.New("expiry-email").Parse(string(emailTmpl)) cmd.FailOnError(err, "Could not parse email template") _, err = netmail.ParseAddress(c.Mailer.From) cmd.FailOnError(err, fmt.Sprintf("Could not parse from address: %s", c.Mailer.From)) smtpPassword, err := c.Mailer.PasswordConfig.Pass() cmd.FailOnError(err, "Failed to load SMTP password") mailClient := mail.New(c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, smtpPassword, c.Mailer.From) err = mailClient.Connect() cmd.FailOnError(err, "Couldn't connect to mail server.") defer func() { _ = mailClient.Close() }() nagCheckInterval := defaultNagCheckInterval if s := c.Mailer.NagCheckInterval; s != "" { nagCheckInterval, err = time.ParseDuration(s) if err != nil { logger.Err(fmt.Sprintf("Failed to parse NagCheckInterval string %q: %s", s, err)) return } } var nags durationSlice for _, nagDuration := range c.Mailer.NagTimes { dur, err := time.ParseDuration(nagDuration) if err != nil { logger.Err(fmt.Sprintf("Failed to parse nag duration string [%s]: %s", nagDuration, err)) return } nags = append(nags, dur+nagCheckInterval) } // Make sure durations are sorted in increasing order sort.Sort(nags) subject := "Certificate expiration notice" if c.Mailer.Subject != "" { subject = c.Mailer.Subject } m := mailer{ stats: stats, subject: subject, log: logger, dbMap: dbMap, rs: sac, mailer: &mailClient, emailTemplate: tmpl, nagTimes: nags, limit: c.Mailer.CertLimit, clk: cmd.Clock(), } logger.Info("expiration-mailer: Starting") err = m.findExpiringCertificates() cmd.FailOnError(err, "expiration-mailer has failed") } app.Run() }
func main() { from := flag.String("from", "", "From header for emails. Must be a bare email address.") subject := flag.String("subject", "", "Subject of emails") toFile := flag.String("toFile", "", "File containing a list of email addresses to send to, one per file.") bodyFile := flag.String("body", "", "File containing the email body in plain text format.") dryRun := flag.Bool("dryRun", true, "Whether to do a dry run.") sleep := flag.Duration("sleep", 60*time.Second, "How long to sleep between emails.") start := flag.Int("start", 0, "Line of input file to start from.") end := flag.Int("end", 99999999, "Line of input file to end before.") type config struct { NotifyMailer struct { cmd.DBConfig cmd.PasswordConfig cmd.SMTPConfig } } configFile := flag.String("config", "", "File containing a JSON config.") flag.Parse() if from == nil || subject == nil || bodyFile == nil || configFile == nil { flag.Usage() os.Exit(1) } _, log := cmd.StatsAndLogging(cmd.StatsdConfig{}, cmd.SyslogConfig{StdoutLevel: 7}) configData, err := ioutil.ReadFile(*configFile) cmd.FailOnError(err, fmt.Sprintf("Reading %s", *configFile)) var cfg config err = json.Unmarshal(configData, &cfg) cmd.FailOnError(err, "Unmarshaling config") dbURL, err := cfg.NotifyMailer.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, 10) cmd.FailOnError(err, "Could not connect to database") // Load email body body, err := ioutil.ReadFile(*bodyFile) cmd.FailOnError(err, fmt.Sprintf("Reading %s", *bodyFile)) address, err := mail.ParseAddress(*from) cmd.FailOnError(err, fmt.Sprintf("Parsing %s", *from)) toBody, err := ioutil.ReadFile(*toFile) cmd.FailOnError(err, fmt.Sprintf("Reading %s", *toFile)) destinations := strings.Split(string(toBody), "\n") checkpointRange := interval{ start: *start, end: *end, } var mailClient bmail.Mailer if *dryRun { mailClient = bmail.NewDryRun(*address, log) } else { smtpPassword, err := cfg.NotifyMailer.PasswordConfig.Pass() cmd.FailOnError(err, "Failed to load SMTP password") mailClient = bmail.New( cfg.NotifyMailer.Server, cfg.NotifyMailer.Port, cfg.NotifyMailer.Username, smtpPassword, *address) } err = mailClient.Connect() cmd.FailOnError(err, fmt.Sprintf("Connecting to %s:%s", cfg.NotifyMailer.Server, cfg.NotifyMailer.Port)) defer func() { err = mailClient.Close() cmd.FailOnError(err, "Closing mail client") }() m := mailer{ clk: cmd.Clock(), log: log, dbMap: dbMap, mailer: mailClient, subject: *subject, destinations: destinations, emailTemplate: string(body), checkpoint: checkpointRange, sleepInterval: *sleep, } err = m.run() cmd.FailOnError(err, "mailer.send returned error") }
func main() { configFile := flag.String("config", "", "File path to the configuration file for this service") certLimit := flag.Int("cert_limit", 0, "Count of certificates to process per expiration period") reconnBase := flag.Duration("reconnectBase", 1*time.Second, "Base sleep duration between reconnect attempts") reconnMax := flag.Duration("reconnectMax", 5*60*time.Second, "Max sleep duration between reconnect attempts after exponential backoff") 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") stats, logger := cmd.StatsAndLogging(c.Statsd, c.Syslog) scope := metrics.NewStatsdScope(stats, "Expiration") defer logger.AuditPanic() logger.Info(cmd.VersionString(clientName)) if *certLimit > 0 { c.Mailer.CertLimit = *certLimit } // Default to 100 if no certLimit is set if c.Mailer.CertLimit == 0 { c.Mailer.CertLimit = 100 } // Configure DB dbURL, err := c.Mailer.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, c.Mailer.DBConfig.MaxDBConns) sa.SetSQLDebug(dbMap, logger) cmd.FailOnError(err, "Could not connect to database") go sa.ReportDbConnCount(dbMap, scope) var sac core.StorageAuthority if c.Mailer.SAService != nil { conn, err := bgrpc.ClientSetup(c.Mailer.SAService, scope) cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA") sac = bgrpc.NewStorageAuthorityClient(sapb.NewStorageAuthorityClient(conn)) } else { sac, err = rpc.NewStorageAuthorityClient(clientName, c.Mailer.AMQP, scope) cmd.FailOnError(err, "Failed to create SA client") } // Load email template emailTmpl, err := ioutil.ReadFile(c.Mailer.EmailTemplate) cmd.FailOnError(err, fmt.Sprintf("Could not read email template file [%s]", c.Mailer.EmailTemplate)) tmpl, err := template.New("expiry-email").Parse(string(emailTmpl)) cmd.FailOnError(err, "Could not parse email template") fromAddress, err := netmail.ParseAddress(c.Mailer.From) cmd.FailOnError(err, fmt.Sprintf("Could not parse from address: %s", c.Mailer.From)) smtpPassword, err := c.Mailer.PasswordConfig.Pass() cmd.FailOnError(err, "Failed to load SMTP password") mailClient := bmail.New( c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, smtpPassword, *fromAddress, logger, scope, *reconnBase, *reconnMax) nagCheckInterval := defaultNagCheckInterval if s := c.Mailer.NagCheckInterval; s != "" { nagCheckInterval, err = time.ParseDuration(s) if err != nil { logger.AuditErr(fmt.Sprintf("Failed to parse NagCheckInterval string %q: %s", s, err)) return } } var nags durationSlice for _, nagDuration := range c.Mailer.NagTimes { dur, err := time.ParseDuration(nagDuration) if err != nil { logger.AuditErr(fmt.Sprintf("Failed to parse nag duration string [%s]: %s", nagDuration, err)) return } nags = append(nags, dur+nagCheckInterval) } // Make sure durations are sorted in increasing order sort.Sort(nags) m := mailer{ stats: scope, subject: c.Mailer.Subject, log: logger, dbMap: dbMap, rs: sac, mailer: mailClient, emailTemplate: tmpl, nagTimes: nags, limit: c.Mailer.CertLimit, clk: cmd.Clock(), } go cmd.DebugServer(c.Mailer.DebugAddr) err = m.findExpiringCertificates() cmd.FailOnError(err, "expiration-mailer has failed") }
func main() { from := flag.String("from", "", "From header for emails. Must be a bare email address.") subject := flag.String("subject", "", "Subject of emails") toFile := flag.String("toFile", "", "File containing a JSON array of registration IDs to send to.") bodyFile := flag.String("body", "", "File containing the email body in plain text format.") dryRun := flag.Bool("dryRun", true, "Whether to do a dry run.") sleep := flag.Duration("sleep", 60*time.Second, "How long to sleep between emails.") start := flag.Int("start", 0, "Line of input file to start from.") end := flag.Int("end", 99999999, "Line of input file to end before.") reconnBase := flag.Duration("reconnectBase", 1*time.Second, "Base sleep duration between reconnect attempts") reconnMax := flag.Duration("reconnectMax", 5*60*time.Second, "Max sleep duration between reconnect attempts after exponential backoff") type config struct { NotifyMailer struct { cmd.DBConfig cmd.PasswordConfig cmd.SMTPConfig } Statsd cmd.StatsdConfig Syslog cmd.SyslogConfig } configFile := flag.String("config", "", "File containing a JSON config.") flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n\n", usageIntro) fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() if *from == "" || *subject == "" || *bodyFile == "" || *configFile == "" { flag.Usage() os.Exit(1) } configData, err := ioutil.ReadFile(*configFile) cmd.FailOnError(err, fmt.Sprintf("Reading %q", *configFile)) var cfg config err = json.Unmarshal(configData, &cfg) cmd.FailOnError(err, "Unmarshaling config") stats, log := cmd.StatsAndLogging(cfg.Statsd, cfg.Syslog) scope := metrics.NewStatsdScope(stats, "NotificationMailer") defer log.AuditPanic() dbURL, err := cfg.NotifyMailer.DBConfig.URL() cmd.FailOnError(err, "Couldn't load DB URL") dbMap, err := sa.NewDbMap(dbURL, 10) cmd.FailOnError(err, "Could not connect to database") go sa.ReportDbConnCount(dbMap, scope) // Load email body body, err := ioutil.ReadFile(*bodyFile) cmd.FailOnError(err, fmt.Sprintf("Reading %q", *bodyFile)) address, err := mail.ParseAddress(*from) cmd.FailOnError(err, fmt.Sprintf("Parsing %q", *from)) toBody, err := ioutil.ReadFile(*toFile) cmd.FailOnError(err, fmt.Sprintf("Reading %q", *toFile)) checkpointRange := interval{ start: *start, end: *end, } var mailClient bmail.Mailer if *dryRun { mailClient = bmail.NewDryRun(*address, log) } else { smtpPassword, err := cfg.NotifyMailer.PasswordConfig.Pass() cmd.FailOnError(err, "Failed to load SMTP password") mailClient = bmail.New( cfg.NotifyMailer.Server, cfg.NotifyMailer.Port, cfg.NotifyMailer.Username, smtpPassword, *address, log, scope, *reconnBase, *reconnMax) } m := mailer{ clk: cmd.Clock(), log: log, dbMap: dbMap, mailer: mailClient, subject: *subject, destinations: toBody, emailTemplate: string(body), checkpoint: checkpointRange, sleepInterval: *sleep, } err = m.run() cmd.FailOnError(err, "mailer.send returned error") }