Merge branch 'next' of github.com:OliveTin/OliveTin into next

This commit is contained in:
jamesread 2026-02-26 17:42:30 +00:00
commit 5cbcf29704
3 changed files with 70 additions and 1 deletions

View File

@ -107,6 +107,15 @@ type PrometheusConfig struct {
DefaultGoMetrics bool `koanf:"defaultGoMetrics"` DefaultGoMetrics bool `koanf:"defaultGoMetrics"`
} }
// SecurityConfig allows users to fine tune the security related HTTP headers.
type SecurityConfig struct {
HeaderContentSecurityPolicy bool `koanf:"headerContentSecurityPolicy"`
ContentSecurityPolicy string `koanf:"contentSecurityPolicy"`
HeaderXContentTypeOptions bool `koanf:"headerXContentTypeOptions"`
HeaderXFrameOptions bool `koanf:"headerXFrameOptions"`
XFrameOptions string `koanf:"xFrameOptions"`
}
// Config is the global config used through the whole app. // Config is the global config used through the whole app.
type Config struct { type Config struct {
UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"` UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"`
@ -160,6 +169,7 @@ type Config struct {
InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"` InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"`
InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"` InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"`
Prometheus PrometheusConfig `koanf:"prometheus"` Prometheus PrometheusConfig `koanf:"prometheus"`
Security SecurityConfig `koanf:"security"`
SaveLogs SaveLogsConfig `koanf:"saveLogs"` SaveLogs SaveLogsConfig `koanf:"saveLogs"`
DefaultIconForActions string `koanf:"defaultIconForActions"` DefaultIconForActions string `koanf:"defaultIconForActions"`
DefaultIconForDirectories string `koanf:"defaultIconForDirectories"` DefaultIconForDirectories string `koanf:"defaultIconForDirectories"`
@ -268,6 +278,11 @@ func DefaultConfigWithBasePort(basePort int) *Config {
config.InsecureAllowDumpJwtClaims = false config.InsecureAllowDumpJwtClaims = false
config.Prometheus.Enabled = false config.Prometheus.Enabled = false
config.Prometheus.DefaultGoMetrics = false config.Prometheus.DefaultGoMetrics = false
config.Security.HeaderContentSecurityPolicy = true
config.Security.ContentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; base-uri 'self'"
config.Security.HeaderXContentTypeOptions = true
config.Security.HeaderXFrameOptions = true
config.Security.XFrameOptions = "DENY"
config.DefaultIconForActions = "😀" config.DefaultIconForActions = "😀"
config.DefaultIconForDirectories = "&#128193" config.DefaultIconForDirectories = "&#128193"
config.DefaultIconForBack = "«" config.DefaultIconForBack = "«"

View File

@ -16,6 +16,7 @@ func (cfg *Config) Sanitize() {
cfg.sanitizeAuthRequireGuestsToLogin() cfg.sanitizeAuthRequireGuestsToLogin()
cfg.sanitizeLogHistoryPageSize() cfg.sanitizeLogHistoryPageSize()
cfg.sanitizeLocalUserPasswords() cfg.sanitizeLocalUserPasswords()
cfg.sanitizeSecurityHeaders()
// log.Infof("cfg %p", cfg) // log.Infof("cfg %p", cfg)
@ -183,6 +184,25 @@ func (cfg *Config) sanitizeLocalUserPasswords() {
} }
} }
func (cfg *Config) sanitizeSecurityHeaders() {
cfg.sanitizeSecurityHeadersCSP()
cfg.sanitizeSecurityHeadersXFrameOptions()
}
func (cfg *Config) sanitizeSecurityHeadersCSP() {
if !cfg.Security.HeaderContentSecurityPolicy || cfg.Security.ContentSecurityPolicy != "" {
return
}
cfg.Security.ContentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'; base-uri 'self'"
}
func (cfg *Config) sanitizeSecurityHeadersXFrameOptions() {
if !cfg.Security.HeaderXFrameOptions || cfg.Security.XFrameOptions != "" {
return
}
cfg.Security.XFrameOptions = "DENY"
}
// parsePasswordTemplate expands {{ .Env.VAR }} in local user password fields using the process environment. // parsePasswordTemplate expands {{ .Env.VAR }} in local user password fields using the process environment.
func parsePasswordTemplate(source string) string { func parsePasswordTemplate(source string) string {
t, err := template.New("password").Option("missingkey=error").Parse(source) t, err := template.New("password").Option("missingkey=error").Parse(source)

View File

@ -23,6 +23,40 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func applySecurityHeaders(cfg *config.Config, w http.ResponseWriter) {
applyCSP(cfg, w)
applyXContentTypeOptions(cfg, w)
applyXFrameOptions(cfg, w)
}
func applyCSP(cfg *config.Config, w http.ResponseWriter) {
if !cfg.Security.HeaderContentSecurityPolicy || cfg.Security.ContentSecurityPolicy == "" {
return
}
w.Header().Set("Content-Security-Policy", cfg.Security.ContentSecurityPolicy)
}
func applyXContentTypeOptions(cfg *config.Config, w http.ResponseWriter) {
if !cfg.Security.HeaderXContentTypeOptions {
return
}
w.Header().Set("X-Content-Type-Options", "nosniff")
}
func applyXFrameOptions(cfg *config.Config, w http.ResponseWriter) {
if !cfg.Security.HeaderXFrameOptions || cfg.Security.XFrameOptions == "" {
return
}
w.Header().Set("X-Frame-Options", cfg.Security.XFrameOptions)
}
func securityHeadersMiddleware(cfg *config.Config, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
applySecurityHeaders(cfg, w)
next.ServeHTTP(w, r)
})
}
func logDebugRequest(cfg *config.Config, source string, r *http.Request) { func logDebugRequest(cfg *config.Config, source string, r *http.Request) {
if cfg.LogDebugOptions.SingleFrontendRequests { if cfg.LogDebugOptions.SingleFrontendRequests {
log.Debugf("SingleFrontend HTTP Req URL %v: %q", source, r.URL) log.Debugf("SingleFrontend HTTP Req URL %v: %q", source, r.URL)
@ -96,7 +130,7 @@ func StartFrontendMux(cfg *config.Config, ex *executor.Executor) {
srv := &http.Server{ srv := &http.Server{
Addr: cfg.ListenAddressSingleHTTPFrontend, Addr: cfg.ListenAddressSingleHTTPFrontend,
Handler: mux, Handler: securityHeadersMiddleware(cfg, mux),
} }
log.Fatal(srv.ListenAndServe()) log.Fatal(srv.ListenAndServe())