Vault SSH et secrets de SaltStack dans Vault



  • 0_1527623004365_saltstack.png
    0_1527623101342_vault-image.png

    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 votre sshd_config l’option suivante et redémarrer le service ssh :

    # /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 fichier ssh-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 😉