feat(server): csp template (#2856)

Implements the ability for advanced users to override the CSP and easily include the required nonce.
pull/2891/head
James Elliott 2022-02-21 10:14:09 +11:00 committed by GitHub
parent 6043b43d94
commit bed7a8ae32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 4 deletions

View File

@ -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
##

View File

@ -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
<div markdown="1">
type: string
{: .label .label-config .label-purple }
default: ""
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
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

View File

@ -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
##

View File

@ -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",

View File

@ -171,6 +171,7 @@ var ValidKeys = []string{
"server.disable_healthcheck",
"server.tls.key",
"server.tls.certificate",
"server.headers.csp_template",
// TOTP Keys.
"totp.issuer",

View File

@ -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}"
)

View File

@ -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})