Release: v0.1.0-alpha
Release Docker Image / Build & Push Docker Image (release) Failing after 1m30s

This commit is contained in:
2026-04-05 16:56:16 +02:00
parent 23ff731579
commit fd13e67aef
89 changed files with 18786 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
// Keywarden - Centralized SSH Key Management and Deployment
// Copyright (C) 2026 Patrick Asmus (scriptos)
// SPDX-License-Identifier: AGPL-3.0-or-later
package main
import (
"net/http"
"os"
"git.techniverse.net/scriptos/keywarden/internal/audit"
"git.techniverse.net/scriptos/keywarden/internal/auth"
"git.techniverse.net/scriptos/keywarden/internal/config"
"git.techniverse.net/scriptos/keywarden/internal/cron"
"git.techniverse.net/scriptos/keywarden/internal/database"
"git.techniverse.net/scriptos/keywarden/internal/deploy"
"git.techniverse.net/scriptos/keywarden/internal/encryption"
"git.techniverse.net/scriptos/keywarden/internal/handlers"
"git.techniverse.net/scriptos/keywarden/internal/keys"
"git.techniverse.net/scriptos/keywarden/internal/logging"
"git.techniverse.net/scriptos/keywarden/internal/mail"
"git.techniverse.net/scriptos/keywarden/internal/security"
"git.techniverse.net/scriptos/keywarden/internal/servers"
"git.techniverse.net/scriptos/keywarden/web"
)
func main() {
// Load config first (needed for log level)
cfg := config.Load()
// Initialize structured logging
logging.Init(cfg.LogLevel)
logging.Info("🔑 Keywarden - Centralized SSH Key Management and Deployment")
logging.Info(" https://git.techniverse.net/scriptos/keywarden")
// Ensure data directories exist
for _, dir := range []string{cfg.DataDir, cfg.KeysDir, cfg.MasterDir} {
if err := os.MkdirAll(dir, 0700); err != nil {
logging.Fatal("Failed to create directory %s: %v", dir, err)
}
}
// Initialize database
db, err := database.New(cfg.DBPath)
if err != nil {
logging.Fatal("Failed to initialize database: %v", err)
}
defer db.Close()
logging.Info("Database initialized")
// Initialize services
encSvc := encryption.NewService(cfg.EncryptionKey)
authSvc := auth.NewService(db)
keysSvc := keys.NewService(db, encSvc)
serversSvc := servers.NewService(db)
deploySvc := deploy.NewService(db)
auditSvc := audit.NewService(db)
cronSvc := cron.NewService(db, deploySvc, keysSvc, serversSvc, auditSvc)
mailSvc := mail.NewService(cfg)
// Create default owner if no users exist (password is auto-generated)
adminUser := getEnv("KEYWARDEN_ADMIN_USER", "admin")
adminEmail := getEnv("KEYWARDEN_ADMIN_EMAIL", "admin@keywarden.local")
created, generatedPass, err := authSvc.EnsureAdmin(adminUser, adminEmail)
if err != nil {
logging.Fatal("Failed to create admin user: %v", err)
}
if created {
logging.Info("════════════════════════════════════════════════════════════")
logging.Info(" Initial owner account created")
logging.Info(" Username: %s", adminUser)
logging.Info(" Password: %s", generatedPass)
logging.Info(" Please change this password after first login!")
logging.Info("════════════════════════════════════════════════════════════")
}
// Ensure system master key exists (generated on first startup)
masterPub, err := keysSvc.EnsureSystemMasterKey()
if err != nil {
logging.Fatal("Failed to ensure system master key: %v", err)
}
logging.Info("System master key ready (deploy this public key to your servers)")
logging.Info("Master key: %s", masterPub)
// Initialize security subsystem (trusted proxy IP extraction)
security.Init(cfg.TrustedProxies)
if cfg.TrustedProxies != "" {
logging.Info("Trusted proxies: %s", cfg.TrustedProxies)
} else {
logging.Warn("KEYWARDEN_TRUSTED_PROXIES not set proxy headers (X-Forwarded-For) are trusted unconditionally. Configure trusted proxies for production use.")
}
if cfg.BaseURL != "" {
logging.Info("Base URL: %s", cfg.BaseURL)
}
// Setup HTTP handlers
handler := handlers.New(authSvc, keysSvc, serversSvc, deploySvc, auditSvc, cronSvc, mailSvc, db, web.TemplateFS, web.StaticFS, cfg.DataDir, cfg.SecureCookies, cfg.BaseURL)
mux := http.NewServeMux()
handler.RegisterRoutes(mux)
// Start session cleanup (removes expired sessions periodically)
handler.StartSessionCleanup()
// Build middleware chain (innermost → outermost)
var h http.Handler = mux
h = security.CSRFMiddleware(cfg.SecureCookies)(h)
h = security.SizeLimitMiddleware(cfg.MaxRequestSize)(h)
h = security.RateLimitMiddleware(cfg.RateLimitLogin)(h)
h = security.HeadersMiddleware()(h)
h = logging.RequestLogger(handler.GetUserName, security.ClientIP)(h)
logging.Info("Security hardening active: CSRF protection, security headers, rate limiting (%d/min login), request size limit (%d bytes)",
cfg.RateLimitLogin, cfg.MaxRequestSize)
if cfg.SecureCookies {
logging.Info("Secure cookies enabled (HTTPS mode)")
}
// Start cron scheduler
cronSvc.Start()
defer cronSvc.Stop()
// Start server
addr := ":" + cfg.Port
logging.Info("Server starting on http://0.0.0.0%s", addr)
if err := http.ListenAndServe(addr, h); err != nil {
logging.Fatal("Server failed: %v", err)
}
}
func getEnv(key, fallback string) string {
if val, ok := os.LookupEnv(key); ok {
return val
}
return fallback
}