Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kubernetes security header annotations #2460

Merged
merged 3 commits into from
Nov 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 46 additions & 0 deletions autogen/gentemplates/gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 28 additions & 2 deletions docs/configuration/backends/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Træfik can be configured to use Kubernetes Ingress as a backend configuration.

See also [Kubernetes user guide](/user-guide/kubernetes).
See also [Kubernetes user guide](/user-guide/kubernetes).


## Configuration
Expand Down Expand Up @@ -102,7 +102,7 @@ Annotations can be used on containers to override default behaviour for the whol
Override the default frontend rule type. Default: `PathPrefix`.
- `traefik.frontend.priority: "3"`
Override the default frontend rule priority.
- `traefik.frontend.redirect: https`:
- `traefik.frontend.redirect: https`:
Enables Redirect to another entryPoint for that frontend (e.g. HTTPS).
- `traefik.frontend.entryPoints: http,https`
Override the default frontend endpoints.
Expand Down Expand Up @@ -132,6 +132,32 @@ As known from nginx when used as Kubernetes Ingress Controller, a list of IP-Ran
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access.

#### Security annotations

The following security annotations can be applied to the ingress object to add security features:

| Annotation | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ingress.kubernetes.io/allowed-hosts:EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
| `ingress.kubernetes.io/custom-request-headers:EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: `HEADER:value,HEADER2:value2` |
| `ingress.kubernetes.io/custom-response-headers:EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: `HEADER:value,HEADER2:value2` |
| `ingress.kubernetes.io/proxy-headers:EXPR ` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
| `ingress.kubernetes.io/ssl-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `ingress.kubernetes.io/ssl-temporary-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `ingress.kubernetes.io/ssl-host:HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: `HEADER:value,HEADER2:value2` |
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. |
| `ngress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. |
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `ingress.kubernetes.io/custom-frame-options-value:VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `ingress.kubernetes.io/content-type-nosniff:true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `ingress.kubernetes.io/browser-xss-filter:true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `ingress.kubernetes.io/content-security-policy:VALUE` | Adds CSP Header with the custom value. |
| `ingress.kubernetes.io/public-key:VALUE` | Adds pinned HTST public key header. |
| `ingress.kubernetes.io/referrer-policy:VALUE` | Adds referrer policy header. |
| `ingress.kubernetes.io/is-development:false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |

### Authentication

Expand Down
62 changes: 62 additions & 0 deletions provider/kubernetes/annotations_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package kubernetes

import (
"strings"

"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/types"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func getBoolAnnotation(meta *v1beta1.Ingress, name string, defaultValue bool) bool {
annotationValue := defaultValue
annotationStringValue, ok := meta.Annotations[name]
switch {
case !ok:
// No op.
case annotationStringValue == "false":
annotationValue = false
case annotationStringValue == "true":
annotationValue = true
default:
log.Warnf("Unknown value '%s' for %s, falling back to %s", name, types.LabelFrontendPassTLSCert, defaultValue)
}
return annotationValue
}

func getStringAnnotation(meta *v1beta1.Ingress, name string) string {
value := meta.Annotations[name]
return value
}

func getSliceAnnotation(meta *v1beta1.Ingress, name string) []string {
var value []string
if annotation, ok := meta.Annotations[name]; ok && annotation != "" {
value = provider.SplitAndTrimString(annotation)
}
if len(value) == 0 {
log.Debugf("Could not load %v annotation, skipping...", name)
return nil
}
return value
}

func getMapAnnotation(meta *v1beta1.Ingress, name string) map[string]string {
value := make(map[string]string)
if annotation := meta.Annotations[name]; annotation != "" {
for _, v := range strings.Split(annotation, ",") {
pair := strings.Split(v, ":")
if len(pair) != 2 {
log.Debugf("Could not load annotation (%v) with value: %v, skipping...", name, pair)
} else {
value[pair[0]] = pair[1]
}
}
}
if len(value) == 0 {
log.Debugf("Could not load %v annotation, skipping...", name)
return nil
}
return value
}