mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-07-28 17:21:11 +00:00
Add Ingress TLS chapter
This commit is contained in:
33
k8s/certbot.yaml
Normal file
33
k8s/certbot.yaml
Normal 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
traefik-v1.yaml
|
||||
traefik-v2.yaml
|
||||
403
slides/k8s/ingress-tls.md
Normal file
403
slides/k8s/ingress-tls.md
Normal 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*
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user