description: Comment gérer TLS dans un conteneur statique
---
Cet article est motivé par un problème rencontré sur l'interface Guichet de Deuxfleurs.
Au moment d'intéragir avec l'API S3, on a cette erreur suivante :
```
Impossible d'effectuer la modification.
Put "https://garage.deuxfleurs.fr/bottin-pictures/d1e3607f-4b9c-45fa-9e11-ddf8eef48676-thumb": tls: failed to verify certificate: x509: certificate signed by unknown authority
```
Pour comprendre le problème, partons de cet exemple basique en Go :
```go
package main
import (
"net/http"
"log"
)
func main() {
_, err := http.Get("https://deuxfleurs.fr")
if err != nil {
log.Fatal(err)
}
log.Println("Success")
}
```
Ce bloc de code fait une requête HTTPS vers deuxfleurs.fr et log l'erreur si il y en a une, sinon il affiche `Success`.
On va le compiler en statique pour ne pas dépendre de la lib C locale :
```bash
CGO_ENABLED=0 go build main.go
```
Ce qui nous donne bien un executable statique :
```
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=ctUIsYsrR2BtpR58vqRU/TI93T6hZlDxMBNqsplsv/QYD-xJaEDyWB0QaX6tSS/cDyvoEdvE3kZpdq8yCs3, with debug_info, not stripped
```
Si on le fait tourner en local, tout se passe bien :
```
2024/06/09 15:54:59 Success
```
Maintenant, puisqu'on nous avons un binaire statique qui ne dépend de rien, on va vouloir créer un conteneur Docker qui ne contient que ce fichier (alors que souvent on embarque une distribution de base comme Debian ou Alpine). Pour se faire, on écrit un Dockerfile avec deux étapes : une qui a les outils pour le build, et une qui ne contient que notre binaire :
```Dockerfile
FROM golang as builder
COPY main.go .
RUN CGO_ENABLED=0 go build -o /main main.go
FROM scratch
COPY --from=builder /main /main
CMD [ "/main" ]
```
On construit & lance le conteneur et... on reproduit l'erreur !
```
$ docker build -t tls-static-go .
$ docker run --rm -it tls-static-go
2024/06/09 14:02:37 Get "https://deuxfleurs.fr": tls: failed to verify certificate: x509: certificate signed by unknown authority
```
Pour comprendre la différence de comportement entre l'intérieur du conteneur et l'extérieur, on peut faire appel à `strace` et voir les fichiers que notre binaire essaie d'ouvrir :
En général ce fichier est fourni par les distributions, qui à ma connaissance majoritairement se basent sur le travail de Mozilla.
On peut en lire plus à propos de la gestion de ces racines de confiance sur la page dédiée du wiki de Mozilla : [CA/FAQ](https://wiki.mozilla.org/CA/FAQ)
Une façon de faire est donc de copier le fichier de notre builder dans notre conteneur final :
Pour visualiser le contenu d'une image docker, on peut utiliser l'outil [dive](https://github.com/wagoodman/dive) :
![Dive](/assets/images/posts/dive.png)
À gauche, puis à droite, les commandes sont :
```bash
dive tls-static-go
dive tls-static-go-2
```
On voit bien l'ajout du chemin `/etc/ssl/certs/ca-certificates.crt` à droite.
Maintenant, le problème avec les `Dockerfile`, c'est que ce ne sont pas du tout des builds reproductibles ni précis : on ne sait pas quelles versions ou dépendances on embarque. Donc on veut plutôt construire nos conteneurs avec NixOS pour plus de contrôle.
Pour faciliter le packaging Nix, on va générer un fichier `go.mod` :