Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func init() {
migrateUpCmd.Flags().StringVarP(&dsn, "database-path", "d", "./notary.db", "A DSN for connecting to the database. Also accepts a path to a file, and will assume that the database is SQLite.")
migrateDownCmd.Flags().StringVarP(&dsn, "database-path", "d", "./notary.db", "A DSN for connecting to the database. Also accepts a path to a file, and will assume that the database is SQLite.")
migrateStatusCmd.Flags().StringVarP(&dsn, "database-path", "d", "./notary.db", "A DSN for connecting to the database. Also accepts a path to a file, and will assume that the database is SQLite.")

if err := migrateUpCmd.MarkFlagRequired("database-path"); err != nil {
log.Fatalf("Error marking database-path flag as required: %v", err)
}
Expand Down
13 changes: 7 additions & 6 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ https://canonical-notary.readthedocs-hosted.com/en/latest/reference/config_file/
if err != nil {
log.Fatalf("couldn't create app context: %s", err)
}
l := appContext.Logger
l := appContext.SystemLogger

// Initialize the database connection
db, err := db.NewDatabase(&db.DatabaseOpts{
DatabasePath: appContext.DBPath,
DatabasePath: appContext.DBPath,
ApplyMigrations: appContext.ApplyMigrations,
Backend: appContext.EncryptionBackend,
Logger: appContext.Logger,
Backend: appContext.EncryptionBackend,
Logger: appContext.SystemLogger,
})
if err != nil {
l.Fatal("couldn't initialize database", zap.Error(err))
Expand All @@ -49,7 +49,8 @@ https://canonical-notary.readthedocs-hosted.com/en/latest/reference/config_file/
Database: db,
ExternalHostname: appContext.ExternalHostname,
EnablePebbleNotifications: appContext.PebbleNotificationsEnabled,
Logger: appContext.Logger,
SystemLogger: appContext.SystemLogger,
AuditLogger: appContext.AuditLogger,
PublicConfig: appContext.PublicConfig,
})
if err != nil {
Expand Down Expand Up @@ -82,7 +83,7 @@ func init() {

startCmd.Flags().StringVarP(&configFilePath, "config", "c", "", "path to the configuration file")
startCmd.Flags().BoolP("migrate-database", "m", false, "automatically apply database migrations if needed (use with caution)")

err := startCmd.MarkFlagRequired("config")
if err != nil {
log.Fatalf("couldn't mark flag required: %s", err)
Expand Down
60 changes: 51 additions & 9 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,20 @@ func CreateAppContext(cmdFlags *pflag.FlagSet, configFilePath string) (*NotaryAp
return nil, err
}

// initialize logger
logger, err := initializeLogger(cfg.Sub("logging"))
// initialize system logger
systemLogger, err := initializeLogger(cfg.Sub("logging.system"))
if err != nil {
return nil, fmt.Errorf("couldn't initialize logger: %w", err)
return nil, fmt.Errorf("couldn't initialize system logger: %w", err)
}

// initialize audit logger
// Audit logs are always at INFO level
auditLogger, err := initializeAuditLogger(cfg.Sub("logging.audit"))
if err != nil {
return nil, fmt.Errorf("couldn't initialize audit logger: %w", err)
}
// initialize encryption backend
backendType, backend, err := initializeEncryptionBackend(cfg.Sub("encryption_backend"), logger)
backendType, backend, err := initializeEncryptionBackend(cfg.Sub("encryption_backend"), systemLogger)
if err != nil {
return nil, fmt.Errorf("couldn't initialize encryption backend: %w", err)
}
Expand All @@ -55,7 +62,8 @@ func CreateAppContext(cmdFlags *pflag.FlagSet, configFilePath string) (*NotaryAp

appContext.TLSCertificate = cert
appContext.TLSPrivateKey = key
appContext.Logger = logger
appContext.SystemLogger = systemLogger
appContext.AuditLogger = auditLogger
appContext.EncryptionBackend = backend
appContext.EncryptionBackendType = backendType
appContext.PublicConfig = &PublicConfigData{
Expand Down Expand Up @@ -85,6 +93,7 @@ func initializeServerConfig(cmdFlags *pflag.FlagSet, configFilePath string) (*vi
v.SetDefault("external_hostname", "localhost")
v.SetDefault("logging.system.level", "debug")
v.SetDefault("logging.system.output", "stdout")
v.SetDefault("logging.audit.output", "stdout")

if configFilePath == "" {
return nil, errors.New("config file path not provided")
Expand Down Expand Up @@ -209,17 +218,50 @@ func initializeEncryptionBackend(encryptionCfg *viper.Viper, logger *zap.Logger)
}
}

// initializeLogger creates and configures a logger based on the configuration.
// initializeLogger creates and configures a logger based on the provided configuration.
// cfg is the logger configuration subsection (e.g., logging.system).
// output can be "stdout", "stderr", or a file path.
func initializeLogger(cfg *viper.Viper) (*zap.Logger, error) {
if cfg == nil {
return nil, fmt.Errorf("logger configuration is not defined")
}

zapConfig := zap.NewProductionConfig()

logLevel, err := zapcore.ParseLevel(cfg.GetString("system.level"))
logLevel, err := zapcore.ParseLevel(cfg.GetString("level"))
if err != nil {
return nil, fmt.Errorf("invalid log level: %w", err)
}

zapConfig.OutputPaths = []string{cfg.GetString("system.output")}
zapConfig.Level.SetLevel(logLevel)

output := cfg.GetString("output")
zapConfig.OutputPaths = []string{output}

zapConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

logger, err := zapConfig.Build()
if err != nil {
return nil, err
}

return logger, nil
}

// initializeAuditLogger creates an audit logger that always logs at INFO level, regardless of config.
// cfg is the logger configuration subsection (e.g., logging.audit).
// output can be "stdout", "stderr", or a file path.
func initializeAuditLogger(cfg *viper.Viper) (*zap.Logger, error) {
if cfg == nil {
return nil, fmt.Errorf("logger configuration is not defined")
}

zapConfig := zap.NewProductionConfig()
// Force INFO level for audit logs
zapConfig.Level.SetLevel(zapcore.InfoLevel)

output := cfg.GetString("output")
zapConfig.OutputPaths = []string{output}

zapConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

logger, err := zapConfig.Build()
Expand Down
8 changes: 5 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func TestValidConfig(t *testing.T) {
DBPath: "./notary.db",
Port: 8000,
PebbleNotificationsEnabled: false,
Logger: nil,
SystemLogger: nil,
AuditLogger: nil,
EncryptionBackend: encryption_backend.NoEncryptionBackend{},
EncryptionBackendType: config.EncryptionBackendTypeNone,
}}, // This case tests the expected default values for missing fields are filled correctly
Expand All @@ -51,7 +52,8 @@ func TestValidConfig(t *testing.T) {
DBPath: "./notary.db",
Port: 8000,
PebbleNotificationsEnabled: false,
Logger: nil,
SystemLogger: nil,
AuditLogger: nil,
EncryptionBackend: encryption_backend.NoEncryptionBackend{},
EncryptionBackendType: config.EncryptionBackendTypeNone,
}}, // This case tests that the variables from the yaml are correctly copied to the final config
Expand All @@ -67,7 +69,7 @@ func TestValidConfig(t *testing.T) {
t.Errorf("ValidateConfig(%q) = %v, want nil", "config.yaml", err)
return
}
if !cmp.Equal(gotCfg, tc.wantCfg, cmpopts.IgnoreFields(config.NotaryAppContext{}, "Logger")) {
if !cmp.Equal(gotCfg, tc.wantCfg, cmpopts.IgnoreFields(config.NotaryAppContext{}, "SystemLogger", "AuditLogger")) {
t.Errorf("ValidateConfig returned unexpected diff (-want+got):\n%v", cmp.Diff(tc.wantCfg, gotCfg))
}
})
Expand Down
74 changes: 3 additions & 71 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,73 +13,6 @@ const (
EncryptionBackendTypeNone = "none"
)

// VaultBackendConfigYaml BackendConfig for Vault-specific fields.
type VaultBackendConfigYaml struct {
Endpoint string `yaml:"endpoint"`
Mount string `yaml:"mount"`
KeyName string `yaml:"key_name"`
Token string `yaml:"token"`
AppRoleID string `yaml:"approle_role_id"`
AppRoleSecretID string `yaml:"approle_secret_id"`
TlsCaCertificate string `yaml:"tls_ca_cert,omitempty"` // Optional path to a CA file for Vault TLS verification
TlsSkipVerify bool `yaml:"tls_skip_verify,omitempty"` // Optional flag to skip TLS verification
}

// PKCS11BackendConfigYaml BackendConfig for PKCS11-specific fields.
type PKCS11BackendConfigYaml struct {
LibPath string `yaml:"lib_path"`
KeyID uint16 `yaml:"aes_encryption_key_id"`
Pin string `yaml:"pin"`
}

// NamedBackendConfigYaml represents a single named backend configuration
type NamedBackendConfigYaml struct {
PKCS11 *PKCS11BackendConfigYaml `yaml:"pkcs11,omitempty"`
Vault *VaultBackendConfigYaml `yaml:"vault,omitempty"`
}

type EncryptionBackendConfigYaml map[string]NamedBackendConfigYaml

type SystemLoggingConfigYaml struct {
Level string `yaml:"level"`
Output string `yaml:"output"`
}

type LoggingConfigYaml struct {
System SystemLoggingConfigYaml `yaml:"system"`
}

type ConfigYAML struct {
KeyPath string `yaml:"key_path"`
CertPath string `yaml:"cert_path"`
ExternalHostname string `yaml:"external_hostname"`
DBPath string `yaml:"db_path"`
Port int `yaml:"port"`
PebbleNotifications bool `yaml:"pebble_notifications"`
Logging LoggingConfigYaml `yaml:"logging"`
EncryptionBackend EncryptionBackendConfigYaml `yaml:"encryption_backend"`
}

type LoggingLevel string

const (
Debug LoggingLevel = "debug"
Info LoggingLevel = "info"
Warn LoggingLevel = "warn"
Error LoggingLevel = "error"
Fatal LoggingLevel = "fatal"
Panic LoggingLevel = "panic"
)

type SystemLoggingOptions struct {
Level LoggingLevel
Output string
}

type LoggerOptions struct {
System SystemLoggingOptions
}

// PublicConfigData contains non-sensitive configuration fields that are safe to expose
type PublicConfigData struct {
Port int
Expand All @@ -90,8 +23,6 @@ type PublicConfigData struct {
}

type NotaryAppContext struct {
// The YAML configuration file content
Config *ConfigYAML
PublicConfig *PublicConfigData

// TLSPrivateKey and Certificate for the webserver and the listener port
Expand All @@ -111,8 +42,9 @@ type NotaryAppContext struct {
// Send pebble notifications if enabled. Read more at github.com/canonical/pebble
PebbleNotificationsEnabled bool

// Options for the logger
Logger *zap.Logger
// Options for the loggers
SystemLogger *zap.Logger
AuditLogger *zap.Logger

// Encryption backend to be used for encrypting and decrypting sensitive data
EncryptionBackendType
Expand Down
2 changes: 1 addition & 1 deletion internal/db/db_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func NewDatabase(dbOpts *DatabaseOpts) (*Database, error) {
if err != nil {
return nil, err
}
if version < 1 {
if version < 1 {
if dbOpts.ApplyMigrations {
goose.SetBaseFS(migrations.EmbedMigrations)
if err := goose.Up(sqlConnection, ".", goose.WithNoColor(true)); err != nil {
Expand Down
12 changes: 6 additions & 6 deletions internal/db/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import (
)

type DatabaseOpts struct {
DatabasePath string
DatabasePath string
ApplyMigrations bool
Backend encryption_backend.EncryptionBackend
Logger *zap.Logger
Backend encryption_backend.EncryptionBackend
Logger *zap.Logger
}

// Database is the object used to communicate with the established repository.
type Database struct {
Conn *sqlair.DB
stmts *Statements
Conn *sqlair.DB
stmts *Statements

EncryptionKey []byte
JWTSecret []byte
JWTSecret []byte
}

const CAMaxExpiryYears = 1
Expand Down
Loading
Loading