diff --git a/example/kube/README.md b/example/kube/README.md
new file mode 100644
index 000000000..930724c43
--- /dev/null
+++ b/example/kube/README.md
@@ -0,0 +1,104 @@
+# Authelia on Kubernetes
+
+Authelia is now available on Kube in order to protect your most critical
+applications using 2-factor authentication.
+
+## Getting started
+
+In order to deploy Authelia on Kube, we must have a cluster at hand. If you
+don't, please follow the next section otherwise skip it and go
+to the next.
+
+### Set up a Kube cluster
+
+Hopefully for us, spawning a development cluster from scratch has become very
+easy lately with the use of **minikube**. This project creates a VM on your
+computer and start a Kube cluster inside it. It also configure a CLI called
+kubectl so that you can deploy applications in the cluster right away.
+
+Basically, you need to follow the instruction from the [repository](https://github.com/kubernetes/minikube).
+It should be a matter of downloading the binary and start the cluster with
+two commands:
+
+```
+curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
+minikube start # you can use --vm-driver flag for selecting your hypervisor (virtualbox by default otherwise)
+```
+
+After few seconds, your cluster should be working and you should be able to
+get access to the cluster by creating a proxy with
+
+```
+kubectl proxy
+```
+
+and visiting `http://localhost:8001/ui`
+
+### Deploy Authelia
+
+Once the cluster is ready and you can access it, run the following command to
+deploy Authelia:
+
+```
+./bootstrap.sh
+```
+
+In order to visit the test applications that have been deployed to test
+Authelia, edit your /etc/hosts and add the following lines replacing the IP
+with the IP of your VM given by minikube:
+
+```
+192.168.39.26 login.kube.example.com
+192.168.39.26 app1.kube.example.com
+192.168.39.26 app2.kube.example.com
+192.168.39.26 mail.kube.example.com
+192.168.39.26 home.kube.example.com
+```
+
+Once done, you can visit http://home.kube.example.com and follow the
+instructions written in the page
+
+## How does it work?
+
+### Authentication via Authelia
+
+In a Kube clusters, the routing logic of requests is handled by ingress
+controllers which follow the provided ingress configurations.
+
+In this setup, requests goes through a [ingress-nginx](https://github.com/kubernetes/ingress-nginx)
+controller which forward verification requests to Authelia in order to allow
+or deny access.
+
+The authentication is provided at the ingress level by an annotation called
+`nginx.ingress.kubernetes.io/auth-url` that is filled with the URL of
+Authelia's verification endpoint.
+The ingress controller also requires the ingress provides the URL of the
+authentication portal in case the user is not yet authenticated.
+
+Those annotations can be seen in `apps/secure-ingress.yml` configuration.
+
+### Production grade infrastructure
+
+What is great about using [ingress-nginx](https://github.com/kubernetes/ingress-nginx)
+is that it is compatible with [kube-lego](https://github.com/jetstack/kube-lego)
+that makes renewal of SSL certifiactes automatic.
+
+## What do I need know to deploy it in my cluster?
+
+Given your cluster is already made of an LDAP server, a Redis cluster, a Mongo
+cluster and a SMTP server, you'll only need to install the ingress-controller
+and Authelia whose configurations are respectively in `ingress-controller` and
+`authelia` directories.
+
+### I'm already using ingress-nginx
+
+If you're already using ingress-nginx as your ingress controller, the only
+thing you'll need to change is the nginx template used by the controller to
+make it compatible with Authelia. The template is located in
+`ingress-controller/configs/nginx.tmpl`. Make it a configmap and pass it to
+your controller arguments.
+
+## Questions
+
+If you have questions about the implementation, please post them on
+[![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/authelia/general?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
diff --git a/example/kube/apps/app-home/deployment.yml b/example/kube/apps/app-home/deployment.yml
new file mode 100644
index 000000000..3f88fdc13
--- /dev/null
+++ b/example/kube/apps/app-home/deployment.yml
@@ -0,0 +1,33 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: test-app-home
+ namespace: authelia
+ labels:
+ app: test-app-home
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: test-app-home
+ template:
+ metadata:
+ labels:
+ app: test-app-home
+ spec:
+ containers:
+ - name: test-app-home
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ volumeMounts:
+ - name: app-home-page
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: app-home-page
+ configMap:
+ name: app-home-page
+ items:
+ - key: index.html
+ path: index.html
diff --git a/example/kube/apps/app-home/index.html b/example/kube/apps/app-home/index.html
new file mode 100644
index 000000000..6483f6b38
--- /dev/null
+++ b/example/kube/apps/app-home/index.html
@@ -0,0 +1,35 @@
+
+
+ Authelia Home
+
+
+ Authelia on Kube
+
+ In this example, two applications have been deployed along with Authelia and a fake mailbox in order to confirm your secret registration to Authelia:
+
+
+ Please note that app1 is publicly available and app2 is protected by Authelia.
+
+ You can start by visiting app1 and then try to access app2. Since app2 is protected by Authelia, you will be redirected to Authelia's portal.
+
+ If it's the first time you login in this cluster, you'll need to choose your authentication method and follow Authelia's instructions.
+
+ Once done, you'll be able to authenticate with your selected second factor method.
+
+
+ Here is the list of available users in the LDAP
+
+ - john / password
+
- bob / password
+
- harry / password
+
+
+
+ You can always log off by clicking here
+
+
+
diff --git a/example/kube/apps/app-home/service.yml b/example/kube/apps/app-home/service.yml
new file mode 100644
index 000000000..1dd6f0148
--- /dev/null
+++ b/example/kube/apps/app-home/service.yml
@@ -0,0 +1,12 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: test-app-home-service
+ namespace: authelia
+spec:
+ selector:
+ app: test-app-home
+ ports:
+ - protocol: TCP
+ port: 80
diff --git a/example/kube/apps/app1/deployment.yml b/example/kube/apps/app1/deployment.yml
new file mode 100644
index 000000000..883b857f3
--- /dev/null
+++ b/example/kube/apps/app1/deployment.yml
@@ -0,0 +1,33 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: test-app1
+ namespace: authelia
+ labels:
+ app: test-app1
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: test-app1
+ template:
+ metadata:
+ labels:
+ app: test-app1
+ spec:
+ containers:
+ - name: test-app1
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ volumeMounts:
+ - name: app1-page
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: app1-page
+ configMap:
+ name: app1-page
+ items:
+ - key: index.html
+ path: index.html
diff --git a/example/kube/apps/app1/index.html b/example/kube/apps/app1/index.html
new file mode 100644
index 000000000..b9102c5e7
--- /dev/null
+++ b/example/kube/apps/app1/index.html
@@ -0,0 +1,9 @@
+
+
+ Application 1
+
+
+ Application 1
+ Go Home
+
+
diff --git a/example/kube/apps/app1/service.yml b/example/kube/apps/app1/service.yml
new file mode 100644
index 000000000..3a229ad0f
--- /dev/null
+++ b/example/kube/apps/app1/service.yml
@@ -0,0 +1,12 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: test-app1-service
+ namespace: authelia
+spec:
+ selector:
+ app: test-app1
+ ports:
+ - protocol: TCP
+ port: 80
diff --git a/example/kube/apps/app1/ssl/tls.crt b/example/kube/apps/app1/ssl/tls.crt
new file mode 100644
index 000000000..41a0d908b
--- /dev/null
+++ b/example/kube/apps/app1/ssl/tls.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaQCCQD9zdaObelW0jANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVh
+cHAxLmt1YmUuZXhhbXBsZS5jb20wHhcNMTgwMzA0MTUxMDUxWhcNMjgwMzAxMTUx
+MDUxWjAgMR4wHAYDVQQDDBVhcHAxLmt1YmUuZXhhbXBsZS5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+v2s9ABHFgxuYFyGkqlbNQ5OtzHAB79lM
+sbAMJ9pnMTBA4FXdHgQuQ6Z4F233hMokVfnX9C2KxuLLOQMXJdoVQGytkbGGCzoz
+Hz1qXBjDCwQtUGgLJ6zc3C8QKx90zmY0NmH55ttFCQKPHaaxgrS2YXsPzMlQKrgH
+drRklfCpnRZWG9/M1YzXOKeT4VuwTsHyeI8tnco11WJLsZwRxc6TjEgNwwBnct8R
+/cDhl5guDhqFPgMuBtzRlTVOeZS8NraHL5GRswoUeFJfS2VA3FTABwRWV3J6d5gl
+oXpvMzCB01u2jVKLq75g91lGT6I+AVMgSOJG4Nezum7brS7jJjVFAgMBAAEwDQYJ
+KoZIhvcNAQELBQADggEBAIFox2Ox/hJqmaSAyE0TqCaLf3UK78z9m/FYUzWoupGE
+JAJ83wghVcj7XLKG4x6wN+v342JVa0mQ5X773kqNidHmSdWFPd/hGtx+0dQK3BVV
+4CmQCNfA7BZDuxWPW0mcrkKun2aSCq0zjK0f/CzPXZxyPW18EmzSNGkmkXCesM5i
+aevdE5PBKikGz4EfzieVTsImdHDfh2/azdRrmSh22x9/tpZy3mMkc5ERpgL2ll+m
+HZuZ9FEBQHj92pk2aMBVdxPYpgzWAWbuRUs3vfTIhPlzHcI/ZpE60Ete+yY5lBw/
+Gf+ph3HPB8gzSOH/hmcmerX9h6E3MFAncdC4hH3R0j8=
+-----END CERTIFICATE-----
diff --git a/example/kube/apps/app1/ssl/tls.csr b/example/kube/apps/app1/ssl/tls.csr
new file mode 100644
index 000000000..808704262
--- /dev/null
+++ b/example/kube/apps/app1/ssl/tls.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICZTCCAU0CAQAwIDEeMBwGA1UEAwwVYXBwMS5rdWJlLmV4YW1wbGUuY29tMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvr9rPQARxYMbmBchpKpWzUOT
+rcxwAe/ZTLGwDCfaZzEwQOBV3R4ELkOmeBdt94TKJFX51/QtisbiyzkDFyXaFUBs
+rZGxhgs6Mx89alwYwwsELVBoCyes3NwvECsfdM5mNDZh+ebbRQkCjx2msYK0tmF7
+D8zJUCq4B3a0ZJXwqZ0WVhvfzNWM1zink+FbsE7B8niPLZ3KNdViS7GcEcXOk4xI
+DcMAZ3LfEf3A4ZeYLg4ahT4DLgbc0ZU1TnmUvDa2hy+RkbMKFHhSX0tlQNxUwAcE
+VldyeneYJaF6bzMwgdNbto1Si6u+YPdZRk+iPgFTIEjiRuDXs7pu260u4yY1RQID
+AQABoAAwDQYJKoZIhvcNAQELBQADggEBALcwQAjNjyhMAbGNpFl6iYhzdhUjz02p
+hTpc15T+S4PatbgeERYAIuSGxomPHfh+30udxGDCSy730V2urC0eGPjRpnJpGHNG
+1Dau0sgi28TQPOqhBcZm0GNHMocc5iG+AxWAsxNwtSH3wRoeUGYXavcm9/tWvYbi
+RIKmzXTQKsmY+qhBo3e/phUFuSU8GARYkfDSVqcTM7C3xswgXYcImrkbHAxvsC7+
+3nr8ir2K9t2M3QxV7lTybNacuOF84wL93AZDvJ04HctXXgtt2rSI6vxxt18jyrxJ
+yHX9ADrAa+jhPAM664SZs/+l0fBbil2UaMPOKXOCmYGuERpWh1HLHKo=
+-----END CERTIFICATE REQUEST-----
diff --git a/example/kube/apps/app1/ssl/tls.key b/example/kube/apps/app1/ssl/tls.key
new file mode 100644
index 000000000..c42ccf379
--- /dev/null
+++ b/example/kube/apps/app1/ssl/tls.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvr9rPQARxYMbmBchpKpWzUOTrcxwAe/ZTLGwDCfaZzEwQOBV
+3R4ELkOmeBdt94TKJFX51/QtisbiyzkDFyXaFUBsrZGxhgs6Mx89alwYwwsELVBo
+Cyes3NwvECsfdM5mNDZh+ebbRQkCjx2msYK0tmF7D8zJUCq4B3a0ZJXwqZ0WVhvf
+zNWM1zink+FbsE7B8niPLZ3KNdViS7GcEcXOk4xIDcMAZ3LfEf3A4ZeYLg4ahT4D
+Lgbc0ZU1TnmUvDa2hy+RkbMKFHhSX0tlQNxUwAcEVldyeneYJaF6bzMwgdNbto1S
+i6u+YPdZRk+iPgFTIEjiRuDXs7pu260u4yY1RQIDAQABAoIBAQCwn3ahCUtrZDdM
+4T5ZxxCRCJ3aNI8SfBDuHyowV0a4fqd7qz5WfNDKNgIS+T7uDptOgf3SpVr2QasH
+GkduS7JgM0NuhJWo1QSTCb5Imfajw7OecfGlQpuh9o/tnMCH3AZvGlwmlkk651jj
+REVx4OGMbz8QJkPSY3v8DUKEUQKDSkRhZRhRGDZqbOSeERhOo2O3MqcBaVossYRw
++2Apth2XHg+7mgQjQF+8Z/DeXtLZgOB4VwQ0vkJmSu+FLhWDJqA+WH5JaOrmS3SN
+lrPeIEwQMGHmwmIUAaQ7Akkow2xx4VuDRQUcUlitAM21x/MjBdTFbFx975svOiuE
+vePNasYBAoGBAOK8e8x0OrqhgsVGWLvJjHnhzTePpemCCB9jRRnTcmMUxXR7moCh
+WGgbyQ3+pU15WMxaIpVrGlV3LOwqSEXYspIpBQKv5+kSAaJf/Op3rtuzo57JLMYP
+YokMu43ewtSF0CE/7h98vruAYCcDJZs+T7K0lHCBS9pcjhU2MS/ktW2lAoGBANdd
+2a1dkEbZyD9NMGD+wWTkctrML3r1ZLy2gq1AX/oyKzdtD6T1nob435RVkyqPGLhr
+tEHJuxLiaJlCk5Kd8V84ps0J9SlZTkLzf2ixb06f1YI9ye37UzBr13H59+N6w8Zu
+24Gv12ht+WBecWKHgtF16LaCDPYORUMOa3CpgFchAoGBAN6M0Rr6jta3R0tpZBlW
+mErd5vd9SQWtO1nLr3zM/f7g2XsfA6T0OXlepHbXFtu3mwBiDIYK7XssEezxB6V/
+MK+kEaX0kTZFFVOS0gY2WWyOo7BsmEUDvtz0oXd8SlId0g+A17MSV4hlVnuUbCo3
+/DRVaUoQrypzJIcPfTIcVDR9AoGAY3uNrqB2odO9xUfhnhxvtywzxc/l6tVp6CYi
+bOc8rnT4M40kWd2/kbdqh7mT1mftUlsmE/GcgZemG41+X46nzYV8v1/nKGeBWDnk
+U7cKpHX+iUADg/PBNK/MAHEoSaMOxh21Nc3FIg8Sz6owlAPmsNzXV17xn8NtyRDj
+HlKd3yECgYBvB0bV1yXOvfoqGXAyadNJM9eKOuQwhrOJCXzCeMInhUtLpH35kC17
+e8rOmoBhV1rLpxlMLV8giyJSRUPoN/TfUHfS2pWfKPobrp50vaUn1hYp6EEr85Qs
+DdV2VilJsJN49sMg67GV3Zi4lnjVzYNFegw6y5Xi8l8ulYtzxJnemA==
+-----END RSA PRIVATE KEY-----
diff --git a/example/kube/apps/app2/deployment.yml b/example/kube/apps/app2/deployment.yml
new file mode 100644
index 000000000..5cb7e0bb0
--- /dev/null
+++ b/example/kube/apps/app2/deployment.yml
@@ -0,0 +1,33 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: test-app2
+ namespace: authelia
+ labels:
+ app: test-app2
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: test-app2
+ template:
+ metadata:
+ labels:
+ app: test-app2
+ spec:
+ containers:
+ - name: test-app2
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ volumeMounts:
+ - name: app2-page
+ mountPath: /usr/share/nginx/html
+ volumes:
+ - name: app2-page
+ configMap:
+ name: app2-page
+ items:
+ - key: index.html
+ path: index.html
diff --git a/example/kube/apps/app2/index.html b/example/kube/apps/app2/index.html
new file mode 100644
index 000000000..0eaeb5410
--- /dev/null
+++ b/example/kube/apps/app2/index.html
@@ -0,0 +1,9 @@
+
+
+ Application 2
+
+
+ Application 2
+
Go Home
+
+
diff --git a/example/kube/apps/app2/service.yml b/example/kube/apps/app2/service.yml
new file mode 100644
index 000000000..ef21e223e
--- /dev/null
+++ b/example/kube/apps/app2/service.yml
@@ -0,0 +1,12 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: test-app2-service
+ namespace: authelia
+spec:
+ selector:
+ app: test-app2
+ ports:
+ - protocol: TCP
+ port: 80
diff --git a/example/kube/apps/app2/ssl/tls.crt b/example/kube/apps/app2/ssl/tls.crt
new file mode 100644
index 000000000..ae5914029
--- /dev/null
+++ b/example/kube/apps/app2/ssl/tls.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaQCCQCE6h+pwV+OTTANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVh
+cHAyLmt1YmUuZXhhbXBsZS5jb20wHhcNMTgwMzA0MTUwODUyWhcNMjgwMzAxMTUw
+ODUyWjAgMR4wHAYDVQQDDBVhcHAyLmt1YmUuZXhhbXBsZS5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDuVrXRL0i75y0QpbkFM7h1jIwbKOQYdkCR
+tAp4aFjRAnHE1DtiXuexoyhs0hbwtxmFF0RpPjOVLvLl/AZt6i65dC5gl1czLmmH
+nzjBzUF4jM5qJioP867vR2+SDnD6YWCQrwYG8bHeFKvppAlTY7g8YoTD/b3JNnV1
+t+uaPyNKvu33pOP2vP/w/709WFVEwd/334RFF6fjAyYrOgYgvqcgyJ6E6T4cf/Vl
+psgLZpJ7o4VoFHq1MdP6Ro+HzKwHUsDbH53U6wISe3dRBmHjTJH2sp1KuJ5gR1Ko
+CiiVfq+CCPOxGKNngQe2EqDHmuVuXu30VFu82hznfkXdlhuzTGSfAgMBAAEwDQYJ
+KoZIhvcNAQELBQADggEBAEWeTkGmuXnAPU8JGTa+O00kI9nFy10inbiU8O+XuwUL
+Cj53CRffbzlsCDRDDxxoBuJ5EvWho5F7MR7A8ZRDfWqLTogvjpVp2YJ+jK/iTbqU
+95tCVMByZufa4bHPWmngeYsSu0s0+qRYOeyiCbiFzlzFP3nLSS6aMPwrMUz7/Qp1
+XUE1YfyqjKDkvFN4Wq1GKOUZEh4CJh3SuOE/FrRAiaAWnOH4LVDn5TFEbLyEqZLd
+BrYEDopRsTpJck/ZEF43GZ0t8Y8CKffpWGV4PvSiUnl/7mKd+i+QsTx8CnnlR9y/
+ZvwNRwvRw3uXwkgRinE8wwONAfwB2nIYKyuU9/ODAPs=
+-----END CERTIFICATE-----
diff --git a/example/kube/apps/app2/ssl/tls.csr b/example/kube/apps/app2/ssl/tls.csr
new file mode 100644
index 000000000..13181d9f7
--- /dev/null
+++ b/example/kube/apps/app2/ssl/tls.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICZTCCAU0CAQAwIDEeMBwGA1UEAwwVYXBwMi5rdWJlLmV4YW1wbGUuY29tMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7la10S9Iu+ctEKW5BTO4dYyM
+GyjkGHZAkbQKeGhY0QJxxNQ7Yl7nsaMobNIW8LcZhRdEaT4zlS7y5fwGbeouuXQu
+YJdXMy5ph584wc1BeIzOaiYqD/Ou70dvkg5w+mFgkK8GBvGx3hSr6aQJU2O4PGKE
+w/29yTZ1dbfrmj8jSr7t96Tj9rz/8P+9PVhVRMHf99+ERRen4wMmKzoGIL6nIMie
+hOk+HH/1ZabIC2aSe6OFaBR6tTHT+kaPh8ysB1LA2x+d1OsCEnt3UQZh40yR9rKd
+SrieYEdSqAoolX6vggjzsRijZ4EHthKgx5rlbl7t9FRbvNoc535F3ZYbs0xknwID
+AQABoAAwDQYJKoZIhvcNAQELBQADggEBANqbKTFSeOf9GRgrNuqRGYYdqSPaoXpu
+iSKhJRABj4zMOCJlfDpeMQ8mGfmBUV+IHr+X8/nbMt+OMEf4u1+7Mmz4Zfvkt5gP
+MBlYbauVxn/uIYp7aZgBUABC7SvLeITRz4rnQW5SvCNyuJAKQh84uF82g47S7Oaz
+2dp6NO1nQ/N9SD6y0CyuIXf1KbSk4+lXa3+rGyqpF1aovpXCgvcA3tWrI/Lg2t5E
+uPoiHegKGKyWUZeVh8eKY2ZBCl+uRmwLqTTdzj1HcoK5T1slg0X+K9Q1UsGy23Pw
+RHFtGuel8msESgTnspzQF3T1uOscOOiQFG3xnoZtxH92gFT+pI7DoEY=
+-----END CERTIFICATE REQUEST-----
diff --git a/example/kube/apps/app2/ssl/tls.key b/example/kube/apps/app2/ssl/tls.key
new file mode 100644
index 000000000..b57aac983
--- /dev/null
+++ b/example/kube/apps/app2/ssl/tls.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA7la10S9Iu+ctEKW5BTO4dYyMGyjkGHZAkbQKeGhY0QJxxNQ7
+Yl7nsaMobNIW8LcZhRdEaT4zlS7y5fwGbeouuXQuYJdXMy5ph584wc1BeIzOaiYq
+D/Ou70dvkg5w+mFgkK8GBvGx3hSr6aQJU2O4PGKEw/29yTZ1dbfrmj8jSr7t96Tj
+9rz/8P+9PVhVRMHf99+ERRen4wMmKzoGIL6nIMiehOk+HH/1ZabIC2aSe6OFaBR6
+tTHT+kaPh8ysB1LA2x+d1OsCEnt3UQZh40yR9rKdSrieYEdSqAoolX6vggjzsRij
+Z4EHthKgx5rlbl7t9FRbvNoc535F3ZYbs0xknwIDAQABAoIBAFcXgGDsMlvXYfRP
+Woi4GZN6xEe4bYEy1O1pKNpO5wWZKxGNrBWKMIgM4tzA+HkFr2Ge2vTKMfc1rLS1
+n3PSuzgxaDELnGWrdAyG9ip7Yo02hsbrIzupBCeTpwVsGYSkyLCWBFHNR/2q+Bbs
+RiweqFgIeBNWSV+ZctqNVp6Kq87HuYfm/ka8ewVnPYhEMewuE0vqfVjc+fdPr/5I
+4uQCCw0/ZTFFP++hkyNsH1ZCdUeT83xbgUwFA0M/3ckjMhzcP7lncq6Gvy8NpBhI
+mle/Ev82yaDRl5VKoenpy2d8L+qvIjRbhZiZuTAU9AWRvOKrTMtzKSpyMgWAQZ64
+69ResmECgYEA+2Ws4qBiYd3BKF5tc0fWbS0omThT60oLc6aLu5NoryogHHNwPOud
+69nCDHSyYp+PqK1HLrOAVoTLmuwyys15juizewO809gYRgXlNZ9TKED8Znyg004o
+EQVYxMevq2kfDOBJkMphLXAvFrRFQVB2Km8EfKkrI++1rhxKGJD+FzMCgYEA8rPU
+G1v3/uemBxYO/bEmdjvsDfLI7FBYNmjgvBGJocSwb2sG/tRr0imZBPAuDlCKcZ1E
+G3rZOJ0VsLzxX5RCVYFKRQvUtbaNO+uDJjChxQ/fQQdbIoaIMjyAVLI7t3Ei4+nY
+7eHYjcHeMX54drO8YqQ6OoWZ4x8DI8KNcxBnzOUCgYA+uMRkmn1RS4For/6At5ih
+DpZFfA878fJffVr5hrKkmU7/qjGDkYmKEX9fmjHzdznhbLIIzdIkQ+eElI+rl45P
+gHFfLLSM6ipMNiZUtZaKwYP3kfqSHbrTXFEkb2m9y3Fqxf60uDl8m7Oz53Ar9oY0
+2hP1gkN4KNNcSESYUnyCjwKBgAR9+ZYMDLoGFZeZ++sMJVcY4tSbQsbE8e0H4ej5
+Nh/tYQqe44FB80Dvjip+O4v+R6G0tHcBvhWDKsyboqgPOW8Vtocyodw/JbwPLt09
+FzFrislMVo58CPdNEV7/8YUCrg+j22UDwhtVlEQ8QASKbRkySvWcVW3TvB4kUrPn
+gNRVAoGAI99QzqSRBSLeDgxxRgTM7l5wzyVEmJfmMqQsIbHvYy6zG9iYjFI6UZ8I
+U3C/Sdnq7wCWa70b3L8A1iN2fYVwvkEH5WGbEp5B2Cye50avegbPi1FgGw2VSIuS
+ysAkWaVJXsb2oQNtjidRuEflhy4nr7eybwa6cgarh7ci3JB+tQ8=
+-----END RSA PRIVATE KEY-----
diff --git a/example/kube/apps/insecure-ingress.yml b/example/kube/apps/insecure-ingress.yml
new file mode 100644
index 000000000..8a2c5793b
--- /dev/null
+++ b/example/kube/apps/insecure-ingress.yml
@@ -0,0 +1,28 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: insecure-ingress
+ namespace: authelia
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+spec:
+ tls:
+ - secretName: app1-tls
+ hosts:
+ - app1.kube.example.com
+ rules:
+ - host: app1.kube.example.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: test-app1-service
+ servicePort: 80
+ - host: home.kube.example.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: test-app-home-service
+ servicePort: 80
diff --git a/example/kube/apps/secure-ingress.yml b/example/kube/apps/secure-ingress.yml
new file mode 100644
index 000000000..b840fb5a4
--- /dev/null
+++ b/example/kube/apps/secure-ingress.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: secure-ingress
+ namespace: authelia
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+ nginx.ingress.kubernetes.io/auth-url: "http://authelia-service.authelia.svc.cluster.local/api/verify"
+ nginx.ingress.kubernetes.io/auth-signin: "https://login.kube.example.com?redirect=$redirect"
+spec:
+ tls:
+ - secretName: app2-tls
+ hosts:
+ - app2.kube.example.com
+ rules:
+ - host: app2.kube.example.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: test-app2-service
+ servicePort: 80
diff --git a/example/kube/authelia/configs/config.yml b/example/kube/authelia/configs/config.yml
new file mode 100644
index 000000000..e19051a84
--- /dev/null
+++ b/example/kube/authelia/configs/config.yml
@@ -0,0 +1,210 @@
+###############################################################
+# Authelia configuration #
+###############################################################
+
+# The port to listen on
+port: 80
+
+# Log level
+#
+# Level of verbosity for logs
+logs_level: debug
+
+# Default redirection URL
+#
+# If user tries to authenticate without any referer, Authelia
+# does not know where to redirect the user to at the end of the
+# authentication process.
+# This parameter allows you to specify the default redirection
+# URL Authelia will use in such a case.
+#
+# Note: this parameter is optional. If not provided, user won't
+# be redirected upon successful authentication.
+default_redirection_url: https://login.kube.example.com
+
+# LDAP configuration
+#
+# Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com
+ldap:
+ # The url of the ldap server
+ url: ldap://ldap-service
+
+ # The base dn for every entries
+ base_dn: dc=example,dc=com
+
+ # An additional dn to define the scope to all users
+ additional_users_dn: ou=users
+
+ # The users filter used to find the user DN
+ # {0} is a matcher replaced by username.
+ # 'cn={0}' by default.
+ users_filter: cn={0}
+
+ # An additional dn to define the scope of groups
+ additional_groups_dn: ou=groups
+
+ # The groups filter used for retrieving groups of a given user.
+ # {0} is a matcher replaced by username.
+ # {dn} is a matcher replaced by user DN.
+ # 'member={dn}' by default.
+ groups_filter: (&(member={dn})(objectclass=groupOfNames))
+
+ # The attribute holding the name of the group
+ group_name_attribute: cn
+
+ # The attribute holding the mail address of the user
+ mail_attribute: mail
+
+ # The username and password of the admin user.
+ user: cn=admin,dc=example,dc=com
+ password: password
+
+
+# Authentication methods
+#
+# Authentication methods can be defined per subdomain.
+# There are currently two available methods: "single_factor" and "two_factor"
+#
+# Note: by default a domain uses "two_factor" method.
+#
+# Note: 'per_subdomain_methods' is a dictionary where keys must be subdomains and
+# values must be one of the two possible methods.
+#
+# Note: 'per_subdomain_methods' is optional.
+#
+# Note: authentication_methods is optional. If it is not set all sub-domains
+# are protected by two factors.
+authentication_methods:
+ default_method: two_factor
+# per_subdomain_methods:
+# single_factor.example.com: single_factor
+
+# Access Control
+#
+# Access control is a set of rules you can use to restrict user access to certain
+# resources.
+# Any (apply to anyone), per-user or per-group rules can be defined.
+#
+# If 'access_control' is not defined, ACL rules are disabled and the `allow` default
+# policy is applied, i.e., access is allowed to anyone. Otherwise restrictions follow
+# the rules defined.
+#
+# Note: One can use the wildcard * to match any subdomain.
+# It must stand at the beginning of the pattern. (example: *.mydomain.com)
+#
+# Note: You must put the pattern in simple quotes when using the wildcard for the YAML
+# to be syntaxically correct.
+#
+# Definition: A `rule` is an object with the following keys: `domain`, `policy`
+# and `resources`.
+# - `domain` defines which domain or set of domains the rule applies to.
+# - `policy` is the policy to apply to resources. It must be either `allow` or `deny`.
+# - `resources` is a list of regular expressions that matches a set of resources to
+# apply the policy to.
+#
+# Note: Rules follow an order of priority defined as follows:
+# In each category (`any`, `groups`, `users`), the latest rules have the highest
+# priority. In other words, it means that if a given resource matches two rules in the
+# same category, the latest one overrides the first one.
+# Each category has also its own priority. That is, `users` has the highest priority, then
+# `groups` and `any` has the lowest priority. It means if two rules in different categories
+# match a given resource, the one in the category with the highest priority overrides the
+# other one.
+#
+access_control:
+ # Default policy can either be `allow` or `deny`.
+ # It is the policy applied to any resource if it has not been overriden
+ # in the `any`, `groups` or `users` category.
+ default_policy: deny
+
+ # The rules that apply to anyone.
+ # The value is a list of rules.
+ any:
+ - domain: '*.example.com'
+ policy: allow
+
+ # Group-based rules. The key is a group name and the value
+ # is a list of rules.
+ groups: {}
+
+ # User-based rules. The key is a user name and the value
+ # is a list of rules.
+ users: {}
+
+
+# Configuration of session cookies
+#
+# The session cookies identify the user once logged in.
+session:
+ # The secret to encrypt the session cookie.
+ secret: unsecure_password
+
+ # The time in ms before the cookie expires and session is reset.
+ expiration: 3600000 # 1 hour
+
+ # The inactivity time in ms before the session is reset.
+ inactivity: 300000 # 5 minutes
+
+ # The domain to protect.
+ # Note: the authenticator must also be in that domain. If empty, the cookie
+ # is restricted to the subdomain of the issuer.
+ domain: example.com
+
+ # The redis connection details
+ redis:
+ host: redis-service
+ port: 6379
+
+# Configuration of the authentication regulation mechanism.
+#
+# This mechanism prevents attackers from brute forcing the first factor.
+# It bans the user if too many attempts are done in a short period of
+# time.
+regulation:
+ # The number of failed login attempts before user is banned.
+ # Set it to 0 for disabling regulation.
+ max_retries: 3
+
+ # The length of time between login attempts before user is banned.
+ find_time: 120
+
+ # The length of time before a banned user can login again.
+ ban_time: 300
+
+# Configuration of the storage backend used to store data and secrets.
+#
+# You must use only an available configuration: local, mongo
+storage:
+ # The directory where the DB files will be saved
+ # local: /var/lib/authelia/store
+
+ # Settings to connect to mongo server
+ mongo:
+ url: mongodb://mongo-service/authelia
+
+# Configuration of the notification system.
+#
+# Notifications are sent to users when they require a password reset, a u2f
+# registration or a TOTP registration.
+# Use only an available configuration: filesystem, gmail
+notifier:
+ # For testing purpose, notifications can be sent in a file
+ # filesystem:
+ # filename: /tmp/authelia/notification.txt
+
+ # Use your email account to send the notifications. You can use an app password.
+ # List of valid services can be found here: https://nodemailer.com/smtp/well-known/
+ # email:
+ # username: authelia@gmail.com
+ # password: password
+ # sender: authelia@example.com
+ # service: gmail
+
+ # Use a SMTP server for sending notifications
+ smtp:
+ username: test
+ password: password
+ secure: false
+ host: 'mailcatcher-service'
+ port: 1025
+ sender: admin@example.com
diff --git a/example/kube/authelia/deployment.yml b/example/kube/authelia/deployment.yml
new file mode 100644
index 000000000..e09ab2748
--- /dev/null
+++ b/example/kube/authelia/deployment.yml
@@ -0,0 +1,33 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: authelia
+ namespace: authelia
+ labels:
+ app: authelia
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: authelia
+ template:
+ metadata:
+ labels:
+ app: authelia
+ spec:
+ containers:
+ - name: authelia
+ image: clems4ever/authelia:v3.7.0
+ ports:
+ - containerPort: 80
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/authelia
+ volumes:
+ - name: config-volume
+ configMap:
+ name: authelia-config
+ items:
+ - key: config.yml
+ path: config.yml
diff --git a/example/kube/authelia/ingress.yml b/example/kube/authelia/ingress.yml
new file mode 100644
index 000000000..ec3e9e3e9
--- /dev/null
+++ b/example/kube/authelia/ingress.yml
@@ -0,0 +1,22 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: authelia-ingress
+ namespace: authelia
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+spec:
+ tls:
+ - secretName: authelia-tls
+ hosts:
+ - login.kube.example.com
+ rules:
+ rules:
+ - host: login.kube.example.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: authelia-service
+ servicePort: 80
diff --git a/example/kube/authelia/service.yml b/example/kube/authelia/service.yml
new file mode 100644
index 000000000..21a6b79c2
--- /dev/null
+++ b/example/kube/authelia/service.yml
@@ -0,0 +1,13 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: authelia-service
+ namespace: authelia
+spec:
+ selector:
+ app: authelia
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 80
diff --git a/example/kube/authelia/ssl/tls.crt b/example/kube/authelia/ssl/tls.crt
new file mode 100644
index 000000000..ab0ba6200
--- /dev/null
+++ b/example/kube/authelia/ssl/tls.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaYCCQCJYt0VhOelKjANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZs
+b2dpbi5rdWJlLmV4YW1wbGUuY29tMB4XDTE4MDMwNDE1MTQzMVoXDTE5MDMwNDE1
+MTQzMVowITEfMB0GA1UEAwwWbG9naW4ua3ViZS5leGFtcGxlLmNvbTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIlUUppqDLXQCey+OqC4YIhsZFhus0S
+0OcNKBhMcUpKdaqtMf8n8mUtGCByUTf+LMBOyv/WrdcGH5pwlylyERPfDsUFF+5W
+LjhHGjMZVKWHOadb25HpO9IZUyyC+5PepfrHlxS5EhTQXymA7yjaXSizfH0uF9Le
+mF/RoqArtDfq/2/golcX5YkRt6FwbGrypHG0MuREyMN7H+XmKyC4Cwc1ECbROrWv
+C5491Fvw4fW0zWa6M1z56kzA+X7ZleiemiY0vm7hzlm8qztd449pJzweb/Gl2r7n
+LdFK+H2jbkn07Z//rwlm8Wlwtb3GLOTgisNv5jALpCDdgiSmUc+G+f0CAwEAATAN
+BgkqhkiG9w0BAQsFAAOCAQEAUm+gRqlUIGK3UKA+z1Si2EpFeOpSkfBbMjwWQAea
+yEY+XtUxQSWmbTx6Cp1miVwSp4ldd0nYVCpesv94FoI3ahktZGafcfviYgyCNPXl
+QBREQ3NU9TBLHOmCygL8JlzKLtKABKTiGsDahPmBaMogCbvswFqccZ1EtLRcrI48
+FFGS7K4ku561AK+WqFS8yxFKcudJSfmLeEZ0uNazEbh8kIgA5dXtapv6lBhPQ6nN
+MPZO321PWGysvj3RXDagYQOPBLX7NhnoFDCoeJKbPQ9lTLOAI0aQnpNoFZnoiWc3
+NNLboVSTPQ3jyumAAm7tXS/KWI5Samfp8Cgu7uqhPLdHYg==
+-----END CERTIFICATE-----
diff --git a/example/kube/authelia/ssl/tls.csr b/example/kube/authelia/ssl/tls.csr
new file mode 100644
index 000000000..70be16165
--- /dev/null
+++ b/example/kube/authelia/ssl/tls.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICZjCCAU4CAQAwITEfMB0GA1UEAwwWbG9naW4ua3ViZS5leGFtcGxlLmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIlUUppqDLXQCey+OqC4YIh
+sZFhus0S0OcNKBhMcUpKdaqtMf8n8mUtGCByUTf+LMBOyv/WrdcGH5pwlylyERPf
+DsUFF+5WLjhHGjMZVKWHOadb25HpO9IZUyyC+5PepfrHlxS5EhTQXymA7yjaXSiz
+fH0uF9LemF/RoqArtDfq/2/golcX5YkRt6FwbGrypHG0MuREyMN7H+XmKyC4Cwc1
+ECbROrWvC5491Fvw4fW0zWa6M1z56kzA+X7ZleiemiY0vm7hzlm8qztd449pJzwe
+b/Gl2r7nLdFK+H2jbkn07Z//rwlm8Wlwtb3GLOTgisNv5jALpCDdgiSmUc+G+f0C
+AwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCHO3wzf1jCOcTmo5NBnCendtEb/IAl
+aTBCW3b2+QDRQBGgpQb+JeDjHjIzp5FgzzJVF0XTA8H8jmR56lPTXNlWESzUh1oV
+on8QcbPi97nuhIEJNfk7K6gAiK11fULBoNUgI7PsRvAneo2PsCEHGtNsdoU4Ii7A
+CuUtKeeZCdbxVM2HradSJ9vvxRmOuIfsQJbUaH0F/Z3A0l0UQbp1AUOWFcJ6XDkX
+SgDkMCkXJV53SlwGZm8q6Hj8zwP7Tlk6Nkzcn3ZMDB76o92QSVoi1V07NrvRUvcc
+2/eekJBWfpzy1LkaovYGBow4ose8V5nMyH9feXlReCVk2aHYTYbEmQRj
+-----END CERTIFICATE REQUEST-----
diff --git a/example/kube/authelia/ssl/tls.key b/example/kube/authelia/ssl/tls.key
new file mode 100644
index 000000000..3654be488
--- /dev/null
+++ b/example/kube/authelia/ssl/tls.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAwiVRSmmoMtdAJ7L46oLhgiGxkWG6zRLQ5w0oGExxSkp1qq0x
+/yfyZS0YIHJRN/4swE7K/9at1wYfmnCXKXIRE98OxQUX7lYuOEcaMxlUpYc5p1vb
+kek70hlTLIL7k96l+seXFLkSFNBfKYDvKNpdKLN8fS4X0t6YX9GioCu0N+r/b+Ci
+VxfliRG3oXBsavKkcbQy5ETIw3sf5eYrILgLBzUQJtE6ta8Lnj3UW/Dh9bTNZroz
+XPnqTMD5ftmV6J6aJjS+buHOWbyrO13jj2knPB5v8aXavuct0Ur4faNuSfTtn/+v
+CWbxaXC1vcYs5OCKw2/mMAukIN2CJKZRz4b5/QIDAQABAoIBAQCkTwLqcFs6k/Om
+5ZBGoPgLs0pdmRGIR7lnIjphvihPUI8fIK9km8FIoY5+v2E/ey0SoFyrg1vi1Drg
+8RLtr60GXUxZsALd4jABzyM8Rd7erIA9xL8iUPsgx/Adhsk2D0P35v1VO4Ay/1ra
+fFVsBMq9DJJ6Ow1MmLjqtzfkSLigbRRSPwaS081oW570cg9ABc1Cpp9sdLjG2Il0
+Eyet0qe0fiJAOlnE+tMRls9AoGYLG61msb1OhkpKfaNdw6IolkSGQZDqqsf1cSE3
+I7ypsE0LLtDeCU/jsUMjDHBwerqTANUHO5Y4PZ3hSJN55p/IGEiUeAMYs+dqtFx8
+xc/KfV2BAoGBAP+2nR73QjWdqJ0A4IdRq811eZM+NTWbobKRSay+T3Ve8QcRqc41
+YXJYqRhX23me3p9CxHDMVoXYtWS1nlXnsOxk60idffEIf5tbjzEYi1dIdLoCfbVW
+dZS1ZsZh4GZ3If8e78R+9IBQ6+SFvsVocRXpkf6VHp6jB3mXH0XCyNXdAoGBAMJd
+CORqmdrmCbfZnn7G3cZ7kTS05inMkj/svtDb+tkcy2x+pfL9y+SfeAf+o5AGl6pN
+CsiiGJTVj/Wtic572zdT198UFyWjDrgYUMNzvL9430hnZkySF/E8f1XHD8Sb4P65
+CVGJeVKuEHTXcas9F3VYln/87WGDVrtVowO408KhAoGACFiSej9BtvRFW5J6wY/l
+1pfd9vNR00UYGvbo+61edIs7vKpT63oMiynfov7DGA4aYAJS3QeeT1IKYZYX69/b
+A2wrzbvuL17Co3RykPynF5syzBtmtPN0dP0StKjfJRkAUA5XbwdhvYpmmJfQ6SqG
+fluYO0HstOrHRK2tBJ7d5TUCgYBt9mDPihgdpkQdRfvL0gsq/kH6xdXqFBkyHWkf
+lTVonEfizAxrW3d9k1M/gqtbEr+/0/Kj7EFoAyN9ZX8v2Rb/SGo7hYxK+OOc9/TJ
+f7NryKDav9U6wPTWwNlx2DttiptSwbEp9lMzmdMpp7JhpSCefU44fwp2Pu5U8nBV
+7L2xwQKBgHln1Y4EZ9SQDA0jFiSUNoCkkUJFox8752FsPolCna3GmBAYJn8+Oumj
+VbLPJvJxHmXMn+JN+rxxFve/DxV1TJqsan5F7i5xp0Ck4rm+TU0ZxvHW75yNG8ER
+bNGkvo1dme3fh8YETH6sqePTtbJ04hMfNhn1/iu89s6+ft4cqnpk
+-----END RSA PRIVATE KEY-----
diff --git a/example/kube/bootstrap.sh b/example/kube/bootstrap.sh
new file mode 100755
index 000000000..bdcd20ac9
--- /dev/null
+++ b/example/kube/bootstrap.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+start_apps() {
+ # Create the test application pages
+ kubectl create configmap app1-page --namespace=authelia --from-file=apps/app1/index.html
+ kubectl create configmap app2-page --namespace=authelia --from-file=apps/app2/index.html
+ kubectl create configmap app-home-page --namespace=authelia --from-file=apps/app-home/index.html
+
+ # Create TLS certificate and key for HTTPS termination
+ kubectl create secret generic app1-tls --namespace=authelia --from-file=apps/app1/ssl/tls.key --from-file=apps/app1/ssl/tls.crt
+ kubectl create secret generic app2-tls --namespace=authelia --from-file=apps/app2/ssl/tls.key --from-file=apps/app2/ssl/tls.crt
+ kubectl create secret generic authelia-tls --namespace=authelia --from-file=authelia/ssl/tls.key --from-file=authelia/ssl/tls.crt
+
+ # Spawn the applications
+ kubectl apply -f apps
+ kubectl apply -f apps/app1
+ kubectl apply -f apps/app2
+ kubectl apply -f apps/app-home
+}
+
+start_ingress_controller() {
+ kubectl create configmap authelia-ingress-controller-config --namespace=authelia --from-file=ingress-controller/configs/nginx.tmpl
+ kubectl apply -f ingress-controller
+}
+
+start_authelia() {
+ kubectl create configmap authelia-config --namespace=authelia --from-file=authelia/configs/config.yml
+ kubectl apply -f authelia
+}
+
+# Spawn Redis and Mongo as backend for Authelia
+# Please note they are not configured to be distributed on several machines
+start_storage() {
+ kubectl apply -f storage
+}
+
+# Create a fake mailbox to catch emails sent by Authelia
+start_mailcatcher() {
+ kubectl apply -f mailcatcher
+}
+
+start_ldap() {
+ kubectl apply -f ldap
+}
+
+# Create the Authelia namespace in the cluster
+create_namespace() {
+ kubectl apply -f namespace.yml
+}
+
+create_namespace
+start_storage
+start_ldap
+start_mailcatcher
+start_ingress_controller
+start_authelia
+start_apps
diff --git a/example/kube/ingress-controller/configs/nginx.tmpl b/example/kube/ingress-controller/configs/nginx.tmpl
new file mode 100644
index 000000000..6106d6781
--- /dev/null
+++ b/example/kube/ingress-controller/configs/nginx.tmpl
@@ -0,0 +1,887 @@
+{{ $all := . }}
+{{ $servers := .Servers }}
+{{ $cfg := .Cfg }}
+{{ $IsIPV6Enabled := .IsIPV6Enabled }}
+{{ $healthzURI := .HealthzURI }}
+{{ $backends := .Backends }}
+{{ $proxyHeaders := .ProxySetHeaders }}
+{{ $addHeaders := .AddHeaders }}
+
+{{ if $cfg.EnableModsecurity }}
+load_module /etc/nginx/modules/ngx_http_modsecurity_module.so;
+{{ end }}
+
+{{ if $cfg.EnableOpentracing }}
+load_module /etc/nginx/modules/ngx_http_opentracing_module.so;
+{{ end }}
+
+{{ if (and $cfg.EnableOpentracing (ne $cfg.ZipkinCollectorHost "")) }}
+load_module /etc/nginx/modules/ngx_http_zipkin_module.so;
+{{ end }}
+
+daemon off;
+
+worker_processes {{ $cfg.WorkerProcesses }};
+pid /run/nginx.pid;
+{{ if ne .MaxOpenFiles 0 }}
+worker_rlimit_nofile {{ .MaxOpenFiles }};
+{{ end}}
+
+{{/* http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout */}}
+{{/* avoid waiting too long during a reload */}}
+worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ;
+
+events {
+ multi_accept on;
+ worker_connections {{ $cfg.MaxWorkerConnections }};
+ use epoll;
+}
+
+http {
+ {{/* we use the value of the header X-Forwarded-For to be able to use the geo_ip module */}}
+ {{ if $cfg.UseProxyProtocol }}
+ real_ip_header proxy_protocol;
+ {{ else }}
+ real_ip_header {{ $cfg.ForwardedForHeader }};
+ {{ end }}
+
+ real_ip_recursive on;
+ {{ range $trusted_ip := $cfg.ProxyRealIPCIDR }}
+ set_real_ip_from {{ $trusted_ip }};
+ {{ end }}
+
+ {{/* databases used to determine the country depending on the client IP address */}}
+ {{/* http://nginx.org/en/docs/http/ngx_http_geoip_module.html */}}
+ {{/* this is require to calculate traffic for individual country using GeoIP in the status page */}}
+ geoip_country /etc/nginx/GeoIP.dat;
+ geoip_city /etc/nginx/GeoLiteCity.dat;
+ geoip_proxy_recursive on;
+
+ {{ if $cfg.EnableVtsStatus }}
+ vhost_traffic_status_zone shared:vhost_traffic_status:{{ $cfg.VtsStatusZoneSize }};
+ vhost_traffic_status_filter_by_set_key {{ $cfg.VtsDefaultFilterKey }};
+ {{ end }}
+
+ sendfile on;
+
+ aio threads;
+ aio_write on;
+
+ tcp_nopush on;
+ tcp_nodelay on;
+
+ log_subrequest on;
+
+ reset_timedout_connection on;
+
+ keepalive_timeout {{ $cfg.KeepAlive }}s;
+ keepalive_requests {{ $cfg.KeepAliveRequests }};
+
+ client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }};
+ client_header_timeout {{ $cfg.ClientHeaderTimeout }}s;
+ large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }};
+ client_body_buffer_size {{ $cfg.ClientBodyBufferSize }};
+ client_body_timeout {{ $cfg.ClientBodyTimeout }}s;
+
+ http2_max_field_size {{ $cfg.HTTP2MaxFieldSize }};
+ http2_max_header_size {{ $cfg.HTTP2MaxHeaderSize }};
+
+ types_hash_max_size 2048;
+ server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }};
+ server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }};
+ map_hash_bucket_size {{ $cfg.MapHashBucketSize }};
+
+ proxy_headers_hash_max_size {{ $cfg.ProxyHeadersHashMaxSize }};
+ proxy_headers_hash_bucket_size {{ $cfg.ProxyHeadersHashBucketSize }};
+
+ variables_hash_bucket_size {{ $cfg.VariablesHashBucketSize }};
+ variables_hash_max_size {{ $cfg.VariablesHashMaxSize }};
+
+ underscores_in_headers {{ if $cfg.EnableUnderscoresInHeaders }}on{{ else }}off{{ end }};
+ ignore_invalid_headers {{ if $cfg.IgnoreInvalidHeaders }}on{{ else }}off{{ end }};
+
+ {{ if $cfg.EnableOpentracing }}
+ opentracing on;
+ {{ end }}
+
+ {{ if (and $cfg.EnableOpentracing (ne $cfg.ZipkinCollectorHost "")) }}
+ zipkin_collector_host {{ $cfg.ZipkinCollectorHost }};
+ zipkin_collector_port {{ $cfg.ZipkinCollectorPort }};
+ zipkin_service_name {{ $cfg.ZipkinServiceName }};
+ {{ end }}
+
+ include /etc/nginx/mime.types;
+ default_type text/html;
+
+ {{ if $cfg.EnableBrotli }}
+ brotli on;
+ brotli_comp_level {{ $cfg.BrotliLevel }};
+ brotli_types {{ $cfg.BrotliTypes }};
+ {{ end }}
+
+ {{ if $cfg.UseGzip }}
+ gzip on;
+ gzip_comp_level 5;
+ gzip_http_version 1.1;
+ gzip_min_length 256;
+ gzip_types {{ $cfg.GzipTypes }};
+ gzip_proxied any;
+ gzip_vary on;
+ {{ end }}
+
+ # Custom headers for response
+ {{ range $k, $v := $addHeaders }}
+ add_header {{ $k }} "{{ $v }}";
+ {{ end }}
+
+ server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }};
+
+ # disable warnings
+ uninitialized_variable_warn off;
+
+ # Additional available variables:
+ # $namespace
+ # $ingress_name
+ # $service_name
+ log_format upstreaminfo {{ if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ buildLogFormatUpstream $cfg }}';
+
+ {{/* map urls that should not appear in access.log */}}
+ {{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}}
+ map $request_uri $loggable {
+ {{ range $reqUri := $cfg.SkipAccessLogURLs }}
+ {{ $reqUri }} 0;{{ end }}
+ default 1;
+ }
+
+ {{ if $cfg.DisableAccessLog }}
+ access_log off;
+ {{ else }}
+ access_log {{ $cfg.AccessLogPath }} upstreaminfo if=$loggable;
+ {{ end }}
+ error_log {{ $cfg.ErrorLogPath }} {{ $cfg.ErrorLogLevel }};
+
+ {{ buildResolvers $cfg.Resolver }}
+
+ {{/* Whenever nginx proxies a request without a "Connection" header, the "Connection" header is set to "close" */}}
+ {{/* when making the target request. This means that you cannot simply use */}}
+ {{/* "proxy_set_header Connection $http_connection" for WebSocket support because in this case, the */}}
+ {{/* "Connection" header would be set to "" whenever the original request did not have a "Connection" header, */}}
+ {{/* which would mean no "Connection" header would be in the target request. Since this would deviate from */}}
+ {{/* normal nginx behavior we have to use this approach. */}}
+ # Retain the default nginx handling of requests without a "Connection" header
+ map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+ }
+
+ map {{ buildForwardedFor $cfg.ForwardedForHeader }} $the_real_ip {
+ {{ if $cfg.UseProxyProtocol }}
+ # Get IP address from Proxy Protocol
+ default $proxy_protocol_addr;
+ {{ else }}
+ default $remote_addr;
+ {{ end }}
+ }
+
+ # trust http_x_forwarded_proto headers correctly indicate ssl offloading
+ map $http_x_forwarded_proto $pass_access_scheme {
+ default $http_x_forwarded_proto;
+ '' $scheme;
+ }
+
+ map $http_x_forwarded_port $pass_server_port {
+ default $http_x_forwarded_port;
+ '' $server_port;
+ }
+
+ map $http_x_forwarded_host $best_http_host {
+ default $http_x_forwarded_host;
+ '' $this_host;
+ }
+
+ {{ if $all.IsSSLPassthroughEnabled }}
+ # map port {{ $all.ListenPorts.SSLProxy }} to 443 for header X-Forwarded-Port
+ map $pass_server_port $pass_port {
+ {{ $all.ListenPorts.SSLProxy }} 443;
+ default $pass_server_port;
+ }
+ {{ else }}
+ map $pass_server_port $pass_port {
+ 443 443;
+ default $pass_server_port;
+ }
+ {{ end }}
+
+ # Obtain best http host
+ map $http_host $this_host {
+ default $http_host;
+ '' $host;
+ }
+
+ {{ if $cfg.ComputeFullForwardedFor }}
+ # We can't use $proxy_add_x_forwarded_for because the realip module
+ # replaces the remote_addr too soon
+ map $http_x_forwarded_for $full_x_forwarded_for {
+ {{ if $all.Cfg.UseProxyProtocol }}
+ default "$http_x_forwarded_for, $proxy_protocol_addr";
+ '' "$proxy_protocol_addr";
+ {{ else }}
+ default "$http_x_forwarded_for, $realip_remote_addr";
+ '' "$realip_remote_addr";
+ {{ end}}
+ }
+ {{ end }}
+
+ server_name_in_redirect off;
+ port_in_redirect off;
+
+ ssl_protocols {{ $cfg.SSLProtocols }};
+
+ # turn on session caching to drastically improve performance
+ {{ if $cfg.SSLSessionCache }}
+ ssl_session_cache builtin:1000 shared:SSL:{{ $cfg.SSLSessionCacheSize }};
+ ssl_session_timeout {{ $cfg.SSLSessionTimeout }};
+ {{ end }}
+
+ # allow configuring ssl session tickets
+ ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }};
+
+ {{ if not (empty $cfg.SSLSessionTicketKey ) }}
+ ssl_session_ticket_key /etc/nginx/tickets.key;
+ {{ end }}
+
+ # slightly reduce the time-to-first-byte
+ ssl_buffer_size {{ $cfg.SSLBufferSize }};
+
+ {{ if not (empty $cfg.SSLCiphers) }}
+ # allow configuring custom ssl ciphers
+ ssl_ciphers '{{ $cfg.SSLCiphers }}';
+ ssl_prefer_server_ciphers on;
+ {{ end }}
+
+ {{ if not (empty $cfg.SSLDHParam) }}
+ # allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
+ ssl_dhparam {{ $cfg.SSLDHParam }};
+ {{ end }}
+
+ {{ if not $cfg.EnableDynamicTLSRecords }}
+ ssl_dyn_rec_size_lo 0;
+ {{ end }}
+
+ ssl_ecdh_curve {{ $cfg.SSLECDHCurve }};
+
+ {{ if .CustomErrors }}
+ # Custom error pages
+ proxy_intercept_errors on;
+ {{ end }}
+
+ {{ range $errCode := $cfg.CustomHTTPErrors }}
+ error_page {{ $errCode }} = @custom_{{ $errCode }};{{ end }}
+
+ proxy_ssl_session_reuse on;
+
+ {{ if $cfg.AllowBackendServerHeader }}
+ proxy_pass_header Server;
+ {{ end }}
+
+ {{ if not (empty $cfg.HTTPSnippet) }}
+ # Custom code snippet configured in the configuration configmap
+ {{ $cfg.HTTPSnippet }}
+ {{ end }}
+
+ {{ range $name, $upstream := $backends }}
+ {{ if eq $upstream.SessionAffinity.AffinityType "cookie" }}
+ upstream sticky-{{ $upstream.Name }} {
+ sticky hash={{ $upstream.SessionAffinity.CookieSessionAffinity.Hash }} name={{ $upstream.SessionAffinity.CookieSessionAffinity.Name }} httponly;
+
+ {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }}
+ keepalive {{ $cfg.UpstreamKeepaliveConnections }};
+ {{ end }}
+
+ {{ range $server := $upstream.Endpoints }}server {{ $server.Address | formatIP }}:{{ $server.Port }} max_fails={{ $server.MaxFails }} fail_timeout={{ $server.FailTimeout }};
+ {{ end }}
+
+ }
+
+ {{ end }}
+
+
+ upstream {{ $upstream.Name }} {
+ # Load balance algorithm; empty for round robin, which is the default
+ {{ if ne $cfg.LoadBalanceAlgorithm "round_robin" }}
+ {{ $cfg.LoadBalanceAlgorithm }};
+ {{ end }}
+
+ {{ if $upstream.UpstreamHashBy }}
+ hash {{ $upstream.UpstreamHashBy }} consistent;
+ {{ end }}
+
+ {{ if (gt $cfg.UpstreamKeepaliveConnections 0) }}
+ keepalive {{ $cfg.UpstreamKeepaliveConnections }};
+ {{ end }}
+
+ {{ range $server := $upstream.Endpoints }}server {{ $server.Address | formatIP }}:{{ $server.Port }} max_fails={{ $server.MaxFails }} fail_timeout={{ $server.FailTimeout }};
+ {{ end }}
+ }
+
+ {{ end }}
+
+ {{/* build the maps that will be use to validate the Whitelist */}}
+ {{ range $index, $server := $servers }}
+ {{ range $location := $server.Locations }}
+ {{ $path := buildLocation $location }}
+
+ {{ if isLocationAllowed $location }}
+ {{ if gt (len $location.Whitelist.CIDR) 0 }}
+
+ # Deny for {{ print $server.Hostname $path }}
+ geo $the_real_ip {{ buildDenyVariable (print $server.Hostname "_" $path) }} {
+ default 1;
+
+ {{ range $ip := $location.Whitelist.CIDR }}
+ {{ $ip }} 0;{{ end }}
+ }
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+ {{ range $rl := (filterRateLimits $servers ) }}
+ # Ratelimit {{ $rl.Name }}
+ geo $the_real_ip $whitelist_{{ $rl.ID }} {
+ default 0;
+ {{ range $ip := $rl.Whitelist }}
+ {{ $ip }} 1;{{ end }}
+ }
+
+ # Ratelimit {{ $rl.Name }}
+ map $whitelist_{{ $rl.ID }} $limit_{{ $rl.ID }} {
+ 0 {{ $cfg.LimitConnZoneVariable }};
+ 1 "";
+ }
+ {{ end }}
+
+ {{/* build all the required rate limit zones. Each annotation requires a dedicated zone */}}
+ {{/* 1MB -> 16 thousand 64-byte states or about 8 thousand 128-byte states */}}
+ {{ range $zone := (buildRateLimitZones $servers) }}
+ {{ $zone }}
+ {{ end }}
+
+ {{/* Build server redirects (from/to www) */}}
+ {{ range $hostname, $to := .RedirectServers }}
+ server {
+ {{ range $address := $all.Cfg.BindAddressIpv4 }}
+ listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
+ listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl;
+ {{ else }}
+ listen {{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
+ listen {{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl;
+ {{ end }}
+ {{ if $IsIPV6Enabled }}
+ {{ range $address := $all.Cfg.BindAddressIpv6 }}
+ listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
+ listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }};
+ {{ else }}
+ listen [::]:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }};
+ listen [::]:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }};
+ {{ end }}
+ {{ end }}
+ server_name {{ $hostname }};
+ return 301 $scheme://{{ $to }}$request_uri;
+ }
+ {{ end }}
+
+ {{ range $index, $server := $servers }}
+
+ ## start server {{ $server.Hostname }}
+ server {
+ server_name {{ $server.Hostname }} {{ $server.Alias }};
+ {{ template "SERVER" serverConfig $all $server }}
+
+ {{ if not (empty $cfg.ServerSnippet) }}
+ # Custom code snippet configured in the configuration configmap
+ {{ $cfg.ServerSnippet }}
+ {{ end }}
+
+ {{ template "CUSTOM_ERRORS" $all }}
+ }
+ ## end server {{ $server.Hostname }}
+
+ {{ end }}
+
+ # default server, used for NGINX healthcheck and access to nginx stats
+ server {
+ # Use the port {{ $all.ListenPorts.Status }} (random value just to avoid known ports) as default port for nginx.
+ # Changing this value requires a change in:
+ # https://github.com/kubernetes/ingress-nginx/blob/master/controllers/nginx/pkg/cmd/controller/nginx.go
+ listen {{ $all.ListenPorts.Status }} default_server reuseport backlog={{ $all.BacklogSize }};
+ {{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Status }} default_server reuseport backlog={{ $all.BacklogSize }};{{ end }}
+ set $proxy_upstream_name "-";
+
+ location {{ $healthzURI }} {
+ access_log off;
+ return 200;
+ }
+
+ location /nginx_status {
+ set $proxy_upstream_name "internal";
+
+ {{ if $cfg.EnableVtsStatus }}
+ vhost_traffic_status_display;
+ vhost_traffic_status_display_format html;
+ {{ else }}
+ access_log off;
+ stub_status on;
+ {{ end }}
+ }
+
+ location / {
+ {{ if .CustomErrors }}
+ proxy_set_header X-Code 404;
+ {{ end }}
+ set $proxy_upstream_name "upstream-default-backend";
+ proxy_pass http://upstream-default-backend;
+ }
+
+ {{ template "CUSTOM_ERRORS" $all }}
+ }
+}
+
+stream {
+ log_format log_stream {{ $cfg.LogFormatStream }};
+
+ {{ if $cfg.DisableAccessLog }}
+ access_log off;
+ {{ else }}
+ access_log {{ $cfg.AccessLogPath }} log_stream;
+ {{ end }}
+
+ error_log {{ $cfg.ErrorLogPath }};
+
+ # TCP services
+ {{ range $i, $tcpServer := .TCPBackends }}
+ upstream tcp-{{ $tcpServer.Port }}-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
+ {{ range $j, $endpoint := $tcpServer.Endpoints }}
+ server {{ $endpoint.Address }}:{{ $endpoint.Port }};
+ {{ end }}
+ }
+ server {
+ {{ range $address := $all.Cfg.BindAddressIpv4 }}
+ listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }};
+ {{ else }}
+ listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }};
+ {{ end }}
+ {{ if $IsIPV6Enabled }}
+ {{ range $address := $all.Cfg.BindAddressIpv6 }}
+ listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }};
+ {{ else }}
+ listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.ProxyProtocol.Decode }} proxy_protocol{{ end }};
+ {{ end }}
+ {{ end }}
+ proxy_timeout {{ $cfg.ProxyStreamTimeout }};
+ proxy_pass tcp-{{ $tcpServer.Port }}-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }};
+ {{ if $tcpServer.Backend.ProxyProtocol.Encode }}
+ proxy_protocol on;
+ {{ end }}
+ }
+
+ {{ end }}
+
+ # UDP services
+ {{ range $i, $udpServer := .UDPBackends }}
+ upstream udp-{{ $udpServer.Port }}-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
+ {{ range $j, $endpoint := $udpServer.Endpoints }}
+ server {{ $endpoint.Address }}:{{ $endpoint.Port }};
+ {{ end }}
+ }
+
+ server {
+ {{ range $address := $all.Cfg.BindAddressIpv4 }}
+ listen {{ $address }}:{{ $udpServer.Port }} udp;
+ {{ else }}
+ listen {{ $udpServer.Port }} udp;
+ {{ end }}
+ {{ if $IsIPV6Enabled }}
+ {{ range $address := $all.Cfg.BindAddressIpv6 }}
+ listen {{ $address }}:{{ $udpServer.Port }} udp;
+ {{ else }}
+ listen [::]:{{ $udpServer.Port }} udp;
+ {{ end }}
+ {{ end }}
+ proxy_responses 1;
+ proxy_timeout {{ $cfg.ProxyStreamTimeout }};
+ proxy_pass udp-{{ $udpServer.Port }}-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }};
+ }
+
+ {{ end }}
+}
+
+{{/* definition of templates to avoid repetitions */}}
+{{ define "CUSTOM_ERRORS" }}
+ {{ $proxySetHeaders := .ProxySetHeaders }}
+ {{ range $errCode := .Cfg.CustomHTTPErrors }}
+ location @custom_{{ $errCode }} {
+ internal;
+
+ proxy_intercept_errors off;
+
+ proxy_set_header X-Code {{ $errCode }};
+ proxy_set_header X-Format $http_accept;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Namespace $namespace;
+ proxy_set_header X-Ingress-Name $ingress_name;
+ proxy_set_header X-Service-Name $service_name;
+
+ rewrite (.*) / break;
+ proxy_pass http://upstream-default-backend;
+ }
+ {{ end }}
+{{ end }}
+
+{{/* CORS support from https://michielkalkman.com/snippets/nginx-cors-open-configuration.html */}}
+{{ define "CORS" }}
+ {{ $cors := .CorsConfig }}
+ # Cors Preflight methods needs additional options and different Return Code
+ if ($request_method = 'OPTIONS') {
+ add_header 'Access-Control-Allow-Origin' '{{ $cors.CorsAllowOrigin }}' always;
+ {{ if $cors.CorsAllowCredentials }} add_header 'Access-Control-Allow-Credentials' '{{ $cors.CorsAllowCredentials }}' always; {{ end }}
+ add_header 'Access-Control-Allow-Methods' '{{ $cors.CorsAllowMethods }}' always;
+ add_header 'Access-Control-Allow-Headers' '{{ $cors.CorsAllowHeaders }}' always;
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Content-Type' 'text/plain charset=UTF-8';
+ add_header 'Content-Length' 0;
+ return 204;
+ }
+
+ add_header 'Access-Control-Allow-Origin' '{{ $cors.CorsAllowOrigin }}' always;
+ {{ if $cors.CorsAllowCredentials }} add_header 'Access-Control-Allow-Credentials' '{{ $cors.CorsAllowCredentials }}' always; {{ end }}
+ add_header 'Access-Control-Allow-Methods' '{{ $cors.CorsAllowMethods }}' always;
+ add_header 'Access-Control-Allow-Headers' '{{ $cors.CorsAllowHeaders }}' always;
+
+{{ end }}
+
+{{/* definition of server-template to avoid repetitions with server-alias */}}
+{{ define "SERVER" }}
+ {{ $all := .First }}
+ {{ $server := .Second }}
+ {{ range $address := $all.Cfg.BindAddressIpv4 }}
+ listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}};
+ {{ else }}
+ listen {{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}};
+ {{ end }}
+ {{ if $all.IsIPV6Enabled }}
+ {{ range $address := $all.Cfg.BindAddressIpv6 }}
+ listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }};
+ {{ else }}
+ listen [::]:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }};
+ {{ end }}
+ {{ end }}
+ set $proxy_upstream_name "-";
+
+ {{/* Listen on {{ $all.ListenPorts.SSLProxy }} because port {{ $all.ListenPorts.HTTPS }} is used in the TLS sni server */}}
+ {{/* This listener must always have proxy_protocol enabled, because the SNI listener forwards on source IP info in it. */}}
+ {{ if not (empty $server.SSLCertificate) }}
+ {{ range $address := $all.Cfg.BindAddressIpv4 }}
+ listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol {{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
+ {{ else }}
+ listen {{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol {{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
+ {{ end }}
+ {{ if $all.IsIPV6Enabled }}
+ {{ range $address := $all.Cfg.BindAddressIpv6 }}
+ {{ if not (empty $server.SSLCertificate) }}listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
+ {{ else }}
+ {{ if not (empty $server.SSLCertificate) }}listen [::]:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
+ {{ end }}
+ {{ end }}
+ {{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
+ # PEM sha: {{ $server.SSLPemChecksum }}
+ ssl_certificate {{ $server.SSLCertificate }};
+ ssl_certificate_key {{ $server.SSLCertificate }};
+ {{ if not (empty $server.SSLFullChainCertificate)}}
+ ssl_trusted_certificate {{ $server.SSLFullChainCertificate }};
+ ssl_stapling on;
+ ssl_stapling_verify on;
+ {{ end }}
+ {{ end }}
+
+ {{ if (and (not (empty $server.SSLCertificate)) $all.Cfg.HSTS) }}
+ more_set_headers "Strict-Transport-Security: max-age={{ $all.Cfg.HSTSMaxAge }}{{ if $all.Cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }};{{ if $all.Cfg.HSTSPreload }} preload{{ end }}";
+ {{ end }}
+
+
+ {{ if not (empty $server.CertificateAuth.CAFileName) }}
+ # PEM sha: {{ $server.CertificateAuth.PemSHA }}
+ ssl_client_certificate {{ $server.CertificateAuth.CAFileName }};
+ ssl_verify_client {{ $server.CertificateAuth.VerifyClient }};
+ ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }};
+ {{ if not (empty $server.CertificateAuth.ErrorPage)}}
+ error_page 495 496 = {{ $server.CertificateAuth.ErrorPage }};
+ {{ end }}
+ {{ end }}
+
+ {{ if not (empty $server.ServerSnippet) }}
+ {{ $server.ServerSnippet }}
+ {{ end }}
+
+ {{ range $location := $server.Locations }}
+ {{ $path := buildLocation $location }}
+ {{ $authPath := buildAuthLocation $location }}
+
+ {{ if not (empty $location.Rewrite.AppRoot)}}
+ if ($uri = /) {
+ return 302 {{ $location.Rewrite.AppRoot }};
+ }
+ {{ end }}
+
+ {{ if not (empty $authPath) }}
+ location = {{ $authPath }} {
+ internal;
+ set $proxy_upstream_name "external-authentication";
+
+ proxy_pass_request_body off;
+ proxy_set_header Content-Length "";
+
+ {{ if not (empty $location.ExternalAuth.Method) }}
+ proxy_method {{ $location.ExternalAuth.Method }};
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Scheme $pass_access_scheme;
+ {{ end }}
+
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Original-Method $request_method;
+ proxy_set_header X-Auth-Request-Redirect $request_uri;
+ proxy_set_header X-Sent-From "nginx-ingress-controller";
+
+ proxy_ssl_server_name on;
+ proxy_pass_request_headers on;
+ client_max_body_size "{{ $location.Proxy.BodySize }}";
+ {{ if isValidClientBodyBufferSize $location.ClientBodyBufferSize }}
+ client_body_buffer_size {{ $location.ClientBodyBufferSize }};
+ {{ end }}
+
+ set $target {{ $location.ExternalAuth.URL }};
+ proxy_pass $target;
+ }
+ {{ end }}
+
+ location {{ $path }} {
+ {{ if $all.Cfg.EnableVtsStatus }}{{ if $location.VtsFilterKey }} vhost_traffic_status_filter_by_set_key {{ $location.VtsFilterKey }};{{ end }}{{ end }}
+
+ set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $all.Backends $location }}";
+
+ {{ $ing := (getIngressInformation $location.Ingress $path) }}
+ {{/* $ing.Metadata contains the Ingress metadata */}}
+ set $namespace "{{ $ing.Namespace }}";
+ set $ingress_name "{{ $ing.Rule }}";
+ set $service_name "{{ $ing.Service }}";
+
+ {{ if (or $location.Rewrite.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Rewrite.SSLRedirect)) }}
+ # enforce ssl on server side
+ if ($pass_access_scheme = http) {
+ return 301 https://$best_http_host$request_uri;
+ }
+ {{ end }}
+
+ {{ if $all.Cfg.EnableModsecurity }}
+ modsecurity on;
+
+ modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
+ {{ if $all.Cfg.EnableOWASPCoreRules }}
+ modsecurity_rules_file /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf;
+ {{ end }}
+ {{ end }}
+
+ {{ if isLocationAllowed $location }}
+ {{ if gt (len $location.Whitelist.CIDR) 0 }}
+ if ({{ buildDenyVariable (print $server.Hostname "_" $path) }}) {
+ return 403;
+ }
+ {{ end }}
+
+ port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }};
+
+ {{ if not (empty $authPath) }}
+ # this location requires authentication
+ auth_request {{ $authPath }};
+ auth_request_set $auth_cookie $upstream_http_set_cookie;
+ auth_request_set $redirect $upstream_http_redirect;
+ auth_request_set $user $upstream_http_remote_user;
+ proxy_set_header X-Forwarded-User $user;
+ auth_request_set $groups $upstream_http_remote_groups;
+ proxy_set_header Remote-Groups $groups;
+
+ add_header Set-Cookie $auth_cookie;
+ {{- range $idx, $line := buildAuthResponseHeaders $location }}
+ {{ $line }}
+ {{- end }}
+ {{ end }}
+
+ {{ if not (empty $location.ExternalAuth.SigninURL) }}
+ error_page 401 = {{ buildAuthSignURL $location.ExternalAuth.SigninURL }};
+ {{ end }}
+
+ {{/* if the location contains a rate limit annotation, create one */}}
+ {{ $limits := buildRateLimit $location }}
+ {{ range $limit := $limits }}
+ {{ $limit }}{{ end }}
+
+ {{ if $location.BasicDigestAuth.Secured }}
+ {{ if eq $location.BasicDigestAuth.Type "basic" }}
+ auth_basic "{{ $location.BasicDigestAuth.Realm }}";
+ auth_basic_user_file {{ $location.BasicDigestAuth.File }};
+ {{ else }}
+ auth_digest "{{ $location.BasicDigestAuth.Realm }}";
+ auth_digest_user_file {{ $location.BasicDigestAuth.File }};
+ {{ end }}
+ proxy_set_header Authorization "";
+ {{ end }}
+
+ {{ if $location.CorsConfig.CorsEnabled }}
+ {{ template "CORS" $location }}
+ {{ end }}
+
+ {{ if not (empty $location.Redirect.URL) }}
+ if ($uri ~* {{ $path }}) {
+ return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }};
+ }
+ {{ end }}
+
+ client_max_body_size "{{ $location.Proxy.BodySize }}";
+ {{ if isValidClientBodyBufferSize $location.ClientBodyBufferSize }}
+ client_body_buffer_size {{ $location.ClientBodyBufferSize }};
+ {{ end }}
+
+ {{/* By default use vhost as Host to upstream, but allow overrides */}}
+ {{ if not (empty $location.UpstreamVhost) }}
+ proxy_set_header Host "{{ $location.UpstreamVhost }}";
+ {{ else }}
+ proxy_set_header Host $best_http_host;
+ {{ end }}
+
+
+ # Pass the extracted client certificate to the backend
+ {{ if not (empty $server.CertificateAuth.CAFileName) }}
+ {{ if $server.CertificateAuth.PassCertToUpstream }}
+ proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
+ {{ else }}
+ proxy_set_header ssl-client-cert "";
+ {{ end }}
+ proxy_set_header ssl-client-verify $ssl_client_verify;
+ proxy_set_header ssl-client-dn $ssl_client_s_dn;
+ {{ else }}
+ proxy_set_header ssl-client-cert "";
+ proxy_set_header ssl-client-verify "";
+ proxy_set_header ssl-client-dn "";
+ {{ end }}
+
+ # Allow websocket connections
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+
+ proxy_set_header X-Real-IP $the_real_ip;
+ {{ if $all.Cfg.ComputeFullForwardedFor }}
+ proxy_set_header X-Forwarded-For $full_x_forwarded_for;
+ {{ else }}
+ proxy_set_header X-Forwarded-For $the_real_ip;
+ {{ end }}
+ proxy_set_header X-Forwarded-Host $best_http_host;
+ proxy_set_header X-Forwarded-Port $pass_port;
+ proxy_set_header X-Forwarded-Proto $pass_access_scheme;
+ proxy_set_header X-Original-URI $request_uri;
+ proxy_set_header X-Scheme $pass_access_scheme;
+
+ # Pass the original X-Forwarded-For
+ proxy_set_header X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
+
+ # mitigate HTTPoxy Vulnerability
+ # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
+ proxy_set_header Proxy "";
+
+ # Custom headers to proxied server
+ {{ range $k, $v := $all.ProxySetHeaders }}
+ proxy_set_header {{ $k }} "{{ $v }}";
+ {{ end }}
+
+ proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s;
+ proxy_send_timeout {{ $location.Proxy.SendTimeout }}s;
+ proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s;
+
+ {{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }}
+ proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }};
+ {{ else }}
+ proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }} {{ $location.Proxy.ProxyRedirectTo }};
+ {{ end }}
+ proxy_buffering off;
+ proxy_buffer_size "{{ $location.Proxy.BufferSize }}";
+ proxy_buffers 4 "{{ $location.Proxy.BufferSize }}";
+ proxy_request_buffering "{{ $location.Proxy.RequestBuffering }}";
+
+ proxy_http_version 1.1;
+
+ proxy_cookie_domain {{ $location.Proxy.CookieDomain }};
+ proxy_cookie_path {{ $location.Proxy.CookiePath }};
+
+ # In case of errors try the next upstream server before returning an error
+ proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }};
+
+ {{/* rewrite only works if the content is not compressed */}}
+ {{ if $location.Rewrite.AddBaseURL }}
+ proxy_set_header Accept-Encoding "";
+ {{ end }}
+
+ {{/* Add any additional configuration defined */}}
+ {{ $location.ConfigurationSnippet }}
+
+ {{ if not (empty $all.Cfg.LocationSnippet) }}
+ # Custom code snippet configured in the configuration configmap
+ {{ $all.Cfg.LocationSnippet }}
+ {{ end }}
+
+ {{/* if we are sending the request to a custom default backend, we add the required headers */}}
+ {{ if (hasPrefix $location.Backend "custom-default-backend-") }}
+ proxy_set_header X-Code 503;
+ proxy_set_header X-Format $http_accept;
+ proxy_set_header X-Namespace $namespace;
+ proxy_set_header X-Ingress-Name $ingress_name;
+ proxy_set_header X-Service-Name $service_name;
+ {{ end }}
+
+
+ {{ if not (empty $location.Backend) }}
+ {{ buildProxyPass $server.Hostname $all.Backends $location }}
+ {{ else }}
+ # No endpoints available for the request
+ return 503;
+ {{ end }}
+ {{ else }}
+ # Location denied. Reason: {{ $location.Denied }}
+ return 503;
+ {{ end }}
+ }
+
+ {{ end }}
+
+ {{ if eq $server.Hostname "_" }}
+ # health checks in cloud providers require the use of port {{ $all.ListenPorts.HTTP }}
+ location {{ $all.HealthzURI }} {
+ access_log off;
+ return 200;
+ }
+
+ # this is required to avoid error if nginx is being monitored
+ # with an external software (like sysdig)
+ location /nginx_status {
+ allow 127.0.0.1;
+ {{ if $all.IsIPV6Enabled }}allow ::1;{{ end }}
+ deny all;
+
+ access_log off;
+ stub_status on;
+ }
+
+ {{ end }}
+
+{{ end }}
diff --git a/example/kube/ingress-controller/default-backend.yml b/example/kube/ingress-controller/default-backend.yml
new file mode 100644
index 000000000..8bb33965e
--- /dev/null
+++ b/example/kube/ingress-controller/default-backend.yml
@@ -0,0 +1,48 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: default-http-backend
+ labels:
+ app: default-http-backend
+ namespace: authelia
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: default-http-backend
+ spec:
+ terminationGracePeriodSeconds: 60
+ containers:
+ - name: default-http-backend
+ image: gcr.io/google_containers/defaultbackend:1.4
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ scheme: HTTP
+ initialDelaySeconds: 30
+ timeoutSeconds: 5
+ ports:
+ - containerPort: 8080
+ resources:
+ limits:
+ cpu: 10m
+ memory: 20Mi
+ requests:
+ cpu: 10m
+ memory: 20Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: default-http-backend
+ namespace: authelia
+ labels:
+ app: default-http-backend
+spec:
+ ports:
+ - port: 80
+ targetPort: 8080
+ selector:
+ app: default-http-backend
diff --git a/example/kube/ingress-controller/deployment.yml b/example/kube/ingress-controller/deployment.yml
new file mode 100644
index 000000000..083c06204
--- /dev/null
+++ b/example/kube/ingress-controller/deployment.yml
@@ -0,0 +1,54 @@
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: nginx-ingress-controller-external
+ namespace: authelia
+ labels:
+ k8s-app: nginx-ingress-controller-external
+spec:
+ replicas: 1
+ revisionHistoryLimit: 0
+ template:
+ metadata:
+ labels:
+ k8s-app: nginx-ingress-controller-external
+ name: nginx-ingress-controller-external
+ annotations:
+ prometheus.io/port: '10254'
+ prometheus.io/scrape: 'true'
+ spec:
+ terminationGracePeriodSeconds: 60
+ containers:
+ - image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.9.0-beta.19
+ name: nginx-ingress-controller-external
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 80
+ - containerPort: 443
+ volumeMounts:
+ - mountPath: /etc/nginx/template
+ name: nginx-template-volume
+ readOnly: true
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ args:
+ - /nginx-ingress-controller
+ - --ingress-class=nginx
+ - --election-id=ingress-controller-leader-external
+ - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
+ - --configmap=$(POD_NAMESPACE)/authelia-ingress-controller-config
+ volumes:
+ - name: nginx-template-volume
+ configMap:
+ name: authelia-ingress-controller-config
+ items:
+ - key: nginx.tmpl
+ path: nginx.tmpl
diff --git a/example/kube/ingress-controller/service.yml b/example/kube/ingress-controller/service.yml
new file mode 100644
index 000000000..6149ce6f1
--- /dev/null
+++ b/example/kube/ingress-controller/service.yml
@@ -0,0 +1,18 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx-ingress-controller-external-service
+ namespace: authelia
+ labels:
+ k8s-app: nginx-ingress-controller-external
+spec:
+ selector:
+ k8s-app: nginx-ingress-controller-external
+ ports:
+ - port: 80
+ name: http
+ - port: 443
+ name: https
+ externalIPs:
+ - 192.168.39.26
diff --git a/example/kube/ldap/Dockerfile b/example/kube/ldap/Dockerfile
new file mode 100644
index 000000000..c7e70e0c0
--- /dev/null
+++ b/example/kube/ldap/Dockerfile
@@ -0,0 +1,12 @@
+FROM clems4ever/openldap
+
+ENV SLAPD_ORGANISATION=MyCompany
+ENV SLAPD_DOMAIN=example.com
+ENV SLAPD_PASSWORD=password
+ENV SLAPD_CONFIG_PASSWORD=password
+ENV SLAPD_ADDITIONAL_MODULES=memberof
+ENV SLAPD_ADDITIONAL_SCHEMAS=openldap
+ENV SLAPD_FORCE_RECONFIGURE=true
+
+ADD base.ldif /etc/ldap.dist/prepopulate/base.ldif
+ADD access.rules /etc/ldap.dist/prepopulate/access.rules
diff --git a/example/kube/ldap/deployment.yml b/example/kube/ldap/deployment.yml
new file mode 100644
index 000000000..fea1cefd2
--- /dev/null
+++ b/example/kube/ldap/deployment.yml
@@ -0,0 +1,23 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: ldap
+ namespace: authelia
+ labels:
+ app: ldap
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: ldap
+ template:
+ metadata:
+ labels:
+ app: ldap
+ spec:
+ containers:
+ - name: ldap
+ image: clems4ever/authelia-test-ldap
+ ports:
+ - containerPort: 389
diff --git a/example/kube/ldap/service.yml b/example/kube/ldap/service.yml
new file mode 100644
index 000000000..09f599258
--- /dev/null
+++ b/example/kube/ldap/service.yml
@@ -0,0 +1,12 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: ldap-service
+ namespace: authelia
+spec:
+ selector:
+ app: ldap
+ ports:
+ - protocol: TCP
+ port: 389
diff --git a/example/kube/mailcatcher/deployment.yml b/example/kube/mailcatcher/deployment.yml
new file mode 100644
index 000000000..958c94110
--- /dev/null
+++ b/example/kube/mailcatcher/deployment.yml
@@ -0,0 +1,25 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: mailcatcher
+ namespace: authelia
+ labels:
+ app: mailcatcher
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mailcatcher
+ template:
+ metadata:
+ labels:
+ app: mailcatcher
+ spec:
+ containers:
+ - name: mailcatcher
+ image: schickling/mailcatcher
+ ports:
+ - containerPort: 1025
+ - containerPort: 1080
+
diff --git a/example/kube/mailcatcher/ingress.yml b/example/kube/mailcatcher/ingress.yml
new file mode 100644
index 000000000..ce8131def
--- /dev/null
+++ b/example/kube/mailcatcher/ingress.yml
@@ -0,0 +1,17 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: mailcatcher-ingress
+ namespace: authelia
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+spec:
+ rules:
+ - host: mail.kube.example.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: mailcatcher-service
+ servicePort: 1080
diff --git a/example/kube/mailcatcher/service.yml b/example/kube/mailcatcher/service.yml
new file mode 100644
index 000000000..f51d7dd98
--- /dev/null
+++ b/example/kube/mailcatcher/service.yml
@@ -0,0 +1,16 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: mailcatcher-service
+ namespace: authelia
+spec:
+ selector:
+ app: mailcatcher
+ ports:
+ - protocol: TCP
+ port: 1080
+ name: ui
+ - protocol: TCP
+ port: 1025
+ name: smtp
diff --git a/example/kube/namespace.yml b/example/kube/namespace.yml
new file mode 100644
index 000000000..9dfdb53f8
--- /dev/null
+++ b/example/kube/namespace.yml
@@ -0,0 +1,5 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: authelia
diff --git a/example/kube/storage/mongo.yml b/example/kube/storage/mongo.yml
new file mode 100644
index 000000000..eb0d053a8
--- /dev/null
+++ b/example/kube/storage/mongo.yml
@@ -0,0 +1,48 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: mongo
+ namespace: authelia
+ labels:
+ app: mongo
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: mongo
+ template:
+ metadata:
+ labels:
+ app: mongo
+ spec:
+ containers:
+ - name: mongo
+ image: mongo:3.4
+ ports:
+ - containerPort: 27017
+ volumeMounts:
+ - name: data-volume
+ mountPath: /data/db
+ - name: config-volume
+ mountPath: /data/configdb
+ volumes:
+ - name: data-volume
+ hostPath:
+ path: /data/storage/mongo/data
+ - name: config-volume
+ hostPath:
+ path: /data/storage/mongo/config
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: mongo-service
+ namespace: authelia
+spec:
+ selector:
+ app: mongo
+ ports:
+ - protocol: TCP
+ port: 27017
diff --git a/example/kube/storage/redis.yml b/example/kube/storage/redis.yml
new file mode 100644
index 000000000..e9f12f8e7
--- /dev/null
+++ b/example/kube/storage/redis.yml
@@ -0,0 +1,36 @@
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: redis
+ namespace: authelia
+ labels:
+ app: redis
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: redis
+ template:
+ metadata:
+ labels:
+ app: redis
+ spec:
+ containers:
+ - name: redis
+ image: redis:3.2.11-alpine
+ ports:
+ - containerPort: 6379
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: redis-service
+ namespace: authelia
+spec:
+ selector:
+ app: redis
+ ports:
+ - protocol: TCP
+ port: 6379