diff --git a/cmd/signerclient/main.go b/cmd/signerclient/main.go index b06bc18..939d444 100644 --- a/cmd/signerclient/main.go +++ b/cmd/signerclient/main.go @@ -20,9 +20,12 @@ package main import ( "context" "flag" + "fmt" "os" + "strings" "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" "git.cacert.org/cacert-gosigner/pkg/protocol" "git.cacert.org/cacert-gosignerclient/internal/client" @@ -42,9 +45,9 @@ const ( func main() { var ( - showVersion, verbose bool - configFile, logLevel string - logger *logrus.Logger + showVersion, verbose, generateConfig bool + configFile, logLevel string + logger *logrus.Logger ) logger = logrus.New() @@ -56,6 +59,12 @@ func main() { flag.StringVar(&configFile, "config", defaultConfigFile, "signer client configuration file") flag.BoolVar(&showVersion, "version", false, "show version") flag.BoolVar(&verbose, "verbose", false, "verbose output") + flag.BoolVar( + &generateConfig, + "generate-config", + false, + "generate a configuration file with default values instead of running the client", + ) flag.StringVar(&logLevel, "loglevel", "INFO", "log level") flag.Parse() @@ -71,9 +80,45 @@ func main() { logger.SetLevel(parsedLevel) + if generateConfig { + if err = generateDefaultConfig(); err != nil { + logger.WithError(err).Fatal("could not generate default configuration") + } + + return + } + + if err := startClient(configFile, logger); err != nil { + logger.WithError(err).Fatal("client failure") + } +} + +func generateDefaultConfig() error { + const defaultBaseConfiguration = `--- +serial: + device: /dev/ttyUSB0 + baud: 112500 +` + + cfg, err := config.LoadConfiguration(strings.NewReader( + defaultBaseConfiguration)) + if err != nil { + return fmt.Errorf("could not load empty configuration: %w", err) + } + + enc := yaml.NewEncoder(os.Stdout) + + if err = enc.Encode(cfg); err != nil { + return fmt.Errorf("could not encode: %w", err) + } + + return nil +} + +func startClient(configFile string, logger *logrus.Logger) error { clientConfig, err := config.New(configFile) if err != nil { - logger.WithError(err).Fatal("could not configure client") + return fmt.Errorf("could not configure client: %w", err) } commands := make(chan *protocol.Command) @@ -81,12 +126,12 @@ func main() { clientHandler, err := handler.New(clientConfig, logger, commands, callbacks) if err != nil { - logger.WithError(err).Fatal("could not setup client handler") + return fmt.Errorf("could not setup client handler: %w", err) } signerClient, err := client.New(clientConfig, logger, clientHandler, commands, callbacks) if err != nil { - logger.WithError(err).Fatal("could not setup client") + return fmt.Errorf("could not setup client: %w", err) } defer func() { _ = signerClient.Close() }() @@ -94,6 +139,8 @@ func main() { logger.Info("setup complete, starting client operation") if err = signerClient.Run(context.Background()); err != nil { - logger.WithError(err).Fatal("error in client") + return fmt.Errorf("error in client: %w", err) } + + return nil } diff --git a/internal/client/client.go b/internal/client/client.go index 3a2aa50..3a5ba5b 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -432,24 +432,16 @@ func (c *Client) updateCRL(d *messages.FetchCRLResponse) ([]*protocol.Command, e } func (c *Client) buildCRLFileName(caName string) string { - return path.Join(c.config.PublicDataDirectory, fmt.Sprintf("%s.crl", caName)) + return path.Join(c.config.PublicCRLDirectory, fmt.Sprintf("%s.crl", caName)) } func (c *Client) buildCertificateFileName(caName string, certFormat string) string { - return path.Join(c.config.PublicDataDirectory, fmt.Sprintf("%s.%s", caName, certFormat)) -} - -func (c *Client) ensurePublicDataDirectory() error { - if err := os.MkdirAll(c.config.PublicDataDirectory, worldReadableDirPerm); err != nil { - return fmt.Errorf("could not create public CA data directory %s: %w", c.config.PublicDataDirectory, err) - } - - return nil + return path.Join(c.config.PublicCRLDirectory, fmt.Sprintf("%s.%s", caName, certFormat)) } func (c *Client) writeCertificate(caName string, derBytes []byte) error { - if err := c.ensurePublicDataDirectory(); err != nil { - return err + if err := os.MkdirAll(c.config.PublicCRLDirectory, worldReadableDirPerm); err != nil { + return fmt.Errorf("could not create public CA data directory %s: %w", c.config.PublicCRLDirectory, err) } if err := os.WriteFile( @@ -470,8 +462,8 @@ func (c *Client) writeCertificate(caName string, derBytes []byte) error { } func (c *Client) writeCRL(caName string, crlBytes []byte) error { - if err := c.ensurePublicDataDirectory(); err != nil { - return err + if err := os.MkdirAll(c.config.PublicCRLDirectory, worldReadableDirPerm); err != nil { + return fmt.Errorf("could not create public CA data directory %s: %w", c.config.PublicCRLDirectory, err) } if err := os.WriteFile(c.buildCRLFileName(caName), crlBytes, worldReadableFilePerm); err != nil { diff --git a/internal/config/config.go b/internal/config/config.go index 44eccfd..eceb160 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -52,26 +52,28 @@ type Serial struct { } type ClientConfig struct { - Serial Serial `yaml:"serial"` - HealthInterval time.Duration `yaml:"health-interval"` - HealthStart time.Duration `yaml:"health-start"` - FetchCRLStart time.Duration `yaml:"fetch-crl-start"` - FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"` - ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"` - ResponseDataTimeout time.Duration `yaml:"response-data-timeout"` - PublicDataDirectory string `yaml:"public-data-directory"` + Serial Serial `yaml:"serial"` + HealthInterval time.Duration `yaml:"health-interval"` + HealthStart time.Duration `yaml:"health-start"` + FetchCRLStart time.Duration `yaml:"fetch-crl-start"` + FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"` + ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"` + ResponseDataTimeout time.Duration `yaml:"response-data-timeout"` + PublicCRLDirectory string `yaml:"public-crl-directory"` + PublicCertificateDirectory string `yaml:"public-certificate-directory"` } func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error { data := struct { - Serial Serial `yaml:"serial"` - HealthInterval time.Duration `yaml:"health-interval"` - HealthStart time.Duration `yaml:"health-start"` - FetchCRLStart time.Duration `yaml:"fetch-crl-start"` - FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"` - ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"` - ResponseDataTimeout time.Duration `yaml:"response-data-timeout"` - PublicDataDirectory string `yaml:"public-data-directory"` + Serial Serial `yaml:"serial"` + HealthInterval time.Duration `yaml:"health-interval"` + HealthStart time.Duration `yaml:"health-start"` + FetchCRLStart time.Duration `yaml:"fetch-crl-start"` + FetchCRLInterval time.Duration `yaml:"fetch-crl-interval"` + ResponseAnnounceTimeout time.Duration `yaml:"response-announce-timeout"` + ResponseDataTimeout time.Duration `yaml:"response-data-timeout"` + PublicCRLDirectory string `yaml:"public-crl-directory"` + PublicCertificateDirectory string `yaml:"public-certificate-directory"` }{} err := n.Decode(&data) @@ -129,11 +131,17 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error { c.ResponseDataTimeout = data.ResponseDataTimeout - if data.PublicDataDirectory == "" { - data.PublicDataDirectory = defaultFilesDirectory + if data.PublicCRLDirectory == "" { + data.PublicCRLDirectory = defaultFilesDirectory } - c.PublicDataDirectory = data.PublicDataDirectory + c.PublicCRLDirectory = data.PublicCRLDirectory + + if data.PublicCertificateDirectory == "" { + data.PublicCertificateDirectory = defaultFilesDirectory + } + + c.PublicCertificateDirectory = data.PublicCRLDirectory return nil }