Headers middleware: support Content-Security-Policy-Report-Only

This commit is contained in:
Roman Donchenko 2024-06-07 10:24:04 +03:00 committed by GitHub
parent 67f0700377
commit b37aaea36d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 116 additions and 66 deletions

View file

@ -394,6 +394,10 @@ This overrides the `BrowserXssFilter` option.
The `contentSecurityPolicy` option allows the `Content-Security-Policy` header value to be set with a custom value.
### `contentSecurityPolicyReportOnly`
The `contentSecurityPolicyReportOnly` option allows the `Content-Security-Policy-Report-Only` header value to be set with a custom value.
### `publicKey`
The `publicKey` implements HPKP to prevent MITM attacks with forged certificates.

View file

@ -55,6 +55,7 @@
- "traefik.http.middlewares.middleware12.headers.allowedhosts=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.browserxssfilter=true"
- "traefik.http.middlewares.middleware12.headers.contentsecuritypolicy=foobar"
- "traefik.http.middlewares.middleware12.headers.contentsecuritypolicyreportonly=foobar"
- "traefik.http.middlewares.middleware12.headers.contenttypenosniff=true"
- "traefik.http.middlewares.middleware12.headers.custombrowserxssvalue=foobar"
- "traefik.http.middlewares.middleware12.headers.customframeoptionsvalue=foobar"

View file

@ -198,6 +198,7 @@
browserXssFilter = true
customBrowserXSSValue = "foobar"
contentSecurityPolicy = "foobar"
contentSecurityPolicyReportOnly = "foobar"
publicKey = "foobar"
referrerPolicy = "foobar"
permissionsPolicy = "foobar"

View file

@ -242,6 +242,7 @@ http:
browserXssFilter: true
customBrowserXSSValue: foobar
contentSecurityPolicy: foobar
contentSecurityPolicyReportOnly: foobar
publicKey: foobar
referrerPolicy: foobar
permissionsPolicy: foobar

View file

@ -1309,6 +1309,10 @@ spec:
description: ContentSecurityPolicy defines the Content-Security-Policy
header value.
type: string
contentSecurityPolicyReportOnly:
description: ContentSecurityPolicyReportOnly defines the Content-Security-Policy-Report-Only
header value.
type: string
contentTypeNosniff:
description: ContentTypeNosniff defines whether to add the X-Content-Type-Options
header with the nosniff value.

View file

@ -71,6 +71,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
| `traefik/http/middlewares/Middleware12/headers/allowedHosts/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/browserXssFilter` | `true` |
| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicyReportOnly` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/contentTypeNosniff` | `true` |
| `traefik/http/middlewares/Middleware12/headers/customBrowserXSSValue` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customFrameOptionsValue` | `foobar` |

View file

@ -585,6 +585,10 @@ spec:
description: ContentSecurityPolicy defines the Content-Security-Policy
header value.
type: string
contentSecurityPolicyReportOnly:
description: ContentSecurityPolicyReportOnly defines the Content-Security-Policy-Report-Only
header value.
type: string
contentTypeNosniff:
description: ContentTypeNosniff defines whether to add the X-Content-Type-Options
header with the nosniff value.

View file

@ -1309,6 +1309,10 @@ spec:
description: ContentSecurityPolicy defines the Content-Security-Policy
header value.
type: string
contentSecurityPolicyReportOnly:
description: ContentSecurityPolicyReportOnly defines the Content-Security-Policy-Report-Only
header value.
type: string
contentTypeNosniff:
description: ContentTypeNosniff defines whether to add the X-Content-Type-Options
header with the nosniff value.

View file

@ -330,6 +330,7 @@
browserXssFilter = true
customBrowserXSSValue = "foobar"
contentSecurityPolicy = "foobar"
contentSecurityPolicyReportOnly = "foobar"
publicKey = "foobar"
referrerPolicy = "foobar"
isDevelopment = true

View file

@ -313,6 +313,8 @@ type Headers struct {
CustomBrowserXSSValue string `json:"customBrowserXSSValue,omitempty" toml:"customBrowserXSSValue,omitempty" yaml:"customBrowserXSSValue,omitempty"`
// ContentSecurityPolicy defines the Content-Security-Policy header value.
ContentSecurityPolicy string `json:"contentSecurityPolicy,omitempty" toml:"contentSecurityPolicy,omitempty" yaml:"contentSecurityPolicy,omitempty"`
// ContentSecurityPolicyReportOnly defines the Content-Security-Policy-Report-Only header value.
ContentSecurityPolicyReportOnly string `json:"contentSecurityPolicyReportOnly,omitempty" toml:"contentSecurityPolicyReportOnly,omitempty" yaml:"contentSecurityPolicyReportOnly,omitempty"`
// PublicKey is the public key that implements HPKP to prevent MITM attacks with forged certificates.
PublicKey string `json:"publicKey,omitempty" toml:"publicKey,omitempty" yaml:"publicKey,omitempty"`
// ReferrerPolicy defines the Referrer-Policy header value.
@ -376,6 +378,7 @@ func (h *Headers) HasSecureHeadersDefined() bool {
h.BrowserXSSFilter ||
h.CustomBrowserXSSValue != "" ||
h.ContentSecurityPolicy != "" ||
h.ContentSecurityPolicyReportOnly != "" ||
h.PublicKey != "" ||
h.ReferrerPolicy != "" ||
(h.FeaturePolicy != nil && *h.FeaturePolicy != "") ||

View file

@ -63,6 +63,7 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.middlewares.Middleware8.headers.addvaryheader": "true",
"traefik.http.middlewares.Middleware8.headers.browserxssfilter": "true",
"traefik.http.middlewares.Middleware8.headers.contentsecuritypolicy": "foobar",
"traefik.http.middlewares.Middleware8.headers.contentsecuritypolicyreportonly": "foobar",
"traefik.http.middlewares.Middleware8.headers.contenttypenosniff": "true",
"traefik.http.middlewares.Middleware8.headers.custombrowserxssvalue": "foobar",
"traefik.http.middlewares.Middleware8.headers.customframeoptionsvalue": "foobar",
@ -611,22 +612,23 @@ func TestDecodeConfiguration(t *testing.T) {
"name0": "foobar",
"name1": "foobar",
},
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
ContentSecurityPolicyReportOnly: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
},
},
"Middleware9": {
@ -1134,22 +1136,23 @@ func TestEncodeConfiguration(t *testing.T) {
"name0": "foobar",
"name1": "foobar",
},
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
ContentSecurityPolicyReportOnly: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
},
},
"Middleware9": {
@ -1299,6 +1302,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Middlewares.Middleware8.Headers.AllowedHosts": "foobar, fiibar",
"traefik.HTTP.Middlewares.Middleware8.Headers.BrowserXSSFilter": "true",
"traefik.HTTP.Middlewares.Middleware8.Headers.ContentSecurityPolicy": "foobar",
"traefik.HTTP.Middlewares.Middleware8.Headers.ContentSecurityPolicyReportOnly": "foobar",
"traefik.HTTP.Middlewares.Middleware8.Headers.ContentTypeNosniff": "true",
"traefik.HTTP.Middlewares.Middleware8.Headers.CustomBrowserXSSValue": "foobar",
"traefik.HTTP.Middlewares.Middleware8.Headers.CustomFrameOptionsValue": "foobar",

View file

@ -17,24 +17,25 @@ type secureHeader struct {
// newSecure constructs a new secure instance with supplied options.
func newSecure(next http.Handler, cfg dynamic.Headers, contextKey string) *secureHeader {
opt := secure.Options{
BrowserXssFilter: cfg.BrowserXSSFilter,
ContentTypeNosniff: cfg.ContentTypeNosniff,
ForceSTSHeader: cfg.ForceSTSHeader,
FrameDeny: cfg.FrameDeny,
IsDevelopment: cfg.IsDevelopment,
STSIncludeSubdomains: cfg.STSIncludeSubdomains,
STSPreload: cfg.STSPreload,
ContentSecurityPolicy: cfg.ContentSecurityPolicy,
CustomBrowserXssValue: cfg.CustomBrowserXSSValue,
CustomFrameOptionsValue: cfg.CustomFrameOptionsValue,
PublicKey: cfg.PublicKey,
ReferrerPolicy: cfg.ReferrerPolicy,
AllowedHosts: cfg.AllowedHosts,
HostsProxyHeaders: cfg.HostsProxyHeaders,
SSLProxyHeaders: cfg.SSLProxyHeaders,
STSSeconds: cfg.STSSeconds,
PermissionsPolicy: cfg.PermissionsPolicy,
SecureContextKey: contextKey,
BrowserXssFilter: cfg.BrowserXSSFilter,
ContentTypeNosniff: cfg.ContentTypeNosniff,
ForceSTSHeader: cfg.ForceSTSHeader,
FrameDeny: cfg.FrameDeny,
IsDevelopment: cfg.IsDevelopment,
STSIncludeSubdomains: cfg.STSIncludeSubdomains,
STSPreload: cfg.STSPreload,
ContentSecurityPolicy: cfg.ContentSecurityPolicy,
ContentSecurityPolicyReportOnly: cfg.ContentSecurityPolicyReportOnly,
CustomBrowserXssValue: cfg.CustomBrowserXSSValue,
CustomFrameOptionsValue: cfg.CustomFrameOptionsValue,
PublicKey: cfg.PublicKey,
ReferrerPolicy: cfg.ReferrerPolicy,
AllowedHosts: cfg.AllowedHosts,
HostsProxyHeaders: cfg.HostsProxyHeaders,
SSLProxyHeaders: cfg.SSLProxyHeaders,
STSSeconds: cfg.STSSeconds,
PermissionsPolicy: cfg.PermissionsPolicy,
SecureContextKey: contextKey,
}
return &secureHeader{

View file

@ -139,6 +139,7 @@ func Test_buildConfiguration(t *testing.T) {
"traefik/http/middlewares/Middleware09/headers/accessControlExposeHeaders/0": "foobar",
"traefik/http/middlewares/Middleware09/headers/accessControlExposeHeaders/1": "foobar",
"traefik/http/middlewares/Middleware09/headers/contentSecurityPolicy": "foobar",
"traefik/http/middlewares/Middleware09/headers/contentSecurityPolicyReportOnly": "foobar",
"traefik/http/middlewares/Middleware09/headers/publicKey": "foobar",
"traefik/http/middlewares/Middleware09/headers/customRequestHeaders/name0": "foobar",
"traefik/http/middlewares/Middleware09/headers/customRequestHeaders/name1": "foobar",
@ -601,22 +602,23 @@ func Test_buildConfiguration(t *testing.T) {
"name1": "foobar",
"name0": "foobar",
},
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
SSLForceHost: Bool(true),
STSSeconds: 42,
STSIncludeSubdomains: true,
STSPreload: true,
ForceSTSHeader: true,
FrameDeny: true,
CustomFrameOptionsValue: "foobar",
ContentTypeNosniff: true,
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foobar",
ContentSecurityPolicy: "foobar",
ContentSecurityPolicyReportOnly: "foobar",
PublicKey: "foobar",
ReferrerPolicy: "foobar",
FeaturePolicy: String("foobar"),
PermissionsPolicy: "foobar",
IsDevelopment: true,
},
},
"Middleware17": {

View file

@ -214,6 +214,7 @@ func init() {
BrowserXSSFilter: true,
CustomBrowserXSSValue: "foo",
ContentSecurityPolicy: "foo",
ContentSecurityPolicyReportOnly: "foo",
PublicKey: "foo",
ReferrerPolicy: "foo",
PermissionsPolicy: "foo",

View file

@ -170,6 +170,7 @@
"browserXssFilter": true,
"customBrowserXSSValue": "xxxx",
"contentSecurityPolicy": "xxxx",
"contentSecurityPolicyReportOnly": "xxxx",
"publicKey": "xxxx",
"referrerPolicy": "foo",
"permissionsPolicy": "foo",

View file

@ -173,6 +173,7 @@
"browserXssFilter": true,
"customBrowserXSSValue": "foo",
"contentSecurityPolicy": "foo",
"contentSecurityPolicyReportOnly": "foo",
"publicKey": "foo",
"referrerPolicy": "foo",
"permissionsPolicy": "foo",

View file

@ -817,6 +817,22 @@
</div>
</div>
</q-card-section>
<!-- EXTRA FIELDS FROM MIDDLEWARES - [headers] - contentSecurityPolicyReportOnly -->
<q-card-section v-if="middleware.headers">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">
Content Security Policy (Report Only)
</div>
<q-chip
dense
class="app-chip app-chip-green"
>
{{ exData(middleware).contentSecurityPolicyReportOnly }}
</q-chip>
</div>
</div>
</q-card-section>
<!-- EXTRA FIELDS FROM MIDDLEWARES - [headers] - publicKey -->
<q-card-section v-if="middleware.headers">
<div class="row items-start no-wrap">