diff --git a/config.template.yml b/config.template.yml index e94f8ae16..44820d343 100644 --- a/config.template.yml +++ b/config.template.yml @@ -71,6 +71,12 @@ server: ## The path to the DER base64/PEM format public certificate. certificate: "" + ## Server headers configuration/customization. + headers: + + ## The CSP Template. Read the docs. + csp_template: "" + ## ## Log Configuration ## diff --git a/docs/configuration/server.md b/docs/configuration/server.md index ba8cc8d5d..a724cc384 100644 --- a/docs/configuration/server.md +++ b/docs/configuration/server.md @@ -24,6 +24,8 @@ server: tls: key: "" certificate: "" + headers: + csp_template: "" ``` ## Options @@ -209,6 +211,25 @@ required: situational The path to the public certificate for TLS connections. Must be in DER base64/PEM format. + +### headers + +#### csp_template +
+type: string +{: .label .label-config .label-purple } +default: "" +{: .label .label-config .label-blue } +required: no +{: .label .label-config .label-green } +
+ +This customizes the value of the Content-Security-Policy header. It will replace all instances of `${NONCE}` with the +nonce value of the Authelia react bundle. This is an advanced option to customize and you should do sufficient research +about how browsers utilize and understand this header before attempting to customize it. + +For example, the default CSP template is `default-src 'self'; object-src 'none'; style-src 'self' 'nonce-${NONCE}'`. + ## Additional Notes ### Buffer Sizes diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index e94f8ae16..44820d343 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -71,6 +71,12 @@ server: ## The path to the DER base64/PEM format public certificate. certificate: "" + ## Server headers configuration/customization. + headers: + + ## The CSP Template. Read the docs. + csp_template: "" + ## ## Log Configuration ## diff --git a/internal/configuration/schema/server.go b/internal/configuration/schema/server.go index b05c448f8..e1d6f850d 100644 --- a/internal/configuration/schema/server.go +++ b/internal/configuration/schema/server.go @@ -12,7 +12,8 @@ type ServerConfiguration struct { EnableExpvars bool `koanf:"enable_endpoint_expvars"` DisableHealthcheck bool `koanf:"disable_healthcheck"` - TLS ServerTLSConfiguration `koanf:"tls"` + TLS ServerTLSConfiguration `koanf:"tls"` + Headers ServerHeadersConfiguration `koanf:"headers"` } // ServerTLSConfiguration represents the configuration of the http servers TLS options. @@ -21,6 +22,11 @@ type ServerTLSConfiguration struct { Key string `koanf:"key"` } +// ServerHeadersConfiguration represents the customization of the http server headers. +type ServerHeadersConfiguration struct { + CSPTemplate string `koanf:"csp_template"` +} + // DefaultServerConfiguration represents the default values of the ServerConfiguration. var DefaultServerConfiguration = ServerConfiguration{ Host: "0.0.0.0", diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index ab0a04f98..487c58e44 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -171,6 +171,7 @@ var ValidKeys = []string{ "server.disable_healthcheck", "server.tls.key", "server.tls.certificate", + "server.headers.csp_template", // TOTP Keys. "totp.issuer", diff --git a/internal/server/const.go b/internal/server/const.go index ed3484db9..610b2953e 100644 --- a/internal/server/const.go +++ b/internal/server/const.go @@ -23,3 +23,9 @@ X_AUTHELIA_HEALTHCHECK_HOST=%s X_AUTHELIA_HEALTHCHECK_PORT=%d X_AUTHELIA_HEALTHCHECK_PATH=%s ` + +const ( + cspDefaultTemplate = "default-src 'self'; object-src 'none'; style-src 'self' 'nonce-%s'" + cspDefaultDevTemplate = "default-src 'self' 'unsafe-eval'; object-src 'none'; style-src 'self' 'nonce-%s'" + cspNoncePlaceholder = "${NONCE}" +) diff --git a/internal/server/template.go b/internal/server/template.go index e3214aca1..c3bab9fe9 100644 --- a/internal/server/template.go +++ b/internal/server/template.go @@ -5,6 +5,7 @@ import ( "io" "os" "path/filepath" + "strings" "text/template" "github.com/authelia/authelia/v4/internal/logging" @@ -71,11 +72,13 @@ func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberM switch { case publicDir == swaggerAssets: - ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("base-uri 'self' ; default-src 'self' ; img-src 'self' https://validator.swagger.io data: ; object-src 'none' ; script-src 'self' 'unsafe-inline' 'nonce-%s' ; style-src 'self' 'nonce-%s'", nonce, nonce)) + ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("base-uri 'self'; default-src 'self'; img-src 'self' https://validator.swagger.io data:; object-src 'none'; script-src 'self' 'unsafe-inline' 'nonce-%s'; style-src 'self' 'nonce-%s'", nonce, nonce)) + case ctx.Configuration.Server.Headers.CSPTemplate != "": + ctx.Response.Header.Add("Content-Security-Policy", strings.ReplaceAll(ctx.Configuration.Server.Headers.CSPTemplate, cspNoncePlaceholder, nonce)) case os.Getenv("ENVIRONMENT") == dev: - ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' 'unsafe-eval'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce)) + ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf(cspDefaultDevTemplate, nonce)) default: - ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' ; object-src 'none'; style-src 'self' 'nonce-%s'", nonce)) + ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf(cspDefaultTemplate, nonce)) } err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, BaseURL, CSPNonce, DuoSelfEnrollment, LogoOverride, RememberMe, ResetPassword, Session, Theme string }{Base: base, BaseURL: baseURL, CSPNonce: nonce, DuoSelfEnrollment: duoSelfEnrollment, LogoOverride: logoOverride, RememberMe: rememberMe, ResetPassword: resetPassword, Session: session, Theme: theme})