Gitlab se positionne comme le centre névralgique de votre atelier logiciel. Il centralise la gestion du code et des utilisateurs, mais surtout, il permet de brancher un ensemble cohérent de services qui forme le dit atelier logiciel.
Il y a une RFC pour ça
RFC7519 en l'occurrence.
Gitlab n'est pas super motivé pour réinventer la roue, et encore moins pour tout ce qui touche à la sécurité. Ils se sont pris suffisamment de CVE pour apprendre l'humilité. Gitlab s'appuie sur un ensemble de bibliothèques (rails, doorkeeper, jwt, rack-oauth2…) et de services tiers, comme la Registry de Docker .
Les jetons JWT
JWT, comme Json Web Token, est un ensemble de normes définissant des jetons (du bête clef/valeur avec un ensemble de clefs normalisées aux noms trop courts). Le tout en JSON, emballé en base64.
Ça ressemble à ça :
[prism] { "access": [ { "type": "repository", "name": "demo/beuha", "actions": [ "pull" ] } ], "jti": "8ad97967-165d-4c44-a0a5-b8a47f7", "aud": "container_registry", "sub": "bob", "iss": "gitlab-issuer", "iat": 1550530417, "nbf": 1550530412, "exp": 1550530717 } [/prism]
bob a maintenant l' access à container_registry depuis l'autorité gitlab-issuer , le jeton à l'identité jti , n'est pas utilisable avant nbf , ni après exp et à été émis à iat .
Le tout est emballé dans un autre JSON, accompagné d'une signature asymétrique.
La clef (du très classique RSA) privée est coté Gitlab, et la partie publique est coté Registry.
L'utilisateur demande un jeton à Gitlab, ce jeton est confié à la Registry pour faire une action. La registry ne cause jamais au Gitlab, elle fait confiance au jeton, et surtout à sa signature.
La registry Docker
Lorsque l'on tripote la Registry sans être authentifié, on se prend un prévisible 401, et surtout un header HTTP nous donnant les informations pour s'authentifier :
Www-Authenticate | Bearer realm="https://gitlab.bearstech.com/jwt/auth",service="container_registry" |
---|
On a l'URL pour soumettre notre demande, demande qui sera authentifiée avec un jeton Gitlab. Oui, il faut un jeton pour obtenir un jeton. Sauf qu'entre ces deux jetons, il y a la notion de délégation. La registry ne voit que les informations qui la concernent, et ne pourra faire aucune action coté Gitlab. Chose possible avec le jeton Gitlab.
La demande de jeton, en python , va ressembler à ça :
class Gitlab: "Gitlab server" def __init__(self, domain): self.domain = domain def token(self, project, action='pull', client_id='gitlab_ci', service='container_registry', offline_token=True): p = urllib.parse.quote_plus(project) r = requests.get(('https://{domain}/jwt/auth?' 'client_id={cid}' '&service={service}' '&offline_token={ot}' '&scope={scope}').format( domain=self.domain, cid=client_id, service=service, ot='true' if offline_token else 'false', scope='repository:%s:%s' % (p, action)), auth=(os.getenv('USER'), os.getenv('TOKEN'))) return r.json().get('token')
Il y a quelques pinaillages comme l'escape du nom du projet exigé par Gitlab, ou le scope
avec des :
de partout pour décrire l' access . Pour réclamer un jeton Registry , il faut aller chercher sur son Gitlab un Jeton d'accès à l'adresse /profile/personal_access_tokens
.
Gitlab nous renvoie un jeton, que l'on peut tester avec l'interface web de JWT.io .
En fournissant la clef publique à JWT.io (publique, j'insiste, celle qui est référencée dans la configuration de la Registry), il est même possible de valider la signature du jeton.
Authlib permet de faire cette vérification, avec l'aide de x5092json , par ce que la bibliothèque [cryptography] https://cryptography.io/en/latest/ a tendance à détester tous les certificats. Même quand ils passent la validation d'OpenSSL.
openssl x509 -text -noout -in public.pem
La recommandation trouvée sur Stack overflow est de demander à OpenSSL d'extraire juste la clef publique du certificat pour que cryptography arrête de bouder. Voilà, plus de rien, c'est plus de pureté.
Ils ont un ticket ouvert sur le thème "doit-on détester tout ce qui n'est pas cliniquement pur?".
Je garde CFSSL comme PKI, incomparable face à la bouse Easy-RSA .
Ensuite, avec le token et sa signature, il faut une couche de paranoïa, on vérifie que la signature est bien du type attendu (RSASHA256), de taille suffisante, 256, avec le bon émetteur, et qu'elle est dans sa fenêtre de validité. La bibliothèque est censée le faire, mais bon, même les paranoïaques ont des ennemis. Hum, le certificat est-il en lecture seule, pas périmé, et bien signé par votre Autorité de Certification (CA)?
La démo complète est disponible sur Github .
What else?
Voilà, vous êtes capable d'authentifier un service avec la délégation de droit de Gitlab, avec des informations sur un projet et la notion de lecture/écriture. Tout ça sans avoir à créer un nouveau service pour Gitlab.
C'est pas beau les RFCs?