New to Voyager? Please start here.
HSTS
HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797.
The HSTS Policy is communicated by the server to the user agent via an HTTPS response header field named “Strict-Transport-Security”. HSTS Policy specifies a period of time during which the user agent should only access the server in a secure fashion.
Voyager can insert HSTS headers in http response if configured via following ingress annotations:
ingress.appscode.com/hsts
: Iffalse
disables HSTS. By default HSTS is enabled.ingress.appscode.com/hsts-preload
: Iftrue
enables HSTS preload.ingress.appscode.com/hsts-include-subdomains
: Iftrue
HSTS rule applies to all of the site’s sub domains.ingress.appscode.com/hsts-max-age
: Specifies the time (in seconds) the browser should connect to the server using the HTTPS connection. You can also specify time with units such as “300ms”, “-1.5h” or “2h45m”. Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”. Default value is15768000
(6 months).
Ingress example
First create a test-server and expose it via service:
$ kubectl run test-server --image=gcr.io/google_containers/echoserver:1.8
deployment "test-server" created
$ kubectl expose deployment test-server --type=LoadBalancer --port=80 --target-port=8080
service "test-server" exposed
Then create required tls-certificates:
$ ./onessl-linux-amd64 create ca-cert
Wrote ca certificates in /home/appscode
$ ./onessl-linux-amd64 create server-cert tls
Wrote server certificates in /home/appscode
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
secret "tls-secret" created
Now create the ingress with HSTS annotations:
$ kubectl apply -f test-ingress.yaml
apiVersion: voyager.appscode.com/v1
kind: Ingress
metadata:
name: test-ingress
namespace: default
annotations:
ingress.appscode.com/hsts: "true"
ingress.appscode.com/hsts-preload: "true"
ingress.appscode.com/hsts-include-subdomains: "true"
ingress.appscode.com/hsts-max-age: "100"
spec:
tls:
- secretName: tls-secret
hosts:
- voyager.appscode.test
rules:
- host: voyager.appscode.test
http:
paths:
- path: /foo
backend:
service:
name: test-server
port:
number: 80
Generated haproxy.cfg:
# HAProxy configuration generated by https://github.com/appscode/voyager
# DO NOT EDIT!
global
daemon
stats socket /tmp/haproxy
server-state-file global
server-state-base /var/state/haproxy/
# log using a syslog socket
log /dev/log local0 info
log /dev/log local0 notice
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
hard-stop-after 30s
defaults
log global
# https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-option%20abortonclose
# https://github.com/voyagermesh/voyager/pull/403
option dontlognull
option http-server-close
# Timeout values
timeout client 50s
timeout client-fin 50s
timeout connect 5s
timeout server 50s
timeout tunnel 50s
# Configure error files
# default traffic mode is http
# mode is overwritten in case of tcp services
mode http
frontend http-0_0_0_0-80
bind *:80
mode http
option httplog
option forwardfor
acl is_proxy_https hdr(X-Forwarded-Proto) https
acl acl_voyager.appscode.test hdr(host) -i voyager.appscode.test
acl acl_voyager.appscode.test hdr(host) -i voyager.appscode.test:80
acl acl_voyager.appscode.test:foo path_beg /foo
redirect scheme https code 308 if ! is_proxy_https acl_voyager.appscode.test acl_voyager.appscode.test:foo
frontend http-0_0_0_0-443
bind *:443 ssl no-sslv3 no-tlsv10 no-tls-tickets crt /etc/ssl/private/haproxy/tls/ alpn http/1.1
# Mark all cookies as secure
rsprep ^Set-Cookie:\ (.*) Set-Cookie:\ \1;\ Secure
# Add the HSTS header with a 6 month default max-age
http-response set-header Strict-Transport-Security max-age=100;\ preload;\ includeSubDomains
mode http
option httplog
option forwardfor
acl is_proxy_https hdr(X-Forwarded-Proto) https
acl acl_voyager.appscode.test hdr(host) -i voyager.appscode.test
acl acl_voyager.appscode.test hdr(host) -i voyager.appscode.test:443
acl acl_voyager.appscode.test:foo path_beg /foo
use_backend test-server.default:80 if acl_voyager.appscode.test acl_voyager.appscode.test:foo
backend test-server.default:80
server pod-test-server-68ddc845cd-tpq7d 172.17.0.4:8080
Get url for offshoot voyager service:
$ kubectl get service voyager-test-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
voyager-test-ingress LoadBalancer 10.106.67.91 <pending> 443:32273/TCP,80:31129/TCP 21m
$ minikube service --url voyager-test-ingress
http://192.168.99.100:32273
http://192.168.99.100:31129
Applying the annotation in ingress will have the following effects, will add the HSTS Header in the response.
$ curl -v -k -H 'Host: voyager.appscode.test' https://192.168.99.100:32273/foo
* Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 32273 (#0)
* found 148 certificates in /etc/ssl/certs/ca-certificates.crt
* found 597 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
* server certificate verification SKIPPED
* server certificate status verification SKIPPED
* common name: tls (does not match '192.168.99.100')
* server certificate expiration date OK
* server certificate activation date OK
* certificate public key: RSA
* certificate version: #3
* subject: CN=tls
* start date: Tue, 13 Feb 2018 09:14:08 GMT
* expire date: Wed, 13 Feb 2019 09:14:15 GMT
* issuer: CN=ca
* compression: NULL
* ALPN, server accepted to use http/1.1
> GET /foo HTTP/1.1
> Host: voyager.appscode.test
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 13 Feb 2018 11:42:02 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Server: echoserver
< Strict-Transport-Security: max-age=100; preload; includeSubDomains
<
Hostname: test-server-68ddc845cd-tpq7d
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.3 - lua: 10008
Request Information:
client_address=172.17.0.5
method=GET
real path=/foo
query=
request_version=1.1
request_uri=http://voyager.appscode.test:8080/foo
Request Headers:
accept=*/*
connection=close
host=voyager.appscode.test
user-agent=curl/7.47.0
x-forwarded-for=172.17.0.1
Request Body:
-no body in request-
* Connection #0 to host 192.168.99.100 left intact