Files
archivmail/config/config.go
T
sysops 46d7bfe608 fix(security): Kritische Sicherheitslücken beheben (SEC-01/02/03/05/08/17/22/26/28)
- SEC-01: Privilege Escalation verhindert — Rollenhierarchie in Create/Update/DeleteUser
- SEC-02: Tenant-Isolation in Update/DeleteUser — domain_admin nur eigene Nutzer
- SEC-03: IMAP/POP3 Owner-Check via auth.HasRole statt direktem String-Vergleich
- SEC-05: Export PDF/ZIP prüft Tenant-Zugehörigkeit vor Dateiausgabe
- SEC-08: HKDF-SHA256 trennt JWT-Secret von AES-Key (archivmail-jwt-v1 / archivmail-aes-v1)
- SEC-17: handleSecurityFix erfordert requireRole(superadmin)
- SEC-22: Mail-ID Regex [0-9a-f]{64} in allen Handlern (Path-Traversal-Schutz)
- SEC-26: SMTP Fail-Closed — leere AllowedIPs blockiert alles statt zu erlauben
- SEC-28: handleGetRaw — Parse-Fehler bricht ab statt Fallthrough zu Dateizugriff

BREAKING: IMAP/POP3/LDAP-Passwörter müssen nach Deploy einmalig neu eingegeben
werden (neuer AES-Key). JWT-Sessions laufen ab (einmaliges Re-Login nötig).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:05:47 +01:00

106 lines
3.1 KiB
Go

package config
import (
"fmt"
"os"
"gopkg.in/yaml.v3"
)
// APIConfig holds configuration for the HTTP API server.
type APIConfig struct {
Bind string `yaml:"bind"`
Secret string `yaml:"secret"`
}
// Config is the full application configuration loaded from YAML.
type Config struct {
Server ServerConfig `yaml:"server"`
Storage StorageConfig `yaml:"storage"`
Database DatabaseConfig `yaml:"database"`
SMTP SMTPConfig `yaml:"smtp"`
API APIConfig `yaml:"api"`
Index IndexConfig `yaml:"index"`
Audit AuditConfig `yaml:"audit"`
Logging LoggingConfig `yaml:"logging"`
}
// ServerConfig holds port settings for the main services.
type ServerConfig struct {
APIPort int `yaml:"api_port"`
SMTPPort int `yaml:"smtp_port"`
}
// StorageConfig holds file system paths for email storage.
type StorageConfig struct {
StorePath string `yaml:"store_path"`
AStorePath string `yaml:"astore_path"`
XapianPath string `yaml:"xapian_path"`
Keyfile string `yaml:"keyfile"`
}
// DatabaseConfig holds PostgreSQL connection settings.
type DatabaseConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Name string `yaml:"name"`
User string `yaml:"user"`
Password string `yaml:"password"`
SSLMode string `yaml:"sslmode"`
}
// DSN builds a PostgreSQL connection string from the config fields.
func (d DatabaseConfig) DSN() string {
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
d.User, d.Password, d.Host, d.Port, d.Name, d.SSLMode)
}
// SMTPConfig holds settings for the embedded SMTP server.
// SEC-26: AllowedIPs uses fail-closed logic. An empty list means NO IP is
// allowed to connect. To accept from any IP, explicitly set:
// allowed_ips: ["0.0.0.0/0", "::/0"]
type SMTPConfig struct {
Enabled bool `yaml:"enabled"`
Bind string `yaml:"bind"`
Domain string `yaml:"domain"`
TLSCert string `yaml:"tls_cert"`
TLSKey string `yaml:"tls_key"`
MaxSizeMB int `yaml:"max_size_mb"`
AllowedIPs []string `yaml:"allowed_ips"`
TenantRouting string `yaml:"tenant_routing"` // "domain" or "default"
DefaultTenantID int64 `yaml:"default_tenant_id"` // used when routing is "default" or domain lookup fails
}
// IndexConfig holds full-text index settings.
type IndexConfig struct {
Path string `yaml:"path"`
Backend string `yaml:"backend"`
BatchSize int `yaml:"batch_size"`
AsyncQueueSize int `yaml:"async_queue_size"`
}
// AuditConfig holds audit log settings.
type AuditConfig struct {
LogPath string `yaml:"log_path"`
RetentionDays int `yaml:"retention_days"`
}
// LoggingConfig holds application logging settings.
type LoggingConfig struct {
Path string `yaml:"path"`
Level string `yaml:"level"`
}
// Load reads a YAML config file from path and returns a parsed Config.
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}