Gitfed
bastien-mrq/gitfed/ Commits/ e790d7b

Add a from-scratch install guide; fix a real ErrImageNeverPull bug

INSTALL.md: step-by-step walkthrough for someone with a bare VPS and nothing installed yet (k3s, cert-manager, DNS, gitfed itself) — deploy/ k8s/README.md assumes those already exist (e.g. via ess-helm), which isn't everyone's starting point. Root-caused a real bug this surfaced: deploy/k8s/README.md's manual build instructions said `docker build -t gitfed:latest`, but deployment.yaml is checked into git pinned to whatever version deploy/update.sh last set (e.g. gitfed:1.2.4) with imagePullPolicy: Never — building `:latest` instead of the exact pinned tag leaves the pod in ErrImageNeverPull forever, since there's nowhere else k3s will look. Fixed by building with `-t gitfed:$(cat VERSION)` instead, which always matches. Also moved the manual build from "your workstation" to "the VPS itself" in both docs, matching what deploy/update.sh already does — building on an ARM workstation (e.g. Apple Silicon) for an x86_64 VPS produces an image that just won't run there.

bastien-mrq 2026-07-29 10:24 commit e790d7bb6d6766f4799b29802859df2d8ea94775 parent 3b6ba277cea6de227343e96bc32445aec619210a
4 files changed +296 −12
A INSTALL.md +255 −0
M README.fr.md +8 −2
M README.md +8 −2
M deploy/k8s/README.md +25 −8
INSTALL.md
diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..4940f08 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,255 @@ +# Installer gitfed sur un VPS, de zéro + +Guide pas à pas pour monter une instance gitfed sur un VPS tout neuf, sans +rien présupposer d'installé au préalable. Si k3s/Traefik/cert-manager +tournent déjà chez toi (par exemple à côté d'ess-helm), regarde plutôt +[`deploy/k8s/README.md`](deploy/k8s/README.md), qui suppose exactement ça. + +Compte environ 15-20 minutes, DNS mis à part (la propagation peut prendre +un peu de temps). + +## Ce qu'il te faut avant de commencer + +- Un VPS avec une IP publique (ce guide suppose Ubuntu/Debian), accès root + ou sudo. +- Un nom de domaine (ou sous-domaine) que tu contrôles, pour pouvoir y + ajouter un enregistrement DNS. +- Les ports suivants ouverts vers le VPS (pare-feu du fournisseur/`ufw`) : + `22` (ton propre SSH), `80` et `443` (Traefik/HTTPS), `2222` (git+ssh de + gitfed — volontairement pas sur le port 22 pour ne pas entrer en + conflit avec le SSH du VPS lui-même). +- Docker installé **sur le VPS lui-même**, pas sur ta machine de travail — + voir pourquoi à l'étape 4. + +## 1. Installer k3s + +Sur le VPS : + +```sh +curl -sfL https://get.k3s.io | sh - +sudo k3s kubectl get nodes # doit afficher un nœud "Ready" +``` + +k3s embarque Traefik comme contrôleur d'ingress par défaut — rien à +installer en plus pour ça. Pour éviter de taper `sudo k3s kubectl` à +chaque fois, exporte `KUBECONFIG` ou copie +`/etc/rancher/k3s/k3s.yaml` : + +```sh +mkdir -p ~/.kube +sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config +sudo chown $(id -u):$(id -g) ~/.kube/config +kubectl get nodes # doit marcher sans sudo maintenant +``` + +## 2. Installer cert-manager + un émetteur Let's Encrypt + +```sh +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml +kubectl -n cert-manager rollout status deployment/cert-manager-webhook +``` + +(l'URL `/releases/latest/download/...` pointe toujours vers la dernière +version stable — pas besoin de connaître le numéro de version à jour.) + +Puis crée un `ClusterIssuer` Let's Encrypt — **remplace `toi@example.com` +par ta vraie adresse**, Let's Encrypt s'en sert pour les alertes +d'expiration : + +```sh +cat <<'EOF' | kubectl apply -f - +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: toi@example.com + privateKeySecretRef: + name: letsencrypt-prod-key + solvers: + - http01: + ingress: + ingressClassName: traefik +EOF +``` + +`deploy/k8s/ingress.yaml` (utilisé plus bas) référence cet émetteur par +son nom exact `letsencrypt-prod` — s'il s'appelle autrement ici, il faudra +aussi changer l'annotation dans `ingress.yaml`. + +## 3. Pointer le DNS + +Ajoute un enregistrement A (et AAAA si le VPS a une IPv6) pointant vers +l'IP du VPS : + +``` +git.tondomaine.fr -> <IP publique du VPS> +``` + +Vérifie que ça a propagé avant de continuer (`dig git.tondomaine.fr` doit +renvoyer la bonne IP) — sinon le défi ACME de l'étape 6 échouera. + +## 4. Récupérer gitfed et construire l'image + +**Construis l'image directement sur le VPS, pas sur ta machine de +travail.** Si ta machine est un Mac Apple Silicon (ou n'importe quelle +machine ARM) et que le VPS tourne en x86_64 (le cas le plus courant chez +les hébergeurs), une image construite en local et copiée sur le VPS ne +sera tout simplement pas la bonne architecture — `docker build` construit +par défaut pour l'architecture de la machine qui exécute la commande. +Construire sur le VPS élimine complètement ce risque. + +Installe Docker sur le VPS s'il n'y est pas déjà (script officiel) : + +```sh +curl -fsSL https://get.docker.com | sudo sh +``` + +Puis récupère le code et construis : + +```sh +git clone <url-du-dépôt-gitfed> +cd gitfed +sudo docker build -f deploy/docker/Dockerfile -t gitfed:$(cat VERSION) . +sudo docker save gitfed:$(cat VERSION) | sudo k3s ctr images import - +``` + +**Point important, source du blocage le plus courant à ce stade** : +`deploy/k8s/deployment.yaml` référence l'image par un tag précis +(`gitfed:X.Y.Z`, visible avec `grep image: deploy/k8s/deployment.yaml`) et +le pod est configuré en `imagePullPolicy: Never` — k3s n'ira jamais la +chercher ailleurs. Construire avec `-t gitfed:latest` au lieu du tag exact +attendu fait planter le pod en `ErrImageNeverPull` indéfiniment. La +commande `docker build -t gitfed:$(cat VERSION)` ci-dessus construit +justement avec le bon tag — ne pas la remplacer par `:latest`. + +## 5. Configurer gitfed pour ton domaine + +Deux fichiers ont des valeurs à adapter à ton domaine : + +**`deploy/k8s/configmap.yaml`** — champ `domain` (et `contact`, ton email, +optionnel) : + +```json +"domain": "git.tondomaine.fr", +"contact": "toi@example.com", +``` + +**`deploy/k8s/ingress.yaml`** — l'hôte, à deux endroits : + +```yaml +tls: + - hosts: ["git.tondomaine.fr"] + secretName: gitfed-tls +rules: + - host: git.tondomaine.fr +``` + +## 6. Déployer + +Toujours sur le VPS, dans le dossier `gitfed` cloné à l'étape 4 (avec les +fichiers modifiés à l'étape 5) : + +```sh +kubectl apply -f deploy/k8s/namespace.yaml +kubectl apply -f deploy/k8s/configmap.yaml +kubectl apply -f deploy/k8s/pvc.yaml +kubectl apply -f deploy/k8s/deployment.yaml +kubectl apply -f deploy/k8s/service.yaml +kubectl apply -f deploy/k8s/ingress.yaml +``` + +Observe le démarrage : + +```sh +kubectl -n gitfed get pods -w +``` + +Les deux conteneurs du pod doivent finir à `2/2 Running` — `web` attend le +socket admin de `server` en boucle avant de démarrer, donc un `0/2` bref +au tout début est normal. S'il reste bloqué, voir « Pannes courantes » +plus bas. + +## 7. Vérifier + +```sh +curl https://git.tondomaine.fr/.well-known/gitfed.json +curl https://git.tondomaine.fr/ # page d'accueil de gitfed-web +``` + +Le premier doit renvoyer un petit JSON avec ton domaine et une clé +publique de CA ; le second, du HTML. Si `curl` refuse le certificat ou +timeout, le souci est probablement DNS ou le défi ACME (voir plus bas). + +## 8. Créer le premier compte (admin) + +Il n'y a aucun compte au démarrage, et pas d'auto-inscription — c'est +volontaire. On le crée directement contre le socket admin du pod : + +```sh +kubectl -n gitfed exec -it deployment/gitfed -c server -- \ + gitfed-tui -config /etc/gitfed/gitfed.json +``` + +**Users → a** (add user) : nom d'utilisateur, ta clé SSH publique, un mot +de passe, puis répondre `y` à la question admin. Connecte-toi ensuite sur +`https://git.tondomaine.fr/login` avec ce compte — tu dois voir un lien +**Admin** dans le menu. À partir de là, cet admin peut créer d'autres +comptes depuis **Admin → Utilisateurs** dans l'interface web ; +`gitfed-tui` ne sert plus qu'en secours (interface web injoignable). + +## 9. Cloner pour de vrai + +Le login web n'ouvre qu'une session web — `git clone`/`push` passe +toujours par SSH, indépendamment : + +```sh +git clone ssh://git@git.tondomaine.fr:2222/<utilisateur>/<dépôt> +``` + +Si ça fonctionne, l'installation est terminée. + +## Pannes courantes + +- **Pod bloqué en `ErrImageNeverPull`** — c'est presque toujours un tag + d'image qui ne correspond pas (voir étape 4). Vérifie avec + `kubectl -n gitfed describe pod <nom-du-pod>` (section Events) et + compare `grep image: deploy/k8s/deployment.yaml` avec + `sudo k3s ctr images ls | grep gitfed` sur le VPS — le tag doit + apparaître identique des deux côtés. +- **Certificat qui ne s'obtient jamais (`ingress` reste en HTTP, pas de + cadenas)** — vérifie que le DNS a bien propagé (`dig`), puis regarde les + logs du pod solver : `kubectl get pods | grep acme-http-solver` doit + exister brièvement pendant la validation ; s'il traîne en `Pending`, + c'est que le port 80 n'est pas joignable depuis l'extérieur (pare-feu, + ou DNS pas encore propagé). +- **`web` reste à `0/2` ou `1/2` longtemps** — `web` attend que `server` + crée le socket admin ; si `server` lui-même ne démarre pas, regarde ses + logs : `kubectl -n gitfed logs deployment/gitfed -c server`. +- **`kubectl` : `connection refused`** — as-tu bien fait + `sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config` (étape 1) ? Sinon, + préfixe toutes les commandes `kubectl` par `sudo k3s kubectl` à la + place. + +## Pour la suite + +- **Mises à jour** : ne pas refaire les étapes 4/6 à la main après la + première fois — utilise + [`deploy/update.sh`](deploy/update.sh), qui construit et importe une + image taguée avec la vraie version, bascule `deployment.yaml` dessus, et + attend que le rollout soit prêt. Ce script se lance depuis **ta machine + de travail** (il pousse le code sur le VPS via `rsync` puis construit + là-bas par SSH, il ne dépend donc pas de son architecture) — il te + faudra un clone de gitfed en local à ce moment-là, en plus de celui sur + le VPS créé à l'étape 4. Regarde l'en-tête de + [`deploy/update.sh`](deploy/update.sh) pour les variables à adapter + (`GITFED_VPS_HOST`, `GITFED_VPS_SRC_DIR`). +- **Sauvegardes** : tout ce qui compte vit sur le volume `gitfed-data` + (base, clé de CA, clé d'hôte SSH, dépôts). Voir la section « Backups » + de [`deploy/k8s/README.md`](deploy/k8s/README.md#backups) pour + `deploy/backup.sh` et la procédure de restauration. +- **Pourquoi le déploiement est structuré ainsi** (un seul pod, deux + conteneurs, pourquoi `replicas` doit rester à `1`, etc.) — c'est expliqué + en détail dans [`deploy/k8s/README.md`](deploy/k8s/README.md).
README.fr.md
diff --git a/README.fr.md b/README.fr.md index 77d933d..2da3276 100644 --- a/README.fr.md +++ b/README.fr.md @@ -62,6 +62,7 @@ instance en fonctionnement. | Doc | Contenu | |---|---| +| [`INSTALL.md`](INSTALL.md) | Installation étape par étape sur un VPS tout neuf, d'une machine vide jusqu'à une instance qui tourne — sans supposer k3s/cert-manager déjà en place. | | [`docs/HOW_IT_WORKS.md`](docs/HOW_IT_WORKS.md) | Le modèle de fédération/identité de bout en bout : certificats, magasin de confiance, ACL, un vrai push détaillé étape par étape. | | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Comment le code est organisé : les quatre binaires, les paquets `internal/`, pourquoi il existe un socket RPC d'administration, la topologie Kubernetes. | | [`docs/security/AUDIT.md`](docs/security/AUDIT.md) | L'audit de sécurité pré-production et ce qui en a été corrigé. | @@ -98,8 +99,13 @@ git clone ssh://git@localhost:2222/<user>/<repo> ## Déployer pour de vrai -L'explication complète — manifestes, pourquoi le pod est formé ainsi, DNS, -amorçage du premier compte — est dans +Tu pars d'un VPS tout neuf, sans rien d'installé (ni k3s, ni +cert-manager) ? [`INSTALL.md`](INSTALL.md) reprend tout ça depuis zéro, +étape par étape. + +Si k3s/Traefik/cert-manager tournent déjà chez toi, l'explication +complète — manifestes, pourquoi le pod est formé ainsi, DNS, amorçage du +premier compte — est dans [`deploy/k8s/README.md`](deploy/k8s/README.md). Chaque mise à jour après la première doit passer par [`deploy/update.sh`](deploy/update.sh), qui incrémente la version, construit et importe une image taguée, et effectue
README.md
diff --git a/README.md b/README.md index a58fba0..0ed5150 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ instance. | Doc | What's in it | |---|---| +| [`INSTALL.md`](INSTALL.md) *(French)* | Step-by-step install on a brand new VPS, from an empty machine to a working instance — no k3s/cert-manager assumed already set up. | | [`docs/HOW_IT_WORKS.en.md`](docs/HOW_IT_WORKS.en.md) | The federation/identity model end to end: certificates, trust store, ACLs, a real push walked through step by step. | | [`docs/ARCHITECTURE.en.md`](docs/ARCHITECTURE.en.md) | How the code is organized: the four binaries, the `internal/` packages, why there's an admin RPC socket, the Kubernetes topology. | | [`docs/security/AUDIT.md`](docs/security/AUDIT.md) *(French)* | The pre-production security audit and what was fixed as a result. | @@ -94,8 +95,13 @@ git clone ssh://git@localhost:2222/<user>/<repo> ## Deploying for real -The full walkthrough — manifests, why the pod is shaped the way it is, DNS, -bootstrapping the first account — is in +Starting from a brand new VPS with nothing on it yet (no k3s, no +cert-manager)? [`INSTALL.md`](INSTALL.md) *(French)* walks through all of +that from scratch, step by step. + +Already have k3s/Traefik/cert-manager running? The full walkthrough — +manifests, why the pod is shaped the way it is, DNS, bootstrapping the +first account — is in [`deploy/k8s/README.md`](deploy/k8s/README.md). Every update after the first should go through [`deploy/update.sh`](deploy/update.sh), which bumps the version, builds and imports a tagged image, and rolls it out:
deploy/k8s/README.md
diff --git a/deploy/k8s/README.md b/deploy/k8s/README.md index cfc84fc..78291a0 100644 --- a/deploy/k8s/README.md +++ b/deploy/k8s/README.md @@ -30,23 +30,40 @@ both rather than standing up anything new. ## 1. Build and import the image -From the repo root, on your workstation: +**The image tag you build must match the tag already written in +`deployment.yaml`**, not `:latest` — check it first: ```sh -docker build -f deploy/docker/Dockerfile -t gitfed:latest . -docker save gitfed:latest -o gitfed.tar -scp gitfed.tar your-vps:/tmp/ +grep 'image:' deploy/k8s/deployment.yaml # e.g. "image: gitfed:1.2.4" +cat VERSION # should match ``` -On the VPS: +`deployment.yaml` is checked into git with whatever version was live on the +original deploy — `deploy/update.sh` moves that pin forward on every +release (see below), it's never `:latest`. Build and tag with `VERSION`, +not a hardcoded tag, so this can't drift: + +**Build on the VPS itself, not your workstation** — `docker build` targets +whatever architecture it runs on, and a workstation that isn't x86_64 (an +Apple Silicon Mac, say) would produce an image the VPS can't run. This is +exactly what `deploy/update.sh` already does under the hood (`rsync` the +source over, then `docker build` via SSH on the VPS), so doing it by hand +the same way for the first deploy keeps one mental model instead of two: ```sh -sudo k3s ctr images import /tmp/gitfed.tar -rm /tmp/gitfed.tar +# from a checkout on the VPS (rsync/clone it there first) +docker build -f deploy/docker/Dockerfile -t gitfed:$(cat VERSION) . +docker save gitfed:$(cat VERSION) | sudo k3s ctr images import - ``` `imagePullPolicy: Never` in `deployment.yaml` means k3s will never try to -pull this from a registry — it only ever uses what you've imported. +pull this from a registry — it only ever uses what you've imported **under +that exact tag**. Get the tag wrong (e.g. build `:latest` while +`deployment.yaml` still says `:1.2.4`) and the pod sits in +`ErrImageNeverPull` forever, since there's nowhere else k3s will look — +fix it by importing an image under the tag `deployment.yaml` actually asks +for, not by editing the tag in `deployment.yaml` unless you mean to. + **Whenever you rebuild, redo this import and then** `kubectl rollout restart deployment/gitfed -n gitfed` — importing a new image under the same tag does not restart pods that are already running.