Add Ingress TLS chapter

This commit is contained in:
Jerome Petazzoni
2020-09-15 17:44:05 +02:00
parent 4fc81209d4
commit c1020f24b1
8 changed files with 447 additions and 2 deletions

33
k8s/certbot.yaml Normal file
View File

@@ -0,0 +1,33 @@
kind: Service
apiVersion: v1
metadata:
name: certbot
spec:
ports:
- port: 80
protocol: TCP
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: certbot
spec:
rules:
- http:
paths:
- path: /.well-known/acme-challenge/
backend:
serviceName: certbot
servicePort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
name: certbot
subsets:
- addresses:
- ip: A.B.C.D
ports:
- port: 8000
protocol: TCP

View File

@@ -3,6 +3,10 @@ kind: Ingress
metadata:
name: whatever
spec:
#tls:
#- secretName: whatever.A.B.C.D.nip.io
# hosts:
# - whatever.A.B.C.D.nip.io
rules:
- host: whatever.A.B.C.D.nip.io
http:

View File

@@ -50,8 +50,10 @@ spec:
- --api.insecure
- --log.level=INFO
- --metrics.prometheus
- --providers.kubernetescrd
- --providers.kubernetesingress
- --entrypoints.http.Address=:80
- --entrypoints.https.Address=:443
- --entrypoints.https.http.tls.certResolver=default
---
kind: Service
apiVersion: v1

View File

@@ -1 +1 @@
traefik-v1.yaml
traefik-v2.yaml

403
slides/k8s/ingress-tls.md Normal file
View File

@@ -0,0 +1,403 @@
# Ingress and TLS certificates
- Most ingress controllers support TLS connections
(in a way that is standard across controllers)
- The TLS key and certificate are stored in a Secret
- The Secret is then referenced in the Ingress resource:
```yaml
spec:
tls:
- secretName: XXX
hosts:
- YYY
rules:
- ZZZ
```
---
## Obtaining a certificate
- In the next section, we will need a TLS key and certificate
- These usually come in [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) format:
```
-----BEGIN CERTIFICATE-----
MIIDATCCAemg...
...
-----END CERTIFICATE-----
```
- We will see how to generate a self-signed certificate
(easy, fast, but won't be recognized by web browsers)
- We will also see how to obtain a certificate from [Let's Encrypt](https://letsencrypt.org/)
(requires the cluster to be reachable through a domain name)
---
class: extra-details
## In production ...
- A very popular option is to use the [cert-manager](https://cert-manager.io/docs/) operator
- It's a flexible, modular approach to automated certificate management
- For simplicity, in this section, we will use [certbot](https://certbot.eff.org/)
- The method shown here works well for one-time certs, but lacks:
- automation
- renewal
---
## Which domain to use
- If you're doing this in a training:
*the instructor will tell you what to use*
- If you're doing this on your own Kubernetes cluster:
*you should use a domain that points to your cluster*
- More precisely:
*you should use a domain that points to your ingress controller*
- If you don't have a domain name, you can use [nip.io](https://nip.io/)
(if your ingress controller is on 1.2.3.4, you can use `whatever.1.2.3.4.nip.io`)
---
## Setting `$DOMAIN`
- We will use `$DOMAIN` in the following section
- Let's set it now
.exercise[
- Set the `DOMAIN` environment variable:
```bash
export DOMAIN=...
```
]
---
## Method 1, self-signed certificate
- Thanks to `openssl`, generating a self-signed cert is just one command away!
.exercise[
- Generate a key and certificate:
```bash
openssl req \
-newkey rsa -nodes -keyout privkey.pem \
-x509 -days 30 -subj /CN=$DOMAIN/ -out cert.pem
```
]
This will create two files, `privkey.pem` and `cert.pem`.
---
## Method 2, Let's Encrypt with certbot
- `certbot` is an [ACME](https://tools.ietf.org/html/rfc8555) client
(Automatic Certificate Management Environment)
- We can use it to obtain certificates from Let's Encrypt
- It needs to listen to port 80
(to complete the [HTTP-01 challenge](https://letsencrypt.org/docs/challenge-types/))
- If port 80 is already taken by our ingress controller, see method 3
---
class: extra-details
## HTTP-01 challenge
- `certbot` contacts Let's Encrypt, asking for a cert for `$DOMAIN`
- Let's Encrypt gives a token to `certbot`
- Let's Encrypt then tries to access the following URL:
`http://$DOMAIN/.well-known/acme-challenge/<token>`
- That URL needs to be routed to `certbot`
- Once Let's Encrypt gets the response from `certbot`, it issues the certificate
---
## Running certbot
- There is a very convenient container image, `certbot/certbot`
- Let's use a volume to get easy access to the generated key and certificate
.exercise[
- Obtain a certificate from Let's Encrypt:
```bash
EMAIL=your.address@example.com
docker run --rm -p 80:80 -v $PWD/letsencrypt:/etc/letsencrypt \
certbot/certbot certonly \
-m $EMAIL \
--standalone --agree-tos -n \
--domain $DOMAIN \
--test-cert
```
]
This will get us a "staging" certificate.
Remove `--test-cert` to obtain a *real* certificate.
---
## Copying the key and certificate
- If everything went fine:
- the key and certificate files are in `letsencrypt/live/$DOMAIN`
- they are owned by `root`
.exercise[
- Grant ourselves permissions on these files:
```bash
sudo chown -R $USER letsencrypt
```
- Copy the certificate and key to the current directory:
```bash
cp letsencrypt/live/test/{cert,privkey}.pem .
```
]
---
## Method 3, certbot with Ingress
- Sometimes, we can't simply listen to port 80:
- we might already have an ingress controller there
- our nodes might be on an internal network
- But we can define an Ingress to route the HTTP-01 challenge to `certbot`!
- Our Ingress needs to route all requests to `/.well-known/acme-challenge` to `certbot`
- There are at least two ways to do that:
- run `certbot` in a Pod (and extract the cert+key when it's done)
- run `certbot` in a container on a node (and manually route traffic to it)
- We're going to use the second option
(mostly because it will give us an excuse to tinker with Endpoints resources!)
---
## The plan
- We need the following resources:
- an Endpoints¹ listing a hard-coded IP address and port
<br/>(where our `certbot` container will be listening)
- a Service corresponding to that Endpoints
- an Ingress sending requests to `/.well-known/acme-challenge/*` to that Service
<br/>(we don't even need to include a domain name in it)
- Then we need to start `certbot` so that it's listening on the right address+port
.footnote[¹Endpoints is always plural, because even a single resource is a list of endpoints.]
---
## Creating resources
- We prepared a YAML file to create the three resources
- However, the Endpoints needs to be adapted to put the current node's address
.exercise[
- Edit `~/containers.training/k8s/certbot.yaml`
(replace `A.B.C.D` with the current node's address)
- Create the resources:
```bash
kubectl apply -f ~/containers.training/k8s/certbot.yaml
```
]
---
## Obtaining the certificate
- Now we can run `certbot`, listening on the port listed in the Endpoints
(i.e. 8000)
.exercise[
- Run `certbot`:
```bash
EMAIL=your.address@example.com
docker run --rm -p 8000:80 -v $PWD/letsencrypt:/etc/letsencrypt \
certbot/certbot certonly \
-m $EMAIL \
--standalone --agree-tos -n \
--domain $DOMAIN \
--test-cert
```
]
This is using the staging environment.
Remove `--test-cert` to get a production certificate.
---
## Copying the certificate
- Just like in the previous method, the certificate is in `letsencrypt/live/$DOMAIN`
(and owned by root)
.exercise[
- Grand ourselves permissions on these files:
```bash
sudo chown -R $USER letsencrypt
```
- Copy the certificate and key to the current directory:
```bash
cp letsencrypt/live/$DOMAIN/{cert,privkey}.pem .
```
]
---
## Creating the Secret
- We now have two files:
- `privkey.pem` (the private key)
- `cert.pem` (the certificate)
- We can create a Secret to hold them
.exercise[
- Create the Secret:
```bash
kubectl create secret tls $DOMAIN --cert=cert.pem --key=privkey.pem
```
]
---
## Ingress with TLS
- To enable TLS for an Ingress, we need to add a `tls` section to the Ingress:
```yaml
spec:
tls:
- secretName: DOMAIN
hosts:
- DOMAIN
rules: ...
```
- The list of hosts will be used by the ingress controller
(to know which certificate to use with [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication))
- Of course, the name of the secret can be different
(here, for clarity and convenience, we set it to match the domain)
---
class: extra-details
## About the ingress controller
- Many ingress controllers can use different "stores" for keys and certificates
- Our ingress controller needs to be configured to use secrets
(as opposed to, e.g., obtain certificates directly with Let's Encrypt)
---
## Using the certificate
.exercise[
- Edit the Ingress manifest, `~/container.training/k8s/ingress.yaml`
- Uncomment the `tls` section
- Update the `secretName` and `hosts` list
- Create or update the Ingress:
```bash
kubectl apply -f ~/container.training/k8s/ingress.yaml
```
- Check that the URL now works over `https`
(it might take a minute to be picked up by the ingress controller)
]
---
## Discussion
*To repeat something mentioned earlier ...*
- The methods presented here are for *educational purpose only*
- In most production scenarios, the certificates will be obtained automatically
- A very popular option is to use the [cert-manager](https://cert-manager.io/docs/) operator
???
:EN:- Ingress and TLS
:FR:- Certificats TLS et *ingress*

View File

@@ -74,6 +74,7 @@ content:
-
- k8s/namespaces.md
- k8s/ingress.md
#- k8s/ingress-tls.md
#- k8s/kustomize.md
#- k8s/helm-intro.md
#- k8s/helm-chart-format.md

View File

@@ -77,6 +77,7 @@ content:
- k8s/kubectlproxy.md
-
- k8s/ingress.md
- k8s/ingress-tls.md
- k8s/kustomize.md
- k8s/helm-intro.md
- k8s/helm-chart-format.md

View File

@@ -75,6 +75,7 @@ content:
-
- k8s/namespaces.md
- k8s/ingress.md
#- k8s/ingress-tls.md
- k8s/kustomize.md
- k8s/helm-intro.md
- k8s/helm-chart-format.md