Auto-generate and persist session secret on first startup
- Generate random 32-byte session secret if not set via env var - Persist to $UPAAS_DATA_DIR/session.key for container restarts - Load existing secret from file on subsequent startups - Change container data directory to /var/lib/upaas
This commit is contained in:
parent
5fb0b111fc
commit
dce898bbdb
@ -29,9 +29,9 @@ WORKDIR /app
|
||||
COPY --from=builder /src/bin/upaasd /app/upaasd
|
||||
|
||||
# Create data directory
|
||||
RUN mkdir -p /data
|
||||
RUN mkdir -p /var/lib/upaas
|
||||
|
||||
ENV UPAAS_DATA_DIR=/data
|
||||
ENV UPAAS_DATA_DIR=/var/lib/upaas
|
||||
ENV UPAAS_PORT=8080
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
@ -170,10 +170,12 @@ Environment variables:
|
||||
docker run -d \
|
||||
-p 8080:8080 \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v upaas-data:/data \
|
||||
-v upaas-data:/var/lib/upaas \
|
||||
upaas
|
||||
```
|
||||
|
||||
Session secrets are automatically generated on first startup and persisted to `$UPAAS_DATA_DIR/session.key`.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@ -2,9 +2,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/fx"
|
||||
@ -16,6 +20,18 @@ import (
|
||||
// defaultPort is the default HTTP server port.
|
||||
const defaultPort = 8080
|
||||
|
||||
// sessionSecretFile is the filename for the persisted session secret.
|
||||
const sessionSecretFile = "session.key"
|
||||
|
||||
// sessionSecretBytes is the number of random bytes for session secret.
|
||||
const sessionSecretBytes = 32
|
||||
|
||||
// File permission constants.
|
||||
const (
|
||||
dirPermissions = 0o700
|
||||
filePermissions = 0o600
|
||||
)
|
||||
|
||||
// Params contains dependencies for Config.
|
||||
type Params struct {
|
||||
fx.In
|
||||
@ -112,19 +128,62 @@ func buildConfig(log *slog.Logger, params *Params) (*Config, error) {
|
||||
log: log,
|
||||
}
|
||||
|
||||
// Generate session secret if not set
|
||||
// Load or generate session secret
|
||||
if cfg.SessionSecret == "" {
|
||||
cfg.SessionSecret = "change-me-in-production-please"
|
||||
secret, err := loadOrCreateSessionSecret(log, cfg.DataDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize session secret: %w", err)
|
||||
}
|
||||
|
||||
log.Warn(
|
||||
"using default session secret, " +
|
||||
"set UPAAS_SESSION_SECRET in production",
|
||||
)
|
||||
cfg.SessionSecret = secret
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadOrCreateSessionSecret(log *slog.Logger, dataDir string) (string, error) {
|
||||
secretPath := filepath.Join(dataDir, sessionSecretFile)
|
||||
|
||||
// Try to read existing secret
|
||||
//nolint:gosec // secretPath is constructed from trusted config, not user input
|
||||
data, err := os.ReadFile(secretPath)
|
||||
if err == nil {
|
||||
log.Info("loaded session secret from file", "path", secretPath)
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("failed to read session secret file: %w", err)
|
||||
}
|
||||
|
||||
// Generate new secret
|
||||
secretBytes := make([]byte, sessionSecretBytes)
|
||||
|
||||
_, err = rand.Read(secretBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random secret: %w", err)
|
||||
}
|
||||
|
||||
secret := hex.EncodeToString(secretBytes)
|
||||
|
||||
// Ensure data directory exists
|
||||
err = os.MkdirAll(dataDir, dirPermissions)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create data directory: %w", err)
|
||||
}
|
||||
|
||||
// Write secret to file
|
||||
err = os.WriteFile(secretPath, []byte(secret), filePermissions)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to write session secret file: %w", err)
|
||||
}
|
||||
|
||||
log.Info("generated new session secret", "path", secretPath)
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func configureDebugLogging(cfg *Config, params Params) {
|
||||
// Enable debug logging if configured
|
||||
if cfg.Debug {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user