I'm setting up an instance of ghost and I'm trying to secure the /ghost path with client cert verification.
I've got an initial ingress up and running that serves the site quite happily with the path specified as /.
I'm trying to add a second ingress (that's mostly the same) for the /ghost path. If I do this and add the annotations for basic auth, everything seems to work. i.e. If I browse to /ghost I am prompted for credentials in the basic-auth secret, if I browse to any other URL it is served without auth.
I then switched to client cert verification based on this example: https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/auth/client-certs
When I try this either the whole site or none of the site is secured, rather than the path-based separation, I got with basic-auth. Looking at the nginx.conf from the running pod the proxy_set_header ssl-client-verify
, proxy_set_header ssl-client-subject-dn
& proxy_set_header ssl-client-issuer-dn
elements are added under the root / path and the /ghost path. I've tried removing those (from the root only) and copying the config directly back to the pod but not luck there either.
I'm pulling nginx-ingress (Chart version 0.23.0) in as a dependency via Helm
Ingress definition for /
location - this one works
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
labels:
app: my-app
chart: my-app-0.1.1
heritage: Tiller
release: my-app
name: my-app
namespace: default
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: my-app
servicePort: http
path: /
tls:
- hosts:
- example.com
secretName: mysite-tls
Ingress definition for /ghost
location - this one doesn't work
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-secret: "default/auth-tls-chain"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
nginx.ingress.kubernetes.io/auth-tls-error-page: "http://www.example.com/error-cert.html"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false"
kubernetes.io/ingress.class: "nginx"
labels:
app: my-app
chart: my-app-0.1.1
heritage: Tiller
release: my-app
name: my-app-secure
namespace: default
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: my-app
servicePort: http
path: /ghost
tls:
- hosts:
- example.com
secretName: mysite-tls
You could add a location-snippet
annotations:
nginx.ingress.kubernetes.io/location-snippet: |
if ($location ^~ ghost) {
set $ban_info = "location";
}
if ($ssl_client_s_dn !~ "CN=ok_client") {
set $ban_info = "${ban_info}+no_client";
}
if ($ban_info = "location+no_client") {
return 403;
}
You need a '*'
on your path on your second ingress if you want to serve all the pages securely under /ghost
and if you want just /ghost
you need another rule. Something like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-secret: "default/auth-tls-chain"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
nginx.ingress.kubernetes.io/auth-tls-error-page: "http://www.example.com/error-cert.html"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false"
kubernetes.io/ingress.class: "nginx"
labels:
app: my-app
chart: my-app-0.1.1
heritage: Tiller
release: my-app
name: my-app-secure
namespace: default
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: my-app
servicePort: http
path: /ghost
- backend:
serviceName: my-app
servicePort: http
path: /ghost/*
tls:
- hosts:
- example.com
secretName: mysite-tls
However, if you want something like /
unsecured and /ghost
secured, I believe you won't be able to do it. For example, if you are using nginx, this is a limitation from nginx itself, when you configure a server {}
block with TLS in nginx it looks something like this:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate example.com.crt;
ssl_certificate_key example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
The ingress controller creates paths like this:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate example.com.crt;
ssl_certificate_key example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
location / {
...
}
location /ghost {
...
}
}
So when you configure another server {}
block with the same hostname and with no SSL it will override the first one.
You could do it with different - host:
rules in your ingress for example ghost.example.com
with TLS and main.example.com
without TLS. So in your nginx.conf you would have different server {}
blocks.
You can always shell into the ingress controller pod to check the configs, for example:
$ kubectl exec -it nginx-ingress-controller-xxxxxxxxx-xxxxx bash
www-data@nginx-ingress-controller-6bd7c597cb-8kzjh:/etc/nginx$ cat nginx.conf