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
par root
. 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 ;-)