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: If false disables HSTS. By default HSTS is enabled.
  • ingress.appscode.com/hsts-preload: If true enables HSTS preload.
  • ingress.appscode.com/hsts-include-subdomains: If true 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 is 15768000 (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/v1beta1
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:
          serviceName: test-server
          servicePort: 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
defaults
	log global
	# https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-option%20abortonclose
	# https://github.com/appscode/voyager/pull/403
	option dontlognull
	option http-server-close
	# Timeout values
	timeout client 50s
	timeout client-fin 50s
	timeout connect 50s
	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

Take your team where it needs to go.

Create your cluster in minutes. Our team is here to help and would be happy to chat with you.