944 lines
33 KiB
Markdown
944 lines
33 KiB
Markdown
---
|
|
title: "Identité et méthodes d'authentification : TP"
|
|
slug: cours-id-authn-tp
|
|
authors: "Florian Maury"
|
|
description: "Un cours de niveau M2 sur l'identité et les méthodes d'authentification"
|
|
date: 2024-02-26T00:00:00+02:00
|
|
type: posts
|
|
draft: false
|
|
categories:
|
|
- infosec
|
|
tags:
|
|
- infosec
|
|
- identity
|
|
- identité
|
|
- authentication
|
|
- authentification
|
|
- passkeys
|
|
- webauthn
|
|
- cours
|
|
lang: fr
|
|
---
|
|
|
|
# TP
|
|
|
|
Ce TP consiste à mettre en place un référentiel d'identités et un service qui
|
|
utilisera ce référentiel pour effectuer l'authentification décentralisée de ces
|
|
utilisateurs et utilisatrices. Ce service sera la forge logicielle Forgejo et
|
|
le référentiel d'identités sera Keycloak.
|
|
|
|
Le référentiel d'identités sera configuré pour permettre l'authentification
|
|
sans mot de passe, à l'aide de WebAuthn et de passkeys.
|
|
|
|
Pour stocker et gérer les passkeys, nous aurons recours à Bitwarden et son
|
|
extension navigateur d'une part et à un serveur Vaultwarden d'autre part.
|
|
Vaultwarden est un serveur libre compatible avec l'API de Bitwarden. Il nous
|
|
permet d'effectuer ce TP sans "polluer" les serveurs Bitwarden officiels avec
|
|
des comptes créés pour ce TP.
|
|
|
|
L'authentification avec WebAuthn nécessite que les sites soient servis sur
|
|
HTTPS. Pour cette raison, nous allons mettre en place un serveur HTTPS qui
|
|
tiendra lieu de serveur mandataire inverse (*reverse proxy*).
|
|
|
|
# Ajouter une adresse IP dédiée au TP
|
|
|
|
{{< details summary="(Action) Ajouter une adresse IP locale dédiée pour le TP. Bloquer le cas échéant les connexions depuis le réseau externe.">}}
|
|
|
|
Sur Linux, ajouter une interface de type dummy et configurer une adresse IP
|
|
dédiée pour le TP. Par exemple, 10.108.0.1.
|
|
|
|
```
|
|
sudo ip l a dummy0 type dummy
|
|
sudo ip a a 10.108.0.1 dev dummy0
|
|
sudo ip l s dummy0 up
|
|
```
|
|
|
|
Sur Windows et Mac, trouver une manière de faire...
|
|
|
|
{{< /details >}}
|
|
|
|
# Installation du serveur mandataire inverse
|
|
|
|
Avant toute chose, il convient de configurer le serveur mandataire, et de
|
|
configurer notre navigateur internet afin qu'il fasse confiance aux certificats
|
|
émis par ce serveur mandataire. Pour ce faire, nous allons utiliser Caddy.
|
|
|
|
Caddy est un serveur qui peut à la fois jouer le rôle de serveur HTTP, mais
|
|
aussi assurer la gestion d'une infrastructure de gestions de clés (IGC), et
|
|
même délivrer des certificats à l'aide du protocole ACME[^acme].
|
|
|
|
[^acme]: https://www.rfc-editor.org/rfc/rfc8555.html
|
|
|
|
Le conteneur Caddy a besoin d'exposer le port 443. Il stocke ses fichiers
|
|
relatifs à l'IGC dans un volume qui doit être monté sur le chemin /data.
|
|
Finalement, sa configuration est effectuée avec un fichier "Caddyfile", situé
|
|
au chemin /etc/caddy/Caddyfile.
|
|
|
|
La syntaxe des Caddyfile est documentée sur le site officiel de
|
|
Caddy[^caddyfile].
|
|
|
|
[^caddyfile]: https://caddyserver.com/docs/caddyfile
|
|
|
|
Pour le moment, nous allons simplement le déclarer et le démarrer pour obtenir
|
|
le certificat racine de l'infrastructure de gestion de clés.
|
|
|
|
(Action) Créer un répertoire `caddy` dans votre répertoire de travail pour ce
|
|
TP.
|
|
|
|
{{< details summary="(Action) Écrire un fichier de configuration `Caddyfile` minimaliste et le stocker dans `caddy/Caddyfile`." >}}
|
|
|
|
```
|
|
{
|
|
http_port 80 # écoute sur le port 80
|
|
https_port 443 # écoute sur le port 443
|
|
admin off # désactivation de l'API qui n'est pas utilisée dans ce TP
|
|
log {
|
|
output stdout # journalisation sur stdout afin de le récupérer par le driver de log de l'engine
|
|
format console # journalisation au format console pour faciliter la lisibilité des logs (pas recommandé en production)
|
|
level info # journalisation de niveau info, pour avoir des informations, mais pas trop
|
|
}
|
|
local_certs # tous les certificats sont émis par l'IGC interne, au lieu d'aller les chercher sur Internet
|
|
skip_install_trust # n'effectue pas une étape inutile quand on fait tourner Caddy dans un conteneur
|
|
}
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Étendre l'image officielle de Caddy pour ajouter ce fichier de configuration. Pour cela, créer le fichier `Containerfile` (ou `Dockerfile`) suivant, et le stocker dans le répertoire `caddy`." >}}
|
|
|
|
```
|
|
FROM docker.io/caddy:2.10.0-alpine
|
|
COPY Caddyfile /etc/caddy
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Créer un fichier `podman-compose.yml` ou `docker-compose.yml` qui inclura les différentes définitions fournies ultérieurement" >}}
|
|
|
|
```
|
|
version: '3.7'
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Déclarer dans le fichier compose un conteneur caddy" >}}
|
|
|
|
Le réseau `caddy_frontend` est le réseau public sur lequel seront exposés les
|
|
ports 80 et 443. Le réseau `caddy_backend` est le réseau privé sur lequel
|
|
seront connectés les différents services rendus disponibles par Caddy.
|
|
|
|
```
|
|
volumes:
|
|
caddy_data:
|
|
driver: local
|
|
|
|
networks:
|
|
caddy_frontend:
|
|
name: caddy_frontend
|
|
internal: false
|
|
caddy_backend:
|
|
name: caddy_backend
|
|
internal: true
|
|
|
|
services:
|
|
caddy:
|
|
build: caddy/
|
|
container_name: caddy
|
|
restart: always
|
|
ports:
|
|
- 8080:80
|
|
- 8443:443
|
|
volumes:
|
|
- caddy_data:/data
|
|
networks:
|
|
- caddy_frontend
|
|
- caddy_backend
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Démarrer le conteneur" >}}
|
|
|
|
Depuis un shell, taper :
|
|
|
|
```
|
|
podman compose -f podman-compose.yml up -d
|
|
```
|
|
|
|
```
|
|
docker compose -f docker-compose.yml up -d
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Récupérer le certificat racine" >}}
|
|
|
|
Depuis un shell, taper :
|
|
|
|
```
|
|
podman cp caddy:/data/caddy/pki/authorities/local/root.crt caddy.crt
|
|
```
|
|
|
|
```
|
|
docker cp caddy:/data/caddy/pki/authorities/local/root.crt caddy.crt
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Insérer le certificat racine dans le magasin de certificat de son navigateur." >}}
|
|
|
|
Avec Firefox, cela peut se faire de manière programmatique, avec l'utilitaire
|
|
certtool. Cet utilitaire peut être installé avec le paquet "libnss3-tool" sur
|
|
Debian/Ubuntu, et le paquet "nss-tools" sur Fedora.
|
|
|
|
Ensuite, exécuter la commande suivante dans un shell :
|
|
|
|
```
|
|
find $HOME -type f -name "cert9.db" | while read filename ; do certutil -A -n "CA TP" -t "TC" -i $PWD/caddy.crt -d "$(dirname "$filename")" ; done
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
# Installation de Vaultwarden
|
|
|
|
Vaultwarden est un serveur compatible avec l'API Bitwarden. Il permet le
|
|
stockage de mots de passe et de passkeys.
|
|
|
|
Il va être installé sous la forme d'un conteneur.
|
|
|
|
{{< details summary="(Action) Ajouter le nom de domaine vault.tp-authn.broken-by-design.fr dans le fichier hosts, et le faire pointer vers 10.108.0.1" >}}
|
|
|
|
Sur Linux et Mac, éditer le fichier /etc/hosts. Sur Windows, éditer le fichier
|
|
C:\Windows\system32\drivers\etc\hosts.
|
|
|
|
```
|
|
10.108.0.1 vault.tp-authn.broken-by-design.fr
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter dans le fichier compose la déclaration des ressources pour un serveur Vaultwarden" >}}
|
|
|
|
Dans la section volumes, ajouter :
|
|
|
|
```
|
|
vault_data:
|
|
driver: local
|
|
```
|
|
|
|
Dans la section services, ajouter :
|
|
|
|
```
|
|
vault:
|
|
image: ghcr.io/dani-garcia/vaultwarden:1.33.2
|
|
container_name: vault
|
|
restart: always
|
|
volumes:
|
|
- vault_data:/data
|
|
networks:
|
|
- caddy_backend
|
|
environment:
|
|
SIGNUPS_ALLOWED: true
|
|
SIGNUPS_DOMAINS_WHITELIST: tp-authn.broken-by-design.fr
|
|
INVITATIONS_ALLOWED: false
|
|
DOMAIN: https://vault.tp-authn.broken-by-design.fr:8443
|
|
SHOW_PASSWORD_HINT: false
|
|
depends_on:
|
|
- caddy
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter l'hôte virtuel pour Vaultwarden dans la configuration de Caddy" >}}
|
|
|
|
```
|
|
vault.tp-authn.broken-by-design.fr:80 {
|
|
redir vault.tp-authn.broken-by-design.fr:8443{uri}
|
|
log
|
|
}
|
|
vault.tp-authn.broken-by-design.fr:443 {
|
|
reverse_proxy vault:80
|
|
log
|
|
}
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Détruire, reconstruire et recréer les conteneurs" >}}
|
|
|
|
```
|
|
podman compose -f podman-compose.yml down
|
|
podman compose -f podman-compose.yml build
|
|
podman compose -f podman-compose.yml up -d
|
|
```
|
|
|
|
```
|
|
docker compose -f docker-compose.yml down
|
|
docker compose -f docker-compose.yml build
|
|
docker compose -f docker-compose.yml up -d
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Ouvrir dans son navigateur
|
|
https://vault.tp-authn.broken-by-design.fr:8443/ et se créer un compte
|
|
|
|
(Action) Installer l'extension navigateur Bitwarden
|
|
|
|
(Action) Se connecter à son compte Vaultwarden dans l'extension Bitwarden.
|
|
Penser à sélectionner "Connexion sur auto-hébergé" et renseigner l'URL du
|
|
serveur "https://vault.tp-authn.broken-by-design.fr:8443/"
|
|
|
|
# Installation de la forge logicielle
|
|
|
|
La forge logicielle est installée avec un conteneur. Ce conteneur dispose d'un
|
|
volume /data pour le stockage de la configuration et des données des
|
|
utilisateurs et utilisatrices.
|
|
|
|
Nous utiliserons une base de données sqlite3 pour stocker les données. En
|
|
production, il est recommandé d'utiliser un gestionnaire de base de données
|
|
plus robuste, comme Postgresql ou MariaDB.
|
|
|
|
Nous avons d'abord besoin de déclarer le nom de domaine qui sera utilisé
|
|
pendant ce TP pour joindre la forge.
|
|
|
|
{{< details summary="(Action) Ajouter le nom de domaine git.tp-authn.broken-by-design.fr dans le fichier hosts, et le faire pointer vers 10.108.0.1" >}}
|
|
|
|
Sur Linux et Mac, éditer le fichier /etc/hosts. Sur Windows, éditer le fichier
|
|
C:\Windows\system32\drivers\etc\hosts.
|
|
|
|
```
|
|
10.108.0.1 git.tp-authn.broken-by-design.fr
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
Plus tard dans le TP, nous aurons besoin que Forgejo contacte le référentiel
|
|
d'identités. Comme ce dernier sera rendu accessible par Caddy, il nous faut
|
|
ajouter le certificat racine de Caddy à la liste des certificats de confiance
|
|
du conteneur Forgejo. Il faut également que le conteneur forgejo ait accès à un
|
|
réseau public.
|
|
|
|
(Action) Créer un répertoire `forgejo` à la racine du répertoire pour ce TP.
|
|
|
|
(Action) Copier dans le répertoire forgejo le certificat racine de Caddy.
|
|
|
|
{{< details summary="(Action) Créer un fichier `Containerfile` ou `Dockerfile` qui étend l'image officielle de Forgejo pour y ajouter le certificat de Caddy" >}}
|
|
|
|
```
|
|
FROM codeberg.org/forgejo/forgejo:11
|
|
COPY caddy.crt /usr/local/share/ca-certificates/caddyca.crt
|
|
RUN update-ca-certificates
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Déclarer la forge logicielle dans le fichier compose" >}}
|
|
|
|
Dans la section `volumes` du fichier compose, ajouter :
|
|
|
|
```
|
|
forgejo_data:
|
|
driver: local
|
|
```
|
|
|
|
Dans la section `networks` du fichier compose, ajouter :
|
|
|
|
```
|
|
forgejo_public:
|
|
name: forgejo_public
|
|
internal: false
|
|
```
|
|
|
|
Dans la section `services` du fichier composer, ajouter :
|
|
|
|
```
|
|
forgejo:
|
|
build: forgejo/
|
|
container_name: forgejo
|
|
restart: always
|
|
networks:
|
|
- forgejo_public
|
|
- caddy_backend
|
|
volumes:
|
|
forgejo_data:/data
|
|
environment:
|
|
USER_UID: 1000
|
|
USER_GID: 1000
|
|
FORGEJO__server__DOMAIN: git.tp-authn.broken-by-design.fr
|
|
FORGEJO__server__ROOT_URL: 'https://%(DOMAIN)s:8443/'
|
|
FORGEJO__server__DISABLE_SSH: true
|
|
FORGEJO__database__DB_TYPE: sqlite3
|
|
FORGEJO__admin__DEFAULT_EMAIL_NOTIFICATIONS: disabled
|
|
# La ligne suivante est OK car nous utilisons un réseau "interne" et dédié
|
|
FORGEJO__security__REVERSE_PROXY_TRUSTED_PROXIES: '*'
|
|
FORGEJO__security__PASSWORD_HASH_ALGO: argon2
|
|
FORGEJO__security__MIN_PASSWORD_LENGTH: 12
|
|
FORGEJO__security__PASSWORD_COMPLEXITY: lower,upper,digit,spec
|
|
FORGEJO__security__PASSWORD_CHECK_PWN: true
|
|
depends_on:
|
|
- caddy
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter l'hôte virtuel pour forgejo dans Caddy à la fin du fichier caddy/Caddyfile" >}}
|
|
|
|
```
|
|
git.tp-authn.broken-by-design.fr:80 {
|
|
redir https://git.tp-authn.broken-by-design.fr:8433{uri}
|
|
log
|
|
}
|
|
|
|
git.tp-authn.broken-by-design.fr:443 {
|
|
reverse_proxy forgejo:3000
|
|
log
|
|
}
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Détruire, reconstruire et recréer les conteneurs" >}}
|
|
|
|
```
|
|
podman compose -f podman-compose.yml down
|
|
podman compose -f podman-compose.yml build
|
|
podman compose -f podman-compose.yml up -d
|
|
```
|
|
|
|
```
|
|
docker compose -f docker-compose.yml down
|
|
docker compose -f docker-compose.yml build
|
|
docker compose -f docker-compose.yml up -d
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Ouvrir https://git.tp-authn.broken-by-design.fr:8443/ dans son
|
|
navigateur
|
|
|
|
(Action) Terminer la procédure d'installation de Forgejo en configurant un
|
|
utilisateur administrateur et son mot de passe.
|
|
|
|
{{< details summary="(Action) Ajouter une passkey pour le compte Administrateur du Forgejo" >}}
|
|
|
|
Pour ajouter la passkey, aller dans la "Configuration" du compte Administrateur
|
|
de Forgejo, dans l'onglet "Sécurité". Ensuite, dans la section "Clés de
|
|
sécurité", taper le nom "Bitwarden" et cliquer sur Ajouter une clé de sécurité.
|
|
Dans la popup Bitwarden, sélectionner "Enregistrer la clé d'accès".
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Se déconnecter, et se reconnecter au compte administrateur, et
|
|
utiliser la passkey comme second facteur d'authentification.
|
|
|
|
# Installation d'un serveur Postgres pour le référentiel d'identités
|
|
|
|
Keycloak utilise une base de données pour stocker les identités entre autres
|
|
informations.
|
|
|
|
Nous allons donc installer un serveur Postgres sous la forme d'un conteneur, et
|
|
créer un réseau sur lequel Keycloak pourra le joindre.
|
|
|
|
{{< details summary="(Action) Ajouter un serveur Postgres au fichier compose" >}}
|
|
|
|
Dans la section `volumes`, ajouter :
|
|
|
|
```
|
|
psql_data:
|
|
driver: local
|
|
```
|
|
|
|
Dans la section `networks`, ajouter :
|
|
|
|
```
|
|
psql_backend:
|
|
name: psql_backend
|
|
internal: true
|
|
```
|
|
|
|
Dans la section `services`, ajouter :
|
|
|
|
```
|
|
psql:
|
|
image: docker.io/postgres:17.5-alpine
|
|
container_name: psql
|
|
restart: always
|
|
volumes:
|
|
- psql_data:/var/lib/postgresql/data
|
|
networks:
|
|
- psql_backend
|
|
environment:
|
|
PGDATA: /var/lib/postgresql/data/pgdata
|
|
POSTGRES_DB: keycloak
|
|
POSTGRES_USER: keycloak
|
|
POSTGRES_PASSWORD: keycloak
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Lancer le conteneur Postgres" >}}
|
|
|
|
```
|
|
podman compose -f podman-compose.yml up psql
|
|
```
|
|
|
|
ou
|
|
|
|
```
|
|
docker compose -f docker-compose.yml up psql
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
# Installation du référentiel d'identité
|
|
|
|
Le conteneur Keycloak est fourni sous la forme d'une image "non optimisée" et
|
|
qui ne sait pas se servir de Postgresql comme base de données par défaut. Il
|
|
est nécessaire "d'optimiser" l'image. En outre, Keycloak va avoir besoin de
|
|
parler avec Forgejo ; il lui faut donc ajouter le certificat racine dans sa
|
|
base de confiance. Contrairement à Forgejo, Keycloak n'utilise pas la base
|
|
système et utilise à la place un keystore propre à Java.
|
|
|
|
{{< details summary="(Action) Ajouter l'hôte `idp.tp-authn.broken-by-design.fr` dans le fichier hosts">}}
|
|
|
|
```
|
|
10.108.0.1 idp.tp-authn.broken-by-design.fr
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter l'hôte virtuel dans Caddy pour Keycloak" >}}
|
|
|
|
À la fin du fichier caddy/Caddyfile, ajouter :
|
|
|
|
```
|
|
idp.tp-authn.broken-by-design.fr:80 {
|
|
redir https://idp.tp-authn.broken-by-design.fr:8433{uri}
|
|
log
|
|
}
|
|
|
|
idp.tp-authn.broken-by-design.fr:443 {
|
|
reverse_proxy keycloak:8080
|
|
log
|
|
}
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Créer un répertoire `keycloak` à la racine du répertoire du TP
|
|
|
|
(Action) Copier dans le répertoire `keycloak` le certificat de l'autorité de certification racine généré par Caddy
|
|
|
|
{{< details summary="(Action) Créer un `Containerfile` (ou `Dockerfile`) dans le répertoire `keycloak` pour optimiser keycloak pour Postgresql" >}}
|
|
|
|
```
|
|
FROM quay.io/keycloak/keycloak:latest as builder
|
|
ENV KC_DB=postgres
|
|
|
|
COPY --chmod=0644 caddy.crt /opt/keycloak/conf/caddy.crt
|
|
RUN keytool -import -trustcacerts -keystore /opt/keycloak/conf/truststores/cacerts.jks -noprompt -storepass useless -file /opt/keycloak/conf/caddy.crt
|
|
|
|
WORKDIR /opt/keycloak
|
|
RUN /opt/keycloak/bin/kc.sh build
|
|
|
|
FROM quay.io/keycloak/keycloak:latest
|
|
COPY --from=builder /opt/keycloak/ /opt/keycloak/
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter un conteneur keycloak dans le fichier compose" >}}
|
|
|
|
Dans la section `networks`, ajouter :
|
|
|
|
```
|
|
keycloak_public:
|
|
name: keycloak_public
|
|
internal: false
|
|
```
|
|
|
|
Dans la section `services`, ajouter :
|
|
|
|
```
|
|
keycloak:
|
|
build: keycloak/
|
|
container_name: keycloak
|
|
restart: always
|
|
networks:
|
|
- keycloak_public
|
|
- caddy_backend
|
|
- psql_backend
|
|
command: start
|
|
environment:
|
|
JAVA_OPTS: -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512 -Xms64m -Xmx512m -Djavax.net.ssl.trustStore=/opt/keycloak/conf/truststores/cacerts.jks -Djavax.net.ssl.trustStorePassword=useless
|
|
KC_PROXY_HEADERS: xforwarded
|
|
KC_HTTP_ENABLED: "true"
|
|
KC_DB: postgres
|
|
KC_DB_URL: jdbc:postgresql://psql/keycloak
|
|
KC_DB_USER: keycloak
|
|
KC_DB_PASSWORD: keycloak
|
|
KC_HOSTNAME: https://idp.tp-authn.broken-by-design.fr:8443/
|
|
KEYCLOAK_ADMIN: admin
|
|
KEYCLOAK_ADMIN_PASSWORD: Bonjour1!tititoto
|
|
depends_on:
|
|
- caddy
|
|
- psql
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Détruire, reconstruire et relancer les conteneurs" >}}
|
|
|
|
```
|
|
podman compose -f podman-compose.yml down
|
|
podman compose -f podman-compose.yml build
|
|
podman compose -f podman-compose.yml up -d
|
|
```
|
|
|
|
```
|
|
docker compose -f docker-compose.yml down
|
|
docker compose -f docker-compose.yml build
|
|
docker compose -f docker-compose.yml up -d
|
|
```
|
|
|
|
{{< /details >}}
|
|
|
|
# Configuration du référentiel d'identités
|
|
|
|
Dans cette section, nous allons configurer Keycloak afin qu'il dispose d'un
|
|
domaine ("*realm*") dédié au réseau d'exploitation (par opposition au réseau et
|
|
au domaine d'administration). Dans ce domaine, nous allons créer un
|
|
utilisateur. Nous allons également déclarer un "client" OpenID Connect pour
|
|
Forgejo. Cette configuration client permettra à Forgejo de demander des
|
|
informations à Keycloak concernant les utilisateurs.
|
|
|
|
{{< details summary="(Action) Se connecter sur https://idp.tp-authn.broken-by-design.fr:8443/" >}}
|
|
|
|
Utiliser le compte "admin" et le mot de passe "Bonjour1!tititoto"
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Créer un nouveau domaine (*realm*) nommé "TP" pour les comptes utilisateurs et utilisatrices d'exploitation
|
|
|
|
{{< details summary="(Action) Collecter les informations pour l'ajout d'un nouveau client dans Forgejo." >}}
|
|
|
|
Pour cela, se connecter au compte administrateur, sélectionner "Administration
|
|
du site", puis dans "Identité et accès", sélectionner "Sources
|
|
d'authentification". Ajouter une source d'authentification. Sélectionner le
|
|
type "OAuth2" et le fournisseur OAuth2 appelé "OpenID Connect". Après avoir
|
|
saisir le nom de l'authentification "keycloak", récupérer l'adresse conseillée
|
|
pour l'URL de rappel/redirection
|
|
(`https://git.tp-authn.broken-by-design.fr:8443/user/oauth2/keycloak/callback`).
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Sur Keycloak, créer un nouveau client OpenID Connect `forgejo` dans le domaine "e;TP"e;." >}}
|
|
|
|
Dans le domaine "TP", aller dans le menu "Clients", et cliquer sur "Create
|
|
client". Appeler le client `forgejo`. Cliquer sur "Next", puis sélectionner
|
|
uniquement le flux d'authentification `standard`. Activer également
|
|
l'authentification du client, puis cliquer sur "Next". Indiquer l'URL racine
|
|
`https://git.tp-authn.broken-by-design.fr:8443/`, l'URL de rappel valide
|
|
`https://git.tp-authn.broken-by-design.fr:8443/user/oauth2/keycloak/callback`.
|
|
Finalement, cliquer sur "Save".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Récupérer le secret pour le client Forgejo." >}}
|
|
|
|
Se rendre dans l'onglet "Credentials" du client `forgejo` et copier le `client secret`.
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Configurer Forgejo pour utiliser Keycloak" >}}
|
|
|
|
Une fois "ajouter une source d'authentification" sélectionné, choisir le type
|
|
"OAuth2", et le fournisseur "OpenID Connect". Indiquer le nom de fournisseur
|
|
`keycloak`, l'ID du client `forgejo`, et le secret du client copié depuis
|
|
Keycloak. Indiquer également l'URL de découverte
|
|
`https://idp.tp-authn.broken-by-design.fr:8443/realms/TP/.well-known/openid-configuration`.
|
|
Ajouter la source d'authentification.
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter un utilisateur administrateur forgejo `admin` dans le domaine TP" >}}
|
|
|
|
Dans "Users", sélectionner "Create new user". Nommer l'utilisateur `admin` et
|
|
créer l'utilisateur. Dans l'onglet "Credentials", sélectionner "Set Password"
|
|
et définir un mot de passe pour l'utilisateur `admin`. Utiliser
|
|
Bitwarden/Vaultwarden pour suggérer un bon mot de passe, et le sauvegarder.
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Associer l'utilisateur administrateur de Forgejo à l'identité `admin` du realm TP de Keycloak." >}}
|
|
|
|
Dans Forgejo, aller dans "Configuration" du profil utilisateur. Dans l'onglet
|
|
"Sécurité", dans la section "Gérer les comptes liés", sélectionner "Lier un
|
|
compte", et sélectionner "keycloak" dans la liste déroulante qui est apparue.
|
|
S'authentifier au compte admin du realm TP. Compléter les champs demandés.
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Tester l'authentification avec Keycloak sur Forgejo." >}}
|
|
|
|
Se déconnecter du compte administrateur de Forgejo. Se reconnecter, en cliquant
|
|
sur "Se connecter avec Keycloak".
|
|
|
|
{{< /details >}}
|
|
|
|
# Gérer les groupes et les rôles de façon centralisée
|
|
|
|
Dans cette partie du TP, nous allons créer des rôles et des groupes et les
|
|
assigner à nos utilisateurs `admin` et un nouvel utilisateur `jean.dupont`. Ces
|
|
droits seront ajoutés dans Keycloak et dans Forgejo. Ensuite nous configurerons
|
|
Forgejo pour qu'il utilise les informations récupérées grâce OpenID Connect
|
|
pour ajouter l'utilisateur `jean.dupont` dans les bons groupes gérés par
|
|
Forgejo.
|
|
|
|
{{< details summary="(Action) Créer dans Forgejo une organisation "e;TP"e;" >}}
|
|
|
|
Cliquer sur le "+" en haut à droite, puis sélectionner "Nouvelle Organisation".
|
|
Appeler l'organisation "TP" et cliquer sur "Créer une organisation".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Créer dans l'organisation Forgejo "e;TP"e;, une équipe `mainteneurs`, avec des droits en écriture." >}}
|
|
|
|
Une fois sur la page de l'organisation "TP", sélectionner "Nouvelle équipe".
|
|
Nommer l'équipe `mainteneurs`, et sélectionner le niveau de privilège
|
|
"Ecriture" pour l'ensemble des actions possibles.
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Créer dans l'organisation Forgejo "TP", une équipe `contributeurs`, avec des droits en lecture.
|
|
|
|
{{< details summary="(Action) Ajouter au domaine TP de Keycloak des rôles `mainteneur` et `contributeurs`." >}}
|
|
|
|
Dans le domaine TP, sélectionner le menu "Realm Roles", puis "Create role".
|
|
Nommer les rôles et cliquer sur "Save".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter au domaine TP de Keycloak des groupes `Mainteneurs du TP` et `Contributeurs du TP`." >}}
|
|
|
|
Dans le domaine TP, aller dans le menu "Groups", puis cliquer sur "Create
|
|
Group". Nommer le groupe, puis cliquer sur "Create". Renouveller l'opération
|
|
pour le second groupe.
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Assigner le rôle `mainteneur` aux membres du groupe `Mainteneurs du TP` et le rôle `contributeur` aux membres du groupe `Contributeurs du TP`." >}}
|
|
|
|
Pour cela, dans le domaine TP, dans le menu "Groups", cliquer sur un groupe,
|
|
puis aller dans l'onglet "Role Mapping", et cliquer sur "Assign Role".
|
|
Sélectionner le rôle approprié, puis cliquer sur "Assign". Renouveler
|
|
l'opération pour le second groupe.
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Dans Keycloak, ajouter un utilisateur `jean.dupont` et lui définir un
|
|
mot de passe avec Vaultwarden.
|
|
|
|
{{< details summary="(Action) Ajouter `jean.dupont` au groupe `Mainteneurs du TP`." >}}
|
|
|
|
Pour cela, dans le domaine TP, dans le menu "Groups", cliquer sur le groupe
|
|
`Mainteneurs du TP`, puis aller dans l'onglet "Members". Cliquer sur "Add
|
|
Member", cocher l'utilisateur `jean.dupont`, puis cliquer sur "Add".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Configurer l'exposition des rôles au client `forgejo`, afin de ne divulguer que la possession des rôles `mainteneur` et `contributeur` à ce client, sous une entrée `forgejo-roles`." >}}
|
|
|
|
Dans le menu "Clients", cliquer sur le client `forgejo`. Ensuite, dans l'onglet
|
|
"Client Scopes", cliquer sur l'entrée `forgejo-dedicated`. Cliquer sur le
|
|
bouton "Add predefined mappers". Cocher "realm roles", puis cliquer sur "Add".
|
|
Cliquer ensuite sur l'entrée "realm roles", et remplacer la valeur dans "Token
|
|
Claim Name" par `forgejo-roles`. Cocher également "Add to userinfo" et "Add to
|
|
ID Token", puis cliquer sur "Save". Ensuite, revenir sur la fenêtre
|
|
"forgejo-dedicated", puis se rendre dans l'onglet "Scope". Désactiver "Full
|
|
scope allowed", puis cliquer que "Assign Role", cocher les deux rôles
|
|
`mainteneur` et `contributeur` et cliquer sur "Assign".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Configurer Forgejo pour tenir compte des rôles fournis par Keycloak." >}}
|
|
|
|
Dans "Administration du site", dans le menu "Identités et accès", puis "Sources
|
|
d'authentification", cliquer sur "Editer" pour le fournisseur "keycloak". Dans
|
|
le champ "Associe les groupes réclamés avec les équipes de l'organisation.
|
|
(Optionnel, nécessite un nom de réclamation)", saisir le document JSON suivant
|
|
:
|
|
|
|
```
|
|
{"mainteneur": {"TP": ["mainteneurs"]}, "contributeur": {"TP": ["contributeurs"]}}
|
|
```
|
|
|
|
Saisir également `forgejo-roles` dans le champ "Réclamer le nom fournissant les
|
|
noms de groupe pour cette source. (facultatif)", puis cliquer sur "Mettre à
|
|
jour la source d'authentification".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Configurer Forgejo pour qu'il retire les membres des équipes lors de la synchronisation avec Keycloak" >}}
|
|
|
|
Dans "Administration du site", dans le menu "Identités et accès", puis "Sources
|
|
d'authentification", cliquer sur "Editer" pour le fournisseur "keycloak".
|
|
Cocher la case "Supprimer les utilisateurs des équipes synchronisées si
|
|
l'utilisateur n'appartient pas au groupe correspondant", puis cliquer sur
|
|
"Mettre à jour la source d'authentification".
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Constater qu'il n'y a aucun membre dans l'équipe `mainteneurs` sur
|
|
Forgejo.
|
|
|
|
{{< details summary="(Action) Se déconnecter de l'utilisateur `admin` du realm TP." >}}
|
|
|
|
Se rendre à l'adresse
|
|
`https://idp.tp-authn.broken-by-design.fr:8443/realms/TP/account/` et se
|
|
déconnecter.
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Se connecter en tant que `jean.dupont` sur Forgejo, en se connectant
|
|
avec Keycloak. Valider la création d'un nouveau compte. Constater que
|
|
l'utilisateur appartient à l'équipe `mainteneurs`.
|
|
|
|
(Action, Optionnel) Dans Keycloak, retirer `jean.dupont` du groupe des
|
|
utilisateurs "Mainteneurs du TP".
|
|
|
|
(Action) Se déconnecter du compte `jean.dupont` sur Forgejo et sur Keycloak et
|
|
se reconnecter. Constater que l'utilisateur n'est plus membre de l'équipe
|
|
`mainteneurs`.
|
|
|
|
# Configurer une authentification sans mot de passe
|
|
|
|
Dans cette partie du TP, nous allons améliorer la procédure d'authentification
|
|
sur Keycloak en rendant l'usage du mot de passe optionnel, voire en l'éliminant
|
|
totalement.
|
|
|
|
{{< details summary="(Action) Créer une nouvelle procédure d'authentification à partir de celle préexistante "e;Browser"e;, ne permettant que l'authentification sans mot de passe avec une passkey, ou l'authentification avec mot de passe si l'attribut "e;password-authn-allowed"e; vaut "e;true"e;." >}}
|
|
|
|
Dans le domaine TP, cliquer sur "Authentication". Dans l'onglet "Flows", sur le
|
|
menu kebab au bout de la ligne "browser", et sélectionner "Duplicate. Appeler
|
|
ce flow "Browser TP". Cliquer sur "Browser TP", puis supprimer toutes les
|
|
entrées en cliquant sur les icones poubelles.
|
|
|
|
Cliquer ensuite sur "Add execution", sélectionner "Cookie" et cliquer sur
|
|
"Add". Dans la colonne "Requirement" de la ligne "Cookie", sélectionner
|
|
"Alternative".
|
|
|
|
Cliquer ensuite sur "Add Subflow", appeler ce flow "user active
|
|
authentication", et conserver le "flow type" à la valeur "Generic". Dans la
|
|
colonne "Requirement" de la ligne "user active authentication", sélectionner
|
|
"Alternative".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "user active authentication", puis sur
|
|
"Add execution". Sélectionner "Username Form".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "user active authentication", puis sur
|
|
"Add sub-flow". Le nommer "required placeholder", laisser le "flow type" à la
|
|
valeur "Generic", puis cliquer sur "Add". Dans la colonne "Requirement" de la
|
|
ligne "required placeholder", sélectionner "Required".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "required placeholder", puis sur "Add
|
|
sub-flow". Le nommer "passwordless flow", laisser le "flow type" à la valeur
|
|
"Generic", puis cliquer sur "Add". Dans la colonne "Requirement" de la ligne
|
|
"passwordless flow", sélectionner "Alternative".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "required placeholder", puis sur "Add
|
|
sub-flow". Le nommer "passwordful flow", laisser le "flow type" à la valeur
|
|
"Generic", puis cliquer sur "Add". Dans la colonne "Requirement" de la ligne
|
|
"passwordful flow", sélectionner "Alternative".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "passwordless flow", puis sur "Add
|
|
condition". Sélectionner "Condition - user configured", puis cliquer sur Add.
|
|
Dans la colonne "Requirement" de la ligne "Condition - user configured" que
|
|
l'on vient d'ajouter, sélectionner "Required".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "passwordless flow", puis sur "Add
|
|
step". Sélectionner "Webauthn Passwordless Authenticator", puis cliquer sur
|
|
Add. Dans la colonne "Requirement" de la ligne "Webauthn Passwordless
|
|
Authenticator", sélectionner "Required".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "passwordful flow", puis sur "Add
|
|
subflow". Le nommer "Conditional Flow - password allowed", puis cliquer sur
|
|
Add. Dans la colonne "Requirement" de la ligne "Conditional Flow - password
|
|
allowed", sélectionner "Conditional".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "Conditional Flow - password allowed",
|
|
puis cliquer sur "Add condition". Sélectionner "Condition - user attribute",
|
|
puis cliquer sur Add. Dans la colonne "Requirement" de la ligne "Condition -
|
|
user attribute" que l'on vient d'ajouter, sélectionner "Required". Cliquer
|
|
ensuite sur la roue crantée de la ligne "Condition - user attribute". Saisir
|
|
dans le champ "Alias" la valeur "Authentification par mot de passe autorisée",
|
|
dans le champ "Attribute name" la valeur "password-authn-allowed", dans le
|
|
champ "Expected attribute value" la valeur "true". Cocher "Include group
|
|
attributes", puis cliquer sur "Save".
|
|
|
|
Cliquer ensuite sur le "+" de la ligne "Conditional Flow - password allowed",
|
|
puis sur "Add step". Sélectionner "Password Form", puis cliquer sur "Add". Dans
|
|
la colonne "Requirement", sur la ligne "Password Form", sélectionner
|
|
"Required".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Assigner cette procédure au flux d'authentification "e;Browser"e;" >}}
|
|
|
|
Dans la procédure "Browser TP", cliquer sur "Action", puis "Bind flow".
|
|
Sélectionner "Browser flow", puis "Save".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter un groupe `Password allowed` dans le domaine TP" >}}
|
|
|
|
Cliquer sur le menu "Groups", puis "Create Group". Nommer le groupe, puis
|
|
cliquer sur "Create".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter `jean.dupont` et `admin` en tant que membres du groupe `Password allowed`." >}}
|
|
|
|
Cliquer sur le groupe `Password allowed`. Dans l'onglet "Members", cliquer sur
|
|
"Add member". Cocher les deux utilisateurs et cliquer sur "Add".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Ajouter un attribut `password-authn-allowed` au groupe `Password allowed`, avec la valeur `true`." >}}
|
|
|
|
Cliquer sur le groupe `Password allowed`. Dans l'onglet "Attributes", cliquer
|
|
sur "Add attributes". Saisir `password-authn-allowed` dans le champ "key" et
|
|
`true` dans le champ "Value". Cliquer sur "Save".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Configurer l'enregistrement obligatoire d'une passkey pour `admin`" >}}
|
|
|
|
Dans le menu "Users" du domaine TP, cliquer sur l'utilisateur `admin`. Dans
|
|
"Required actions", sélectionner "Webauthn Register Passwordless". Cliquer sur
|
|
"Save".
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) S'authentifier au compte `admin` avec un mot de passe. Enregister une
|
|
passkey.
|
|
|
|
(Action) Se déconnecter et se reconnecter à l'utilisateur `admin`. Utiliser la passkey.
|
|
|
|
{{< details summary="(Action) Retirer `admin` du groupe `Password allowed`" >}}
|
|
|
|
Dans le menu "Groups" du domaine TP, cliquer sur le groupe `Password allowed`,
|
|
puis cocher l'utilisateur `admin` et cliquer sur "Leave".
|
|
|
|
{{< /details >}}
|
|
|
|
{{< details summary="(Action) Retirer la passkey du compte `admin`" >}}
|
|
|
|
Dans le menu "Users" du domaine TP, cliquer sur l'utilisateur `admin`. Dans
|
|
l'onglet "Credentials", cliquer sur le menu kebab de la ligne Webauthn-password
|
|
less, et cliquer "Delete" et confirmer.
|
|
|
|
{{< /details >}}
|
|
|
|
(Action) Essayer de se connecter au compte admin et constater une erreur.
|