package main import ( "flag" "fmt" "log" "os" "time" "gopkg.in/yaml.v3" ) type Config struct { Listen []string `yaml:"listen"` Checkpoints string `yaml:"checkpoints"` Roots string `yaml:"roots"` Logs []Log `yaml:"logs"` } type Log struct { ShortName string `yaml:"shortname"` Inception string `yaml:"inception"` Period int `yaml:"period"` PoolSize int `yaml:"poolsize"` SubmissionPrefix string `yaml:"submissionprefix"` MonitoringPrefix string `yaml:"monitoringprefix"` CCadbRoots string `yaml:"ccadbroots"` ExtraRoots string `yaml:"extraroots"` Secret string `yaml:"secret"` Cache string `yaml:"cache"` LocalDirectory string `yaml:"localdirectory"` Listen string `yaml:"listen"` NotAfterStart time.Time `yaml:"notafterstart"` NotAfterLimit time.Time `yaml:"notafterlimit"` // Computed fields LogID string PublicKeyPEM string PublicKeyDERB64 string PublicKeyBase64 string } func main() { configFile := flag.String("c", "./tesseract-staging.yaml", "Path to the YAML configuration file") flag.Parse() args := flag.Args() if len(args) == 0 { showHelp() return } switch args[0] { case "gen-html": generateHTML(*configFile) case "gen-env": generateEnv(*configFile) case "gen-key": generateKeys(*configFile) case "gen-nginx": generateNginx(*configFile) case "gen-roots": generateRoots(args[1:]) default: fmt.Fprintf(os.Stderr, "Unknown command: %s\n", args[0]) showHelp() os.Exit(1) } } func loadConfig(yamlFile string) Config { data, err := os.ReadFile(yamlFile) if err != nil { log.Fatalf("Failed to read YAML file: %v", err) } var config Config err = yaml.Unmarshal(data, &config) if err != nil { log.Fatalf("Failed to parse YAML: %v", err) } // Set default listen port if not configured if len(config.Listen) == 0 { config.Listen = []string{":8080"} } // Set defaults for log entries for i := range config.Logs { if config.Logs[i].PoolSize == 0 { config.Logs[i].PoolSize = 750 } if config.Logs[i].Period == 0 { config.Logs[i].Period = 200 } } return config } func showHelp() { fmt.Printf("Usage: %s [options] \n\n", os.Args[0]) fmt.Printf("Options:\n") fmt.Printf(" -c Path to YAML configuration file (default: ./tesseract-staging.yaml)\n\n") fmt.Printf("Commands:\n") fmt.Printf(" gen-html Generate index.html and log.v3.json files in each log's localdirectory.\n") fmt.Printf(" Creates HTML pages with log information and CT log metadata JSON.\n") fmt.Printf(" Computes LOG_ID and public keys from private keys.\n\n") fmt.Printf(" gen-env Generate .env files and combined roots.pem in each log's localdirectory.\n") fmt.Printf(" Creates TESSERACT_ARGS environment variable with command line flags.\n") fmt.Printf(" Combines global roots and log-specific extraroots into roots.pem.\n\n") fmt.Printf(" gen-key Generate prime256v1 private keys for each log (only if they don't exist).\n") fmt.Printf(" Creates EC private key files at the path specified in log.secret.\n\n") fmt.Printf(" gen-nginx Generate nginx configuration files for each log's monitoring endpoint.\n") fmt.Printf(" Creates nginx-.conf files in each log's localdirectory.\n\n") fmt.Printf(" gen-roots Download root certificates from a Certificate Transparency log.\n") fmt.Printf(" Options: --source (default: https://rennet2027h2.log.ct.ipng.ch/)\n") fmt.Printf(" --output (default: roots.pem)\n\n") }