Vault SSH et secrets de SaltStack dans Vault
-
Pré-requis
Il vous faudra :
- Un vault
- Un salt
Se connecter en SSH avec une clé publique signée par Vault
Vault SSH Secrets Engine
La première étape est d’activer le “SSH Secrets Engine” de votre Vault. C’est grâce à lui que vous pourrez signer plus tard votre clé publique SSH et ensuite vous connecter sur l’ensemble de serveurs associés à l’autorité de certification derrière cette signature. Ce n’est pas clair ? Bon. Se connecter avec vault ssh, comment ça marche ?
Rien de très compliqué, la documentation est plutôt claire.- Monter le SSH Secrets Engine :
❯ vault secrets enable -path=ssh ssh Successfully mounted 'ssh' at 'ssh'!
- Il faut maintenant installer l’autorité de certification qui signera les clés SSH. Soit vous avez déjà un CA que vous pouvez injecter, soit vous demandez à Vault de vous en générer un. Pour l’exemple, je pars sur cette solution :
❯ vault write ssh/config/ca generate_signing_key=true Key Value --- ----- public_key ssh-rsa AAAAB3NzaC1yc2EA...
-
Il faut maintenant distribuer la clé publique de notre CA sur les serveurs à cibler par cette méthode d’authentification. Pour récupérer cette clé, la documentation propose deux solutions :
- Via curl :
curl -o /etc/ssh/trusted-user-ca-keys.pem http://mon_vault:8200/v1/ssh-client-signer/public_key
- Via le cli vault :
vault read -field=public_key ssh-client-signer/config/ca > /etc/ssh/trusted-user-ca-keys.pem
L’idée est donc de créer le fichier
/etc/ssh/trusted-user-ca-keys.pem
qui contiendra la clé publique sur vos serveurs. Il faut ensuite rajouter dans votresshd_config
l’option suivante et redémarrer le service ssh : - Via curl :
# /etc/ssh/sshd_config # ... TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
- Il faut ensuite créer un rôle que vous allez ensuite utiliser pour signer votre clé SSH. Par exemple :
❯ vault write ssh/roles/remi -<<"EOH" { "allow_user_certificates": true, "allowed_users": "*", "default_extensions": [ { "permit-pty": "", "permit-port-forwarding": "" } ], "key_type": "ca", "default_user": "remi", "ttl": "12h", "max_ttl": "24h" } EOH Success! Data written to: ssh/roles/remi
Avec ce rôle, je vais pouvoir générer une clé signée, valide pour 12h et me connecter avec le user
remi
uniquement.Attention au max_ttl : si vous n’indiquez rien dans votre rôle, c’est celui par défaut qui sera utilisé. Si vous voulez un ttl supérieur, ca ne fonctionnera pas !
J’en rajoute un identique en remplaçant le
default_user
parroot
. Je dois maintenant rajouter une policy et la lier à mon user pour pouvoir signer ma clé.- Je rajoute la policy
ssh-remi
suivante dans un fichierssh-remi.hcl
:
path "ssh/sign/root" { capabilities = [ "update" ] } path "ssh/sign/remi" { capabilities = [ "update" ] }
Puis :
vault policy write ssh-remi ssh-remi.hcl Success! Uploaded policy: ssh-remi
- Que je lie ensuite à mon compte (à adapter en fonction de votre méthode d’authentification et de vos policies) :
vault write auth/userpass/users/remi policies="default,remi,ssh-remi" Success! Data written to: auth/userpass/users/remi
Signature de la clé et connexion !
Tout est prêt côté Vault et sur vos serveurs. Avant de vous connecter il faut signer votre clé publique ssh. Cette étape sera à refaire à l’expiration de cette clé, dans mon exemple, toutes les 12h.
- Pour signer ma clé, je dois me connecter à mon Vault :
❯ vault login -method=userpass username=remi Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token c8e token_accessor 4745 token_duration 8h token_renewable true token_policies [default remi ssh-remi] token_meta_username remi
- Vérifiez que votre policy ssh se trouve bien dans vos policies. Ici j’ai rajouté ssh-remi que je retrouve bien dans mon token.
- Je peux maintenant (enfin) signer ma clé ssh :
❯ vault write -field=signed_key ssh/sign/remi public_key=@$HOME/.ssh/id_rsa.pub > signed-remi.pub
- \o/ Si on regarde dans le détail :
❯ ssh-keygen -Lf signed-remi.pub signed-remi.pub: Type: ssh-rsa-cert-v01@openssh.com user certificate Public key: RSA-CERT SHA256:koC21 Signing CA: RSA SHA256:1oa Key ID: "vault-userpass-remi-928" Serial: 7658 Valid: from 2018-05-23T22:07:03 to 2018-05-24T10:07:33 Principals: remi Critical Options: (none) Extensions: permit-port-forwarding permit-pty
- Je peux me connecter sur mon serveur avec cette clé :
❯ ssh -i signed-remi.pub remi@mon_serveur
Automatiser la signature et la connexion
Histoire de simplifier tout ça, voici le lien vers un petit script à adapter selon votre besoin pour vous connecter à un serveur et éventuellement faire la signature de votre clé publique ssh.
C’est ici.Connecter Salt à Vault
On va commencer avec quelques manips à faire côté Vault. L’idée est de créer un AppRole qui est la méthode à utiliser pour connecter une appli, un service à Vault. On pourra ensuite renseigner les identifiants dans la configuration de Salt et ainsi autoriser Salt a profiter de Vault en fonction de la policy utilisée.
AppRole
Je me base de nouveau sur la documentation pour créer mon nouvel AppRole :
- Si ce n’est pas déjà activé, on active cette méthode d’authentification :
❯ vault auth enable approle
- Rajoutez un rôle salt et adaptez à votre besoin cet exemple :
❯ vault write auth/approle/role/salt \ secret_id_ttl=0 \ token_ttl=10m \ token_max_ttl=15m \ secret_id_num_uses=0 \ policies="default,salt" \ period=0 \ token_num_uses=0 \ bind_secret_id=true }
- Il est possible de rajouter d’autre contrainte comme brider l’utilisation de cet approle à une adresse ip ou à un réseau. Plus de détails dans la documentation de l’api.
- On va maintenant créer la policy salt. Deux choses à prévoir : Salt va générer des Token pour ses minions, il faut donc donner ce droit, puis, c’est le but de cet article, les droits pour créer et mettre à jour les mots de passe des serveurs. Ce qui donne ici :
path "pass_servers/*" { capabilities = ["create", "read", "update"] } path "auth/token/create" { capabilities = ["create", "update"] }
- Stockez ces règles dans un fichier et appliquez les. Exemple :
❯ vault policy write salt /root/vault_k8s/salt_policies.hcl Success! Uploaded policy: salt
- On va récupérer les informations nécessaires à Salt pour la connexion sur le Vault :
❯ vault read auth/approle/role/salt/role-id role_id db02de05-fa39-4855-059b-67221c5c2f63 ❯ vault write -f auth/approle/role/salt/secret-id secret_id 6a174c20-f6de-a53c-74d2-6018fcceff64 secret_id_accessor c454f7e5-996e-7230-6074-6ef26b7bcf86
- Attention la commande
write -f
génère un secret_id à chaque appel.
Configuration de Salt
- Sur votre Salt Master, rajoutez un fichier dans /etc/salt/master.d avec pour contenu :
vault-home: driver: vault vault: url: https://mon_vault verify: True auth: method: approle role_id: db02de05-fa39-4855-059b-67221c5c2f63 secret_id: 6a174c20-f6de-a53c-74d2-6018fcceff64 policies: - salt peer_run: .*: - vault.generate_token
- Si votre Vault à un certificat autosigné, il faudra l’installer sur votre serveur Salt ou passer l’option verify à False.
- Relancez le service salt-master et salt-minion.
Tests
- Vous devriez pouvoir créer et lire des secrets depuis Salt. Pour faire un test rapide, voici comment écrire un secret :
❯ salt 'mon_serveur' vault.write_secret pass_servers/test pass=coincoin mon_serveur: True
- Et pour lire ce secret :
❯ salt 'mon_serveur' vault.read_secret pass_servers/test mon_serveur: ---------- pass: coincoin
- C’est prêt !
Générer un mot de passe aléatoire et le stocker dans Vault
Voici un state Salt pour générer un mot de passe aléatoire et le stocker dans Vault.
Vous pouvez l’utiliser de différentes façons : à la demande avec par exemple un
state.apply
, via un reactor pour réagir à un évènement lors de la création d’un serveur par exemple, dans un schedule pour changer le mot de passe régulièrement.{% set password = salt['random.get_str'](10) %} {% set secret = salt['vault'].write_secret('pass_servers/'+grains['fqdn'],user='root',password=password, description='Généré par Salt le '+salt['system.get_system_date_time']()) %} {% set hash_password = salt['shadow'].gen_password(password) %} set_new_password: user.present: - name: root password: {{ hash_password }}
Et a sauvegarder ailleurs pour le jour où Vault sera HS