La PKI Vault sur le grill

Vault (par HashiCorp) est un coffre fort permettant de stocker divers secrets, de les restituer, voire de les générer. Beef GrillPlus concrètement, il peut fournir à des utilisateurs ou à des services tout un tas de secrets – généralement temporaires – comme des certificats, des tokens, des couples login/mot de passe générés dans une instance PostgreSQL ou MongoDB et bien d’autres … et le tout par le CLI fourni ou par son API HTTP.

HashiCorp propose deux versions de l’application, une OpenSource et une payante – Vault Enterprise – avec support éditeur et quelques fonctionnalités supplémentaires : l’intégration aux modules HSM, une IHM utilisateur, un dashboard permettant de monitorer la bonne santé de l’instance et un workflow de gestion (init & unseal) améliorés.

L’éditeur est déjà connu pour ses solutions Packer, Terraform ou Consul. Avec Vault il vient compléter son panel d’outillage DevOps et OpenSource.

Nous souhaitons intégrer Vault chez un client où nous automatisons déjà tout, de l’allocation des réseaux aux déploiements des applications finales. Dans un besoin de sécurisation des flux entre ces applications, avoir une PKI as-a-service qui peut générer à la demande des certificats reconnus par l’entreprise devient donc indispensable. Nous vous proposons donc de découvrir Vault et de vous faire une idée de la façon dont Vault peut répondre à ce besoin.

Version utilisée : 0.6.1 (OpenSource et non Enterprise).

Quelques concepts de base

Etat du vault : sealed vs unsealed

Vault, dès lors que l’initialisation a été faite, se présente sous deux états : scellé (sealed) ou descellé (unsealed).

Lorsqu’il est scellé, aucune action n’est possible sur le vault, hormis le changement d’état sealed -> unsealed qui se fait uniquement par le CLI. L’instance Vault repasse au statut scellé lorsqu’un admin en réalise l’action explicite, ou lorsque le service est arrêté et redémarré.

En mode cluster, le statut est porté indépendamment par chacun des nœuds vault et non partagé par tout le cluster. J’ai trouvé ça assez surprenant. Par exemple, cela signifie que chaque nœud mis à jour en rolling upgrade doit ensuite être descellé à la main avant d’être de nouveau actif au sein du cluster.

HashiCorp semble conscient du problème que cela pose pour l’automatisation (Cf documentation dédiée) et annonce des évolutions à ce sujet : wait’n see. N’ayant pas testé le mode HA Cluster, pas possible de vous en dire plus pour l’instant, notamment sur la bonne manière de ne requêter que les nœuds vault descellées.

Des backends à toutes les sauces

BBQ SaucesVault manipule le concept de backend à répétition et selon le contexte il ne s’agit pas de la même chose.

Ceux qui nous intéressent en premier sont les secret backends, dont certains sont dit dynamiques. C’est à dire qu’ils génèrent des secrets / credentials à la demande, directement dans le référentiel cible, avec des droits spécifiques et une durée de vie limitée. Par exemple, Vault peut créer des comptes à la volée dans des instances Cassandra ou postgreSQL avec les droits adéquats sur les données dédiées à l’application cliente. Et le tout est tracé dans un log d’audit.

Au delà des secret backends, vous trouverez aussi :

  • des auth backends, permettant d’authentifier le client qui requète Vault (par ex. LDAP, Certificat client, tokens, compte GitHub etc.)
  • des audit backends, permettant de spécifier où seront écrit les logs d’audit. Seulement deux possibilités : Syslog et file.
  • une section backend dans la configuration du service, qui indique cette fois le service de stockage à utiliser avec plusieurs possibilités parmis file, S3, etcd, consul etc.

backends

Il faut savoir que le choix du backend de stockage n’est pas neutre sur la mise en place du mode cluster pour la haute dispo. Typiquement, l’utilisation du stockage en RAM ou en fichier ne permet pas de faire autre chose qu’une instance standalone.
Du coup, l’archi Vault en illustré et simplifié :
schema_auth

Si vous l’aimez bien cuit

Viande craméeDeux sections de la doc sont intéressantes pour rentrer un peu plus dans l’architecture de Vault et mieux en saisir son fonctionnement :

  • les concepts de base du produit (allègrement piqués par votre serviteur)
  • la partie pour les barbus, qui présente l’archi interne et quelques principes de sécurité (rotation des clés etc.)

J’ajoute un podcast dev.to [en] qui présente Vault par l’un de ses core dev, et le schéma officiel de l’archi de Vault.

Et au delà de Vault, voici un article d’intro à la PKI [en], utile si vous frisez l’indigestion à l’idée de parler de certificats.

Bien, passons à table

L’installation

Vault se présente sous la forme d’un simple binaire GO à télécharger. Du coup, pas de dépendances à tirer et l’installation se limite à le coller dans le répertoire de votre choix, ici /usr/local/bin.

Ce binaire est à la fois le daemon (lancé avec le paramètre server), et le CLI permettant d’attaquer l’API du service. Lancé en mode server, le daemon écrit ses logs dans stdout. Pensez à les récupérer !

Le service

Afin de lancer vault comme service, vous aurez rapidement besoin de spécifier sa configuration. Ici, en utilisant le stockage local en fichiers :

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "backend": {
    "file": {
      "path": "/var/lib/vault/"
    }
  },
  "listener": {
    "tcp": {
      "address": "127.0.0.1:8200",
      "tls_disable": 1
    }
  }
}

Vous pouvez l’exécuter directement dans la console :

$ nohup vault server -config=/etc/vault/config.json &

En passant, voici un exemple de fichier unit de systemd pour démarrer Vault en tant que service. C’est à peu de choses près celui généré par le module Puppet jsok/vault.

$ cat /etc/systemd/system/vault.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# vault systemd unit file
 
[Unit]
 Description=Vault server
 Requires=basic.target network.target
 After=basic.target network.target
 
[Service]
 User=vault
 Group=vault
 PrivateDevices=yes
 PrivateTmp=yes
 ProtectSystem=full
 ProtectHome=read-only
 SecureBits=keep-caps
 Capabilities=CAP_IPC_LOCK+ep
 CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
 NoNewPrivileges=yes
 Environment=GOMAXPROCS=2
 ExecStart=/usr/local/bin/vault server -config=/etc/vault/config.json
 KillSignal=SIGINT
 TimeoutStopSec=30s
 Restart=on-failure
 StartLimitInterval=60s
 StartLimitBurst=3
 
[Install]
 WantedBy=multi-user.target

N.B. cet exemple suppose

  • d’avoir créé l’utilisateur et le groupe vault
  • d’avoir créé et donné les droits d’écriture sur /var/lib/vault (Cf config)
  • et d’avoir écrit la conf dans /etc/vault/config.json

Initialisation de l’instance

Une fois installé et démarré, le vault n’est pas encore utilisable. Il faut l’initialiser, et cela peut se faire par le CLI comme par l’API. La commande vault status permet d’abord d’en vérifier l’état :

$ vault status
 Error checking seal status: Error making API request.

URL: GET http://127.0.0.1:8200/v1/sys/seal-status
 Code: 400. Errors:

* server is not yet initialized

Par défaut, le CLI vault considère que le serveur tourne localement et est accessible en HTTPS. Si comme moi vous faites un POC en HTTP, si votre serveur vault est exécuté sur une machine tierce ou si vous avez changé le port par défaut, vous aurez besoin de spécifier la variable d’environnement VAULT_ADDR pour changer :

$ export VAULT_ADDR=http://127.0.0.1:8200

Ensuite, pour initaliser le vault :

$ vault init
 Unseal Key 1: f991aab557e6d77f85449aba78a54fd104555db77e466162ddcbf33d3a35e7c801
 Unseal Key 2: 0e4a76358e707c92dee0246d8355a3409670c63a612caa01a10f3264e537ef3802
 Unseal Key 3: 1d5f668320c399c856455523fbb1fa2818df14a952e29a7757bba4c4f0bf546d03
 Unseal Key 4: 1cbea470d764e45cfdb8dac9ca587bf4dee4e8942657e6f7cb9350ca0d8074f004
 Unseal Key 5: 0fabb4c679d70106751dab87b2bc229c504b3a071599d6813d27c66a1808cfa505
 Initial Root Token: a0cd73c7-4526-6b37-01ed-67aa8d22b3d8

Vault initialized with 5 keys and a key threshold of 3. Please
 securely distribute the above keys. When the Vault is re-sealed,
 restarted, or stopped, you must provide at least 3 of these keys
 to unseal it again.

Vault does not store the master key. Without at least 3 keys,
 your Vault will remain permanently sealed.

Une fois initialisé, le vault est donc par défaut en état sealed.

$ vault status
 Sealed: true
 Key Shares: 5
 Key Threshold: 3
 Unseal Progress: 0

High-Availability Enabled: false

Si vous fermez votre console, le Root Token ne sera plus chargé dans l’environnement et vous aurez besoin de ça pour ré-utiliser le CLI de Vault :

$ export VAULT_TOKEN=a0cd73c7-4526-6b37-01ed-67aa8d22b3d8

Ouverture et fermeture du vault

Pour desceller le vault il n’y a donc aujourd’hui qu’une seule manière. Cela passe par l’utilisation du CLI et des Unseal Key générées à l’initialisation :

$ vault status
 Sealed: true
 Key Shares: 5
 Key Threshold: 3
 Unseal Progress: 0

High-Availability Enabled: false

$ vault unseal f991aab557e6d77f85449aba78a54fd104555db77e466162ddcbf33d3a35e7c801
 Sealed: true
 Key Shares: 5
 Key Threshold: 3
 Unseal Progress: 1

$ vault unseal 1d5f668320c399c856455523fbb1fa2818df14a952e29a7757bba4c4f0bf546d03
 Sealed: true
 Key Shares: 5
 Key Threshold: 3
 Unseal Progress: 2

$ vault unseal 0fabb4c679d70106751dab87b2bc229c504b3a071599d6813d27c66a1808cfa505
 Sealed: false
 Key Shares: 5
 Key Threshold: 3
 Unseal Progress: 0

Tadaaa !

Et donc pour le refermer, rien de plus simple :

$ vault seal
 Vault is now sealed.

Les opérations de seal/unseal ne peuvent être faites qu’avec les droits root. C’est à dire avec des utilisateurs créés pour l’occasion, ou en utilisant le Root token généré à l’initialisation.

Passons (enfin !) à la PKI

Quelques infos au passage sur les choix d’implémentation de la PKI dans Vault :

  • Les hash sont réalisés en SHA256 minimum (SHA1 écarté depuis la version 0.5.1)
  • Les clés privées sont de 2048 bits minimum

Si ces éléments ne vous parlent pas, je vous renvois à l’article d’intro à la PKI cité en intro.

Montage du backend de PKI

Par défaut, tous les secret backends ne sont pas montés (activés). Seuls cubbyhole, generic et system sont présents :

$ vault mounts
 Path        Type       Default TTL  Max TTL  Description
 cubbyhole/  cubbyhole  n/a          n/a      per-token private secret storage
 secret/     generic    system       system   generic secret storage
 sys/        system     n/a          n/a      system endpoints used for control, policy and debugging

Pour monter pki, rien de plus simple :

$ vault mount pki
 Successfully mounted 'pki' at 'pki'!

$ vault mounts
 Path        Type       Default TTL  Max TTL    Description
 cubbyhole/  cubbyhole  n/a          n/a        per-token private secret storage
 pki/        pki        system       system
 secret/     generic    system       system     generic secret storage
 sys/        system     n/a          n/a        system endpoints used for control, policy and debugging

Point important, dans Vault, le backend pki ne peut gérer qu’une seule CA. Il faut donc le monter autant de fois qu’il y a de CA à gérer dans le vault. Pour éviter les conflits, le chemin et la description peuvent être paramétrés au montage du backend, ainsi que le TTL max des certificats générés :

$ vault mount -path=rootca -description="SBO Root CA" -max-lease-ttl=87600h pki
 Successfully mounted 'pki' at 'rootca'!
$ vault mounts
 Path        Type       Default TTL  Max TTL    Description
 cubbyhole/  cubbyhole  n/a          n/a        per-token private secret storage
 rootca/     pki        system       315360000  SBO Root CA
 secret/     generic    system       system     generic secret storage
 sys/        system     n/a          n/a        system endpoints used for control, policy and debugging

A partir de là, vous pouvez explorer les détails du backend que vous venez de monter, en listant les différents endpoints  :

$ vault path-help pki
 ## DESCRIPTION

The PKI backend dynamically generates X509 server and client certificates.

After mounting this backend, configure the CA using the "pem_bundle" endpoint within
 the "config/" path.

## PATHS

The following paths are supported by this backend. To view help for
 any of the paths below, use the help command with any route matching
 the path pattern. Note that depending on the policy of your auth token,
 you may or may not be able to access certain paths.

^ca(/pem)?$
 Fetch a CA, CRL, or non-revoked certificate.
^cert/(?P<serial>[0-9A-Fa-f-:]+)$
 Fetch a CA, CRL, or non-revoked certificate.
^cert/crl$
 Fetch a CA, CRL, or non-revoked certificate.
^certs/?$
 Fetch a CA, CRL, or non-revoked certificate.
^config/ca$
 Set the CA certificate and private key used for generated credentials.
^config/crl$
 Configure the CRL expiration.
^config/urls$
 Set the URLs for the issuing CA, CRL distribution points, and OCSP servers.
[...]

Pour les plus attentifs, notez que Vault ne supporte pas encore OCSP. C’est annoncé pour une version future. Il existe tout de même un endpoint CRL (liste de révocation de certificats) pour chaque backend pki monté dans le vault si vous en avez l’usage.

Initialisation de la PKI

Pour cette étape il est nécessaire d’importer une autorité de certification. Et en prod, il est fortement recommandé de ne mettre qu’une autorité intermédiaire et non l’autorité racine. En effet, si le vault est corrompu, seule l’intermédiaire (et les certificats générés avec) seront révoqués. La doc de Mozilla vous en dit un peu plus sur la hiérarchie des AC et les chaînes de certification.

Ici, pour tester le produit, nous allons générer une autorité racine et une intermédiaire qui en découle. Plusieurs solutions possibles :

  • Prendre un tuto OpenSSL pour générer les deux CA
  • Utiliser de l’outillage type dummy_ca
  • Récupérer une CA intermédiaire fournie par le service dédié dans votre boîte (cas idéal)
  • Générer directement les deux CA dans Vault

S’agissant d’un POC, je prends ici la 4e solution, bien que cela implique que l’autorité racine soit dans le Vault.

Si vous passez par l’import d’une CA existante, la clé et le certificat doivent être dans un même fichier PEM. Il est possible que la clé ne puisse pas être protégée par une passphrase, mais ce point n’a pas été testé.

Génération de la Root CA

Les étapes à suivre :

  1. Monter un backend pki pour la Root CA
$ vault mount -path=rootca -description="SBO Root CA" -max-lease-ttl=87600h pki
 Successfully mounted 'pki' at 'rootca'!
  1. Générer la Root CA
$ vault write rootca/root/generate/internal common_name="SBO Root CA" ttl=87600h key_bits=4096 exclude_cn_from_sans=true
  1. Vérification :
$ curl -s http://localhost:8200/v1/rootca/ca/pem | openssl x509 -text
 Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:           
            16:00:51:7d:1b:ab:ed:cf:79:a6:45:20:f3:b7:09:cf:96:f2:93:32
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=SBO Root CA
        Validity
            Not Before: Oct  3 13:17:38 2016 GMT
            Not After : Oct  3 13:18:08 2017 GMT
        Subject: CN=SBO Root CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
 [...]

Génération et signature de l’intermediate CA

Les étapes à suivre :

  1. Monter un backend pki pour l’intermediate CA
$ vault mount -path=interca -description="SBO Intermediate CA" -max-lease-ttl=26280h pki
 Successfully mounted 'pki' at 'interca'!
  1. Générer le CSR de l’intermediate CA
$ vault write interca/intermediate/generate/internal common_name="SBO Intermediate CA" ttl=26280h key_bits=4096 exclude_cn_from_sans=true
 Key Value
 --- -----
 csr -----BEGIN CERTIFICATE REQUEST-----
 MIIEYzCCAksCAQAwHjEcMBoGA1UEAxMTU0JPIEludGVybWVkaWF0ZSBDQTCCAiIw
[...]
 NjeCGFuR9AklbBNKuR6MjAqdaawiWnsA9Igx0UsNn/3h+mGDYbKZ5bw3uAQ1znlC
 Rv4a7YqSwhgBpt0g7bBCx85nYNvwVik=
 -----END CERTIFICATE REQUEST-----
  1. Copier le certificat de la sortie standard dans un fichier, /tmp/intermediate.csr.

N.B. les deux CA étant gérées dans deux instances distinctes du backend pki, il faut copier/coller le CSR à la main de l’une vers l’autre.

  1. Signer le CSR avec la Root CA
$ vault write rootca/root/sign-intermediate csr=@/tmp/intermediate.csr common_name="SBO Intermediate CA" ttl=8760h
 Key             Value
 ---             -----
 certificate     -----BEGIN CERTIFICATE-----
 MIIFRzCCAy+gAwIBAgIUfKu69aQViN4wmr2968TUMVO5dzEwDQYJKoZIhvcNAQEL
[...]
 e8d2K0SibGZDzaBHjrf5nCtlzIZRX+6Nre4jwpBto6ThYVQwPilv9TvJkEl+h6H+
 +8k1h9UAcpPloH0=
 -----END CERTIFICATE-----
 expiration      1500997748
 issuing_ca      -----BEGIN CERTIFICATE-----
 MIIFHTCCAwWgAwIBAgIUFgBRfRur7c95pkUg87cJz5bykzIwDQYJKoZIhvcNAQEL
[...]
 vsFoNl6GDrlPuFR9tR+BDrZBQTBBaiRuHH6mO/DUQYFwCYM1XnpgKyKlckUoik81
 aGwtFJ3cjDUe+8PF4PyCCR4=
 -----END CERTIFICATE-----
 serial_number   7c:ab:ba:f5:a4:15:88:de:30:9a:bd:bd:eb:c4:d4:31:53:b9:77:31
  1. Là encore, il faut passer par un fichier temporaire pour importer le certificat signé par la Root CA : /tmp/intermediate.crt
  2. Importer le certificat signé
$ vault write interca/intermediate/set-signed certificate=@/tmp/intermediate.crt
 Success! Data written to: interca/intermediate/set-signed
  1. Vérifier que l’intermediate CA est bien en place
$ curl -s http://localhost:8200/v1/interca/ca/pem | openssl x509 -text | head -20
 Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            7c:ab:ba:f5:a4:15:88:de:30:9a:bd:bd:eb:c4:d4:31:53:b9:77:31
 Signature Algorithm: sha256WithRSAEncryption
     Issuer: CN=SBO Root CA
[...]

Génération des premiers certificats

Création d’un role

Pour faire les choses un peu proprement, Vault permet de créer des rôles qui seront associés à des droits. Typiquement, un utilisateur ou un service client peuvent ainsi être limités dans leurs demandes de certificats sur un sous-domaine DNS précis.

Ici, nous créons un role limité à un domaine TLD, avec un TTL par défaut et qui autorise la création de certificats sur ses sous-domaines :

$ vault write interca/roles/aws-dot-octo allowed_domains="aws.octo" allow_subdomains="true" max_ttl="72h"
 Success! Data written to: interca/roles/aws-dot-octo

Génération du certificat serveur

C’est à partir de là que ça se simplifie. Il suffit de requêter vault avec les bons droits pour obtenir un certificat sur le common_name en paramètre.

$ vault write interca/issue/aws-dot-octo common_name=vault.aws.octo
 Key                 Value
 ---                 -----
 lease_id            interca/issue/aws-dot-octo/784185dc-cfc3-8214-fe89-c28c100d7dcc
 lease_duration      259199
 lease_renewable     false
 certificate         -----BEGIN CERTIFICATE-----
 MIIERTCCAi2gAwIBAgIUJ8Wg1SItTEf3/fps12mg9yPbtIMwDQYJKoZIhvcNAQEL
 BQAwHjEcMBoGA1UEAxMTU0JPIEludGVybWVkaWF0ZSBDQTAeFw0xNjA3MjUxNjEw
[...]
 GsO6qAaHNCwD2kU3+7No+7s1vQQqjD0pLc7HrYj14m8IdW8J5xdpDHjUYhSEv/3n
 RCJdvzr3KCbvYZjdep/kYoH6Nbm/BgKqV/CvNZEAzl+j9nWt5kHe7IU=
 -----END CERTIFICATE-----
 issuing_ca          -----BEGIN CERTIFICATE-----
 MIIFRzCCAy+gAwIBAgIUfKu69aQViN4wmr2968TUMVO5dzEwDQYJKoZIhvcNAQEL
[...]
 e8d2K0SibGZDzaBHjrf5nCtlzIZRX+6Nre4jwpBto6ThYVQwPilv9TvJkEl+h6H+
 +8k1h9UAcpPloH0=
 -----END CERTIFICATE-----
 private_key         -----BEGIN RSA PRIVATE KEY-----
 MIIEpAIBAAKCAQEAwsdta3qu0BCqE+ER9lQv7BGMERiY9mmBQtpnCmVg4kS+sWP5
[...]
 GvibqBkWmxCBJOV66WTvSNBW7Vzej6ET8T8jTkDHc3Se3TvlMRQ0bIzqX96SBVWs
 DixVMDRih0ipT0I0+ypu/A/XeGFJh+v4fj0itoYDt/rWq3PK5dlhCQ==
 -----END RSA PRIVATE KEY-----
 private_key_type    rsa
 serial_number       27:c5:a0:d5:22:2d:4c:47:f7:fd:fa:6c:d7:69:a0:f7:23:db:b4:83

Exposition de l’API Vault avec un certificat

Avec le bac certificat en poche, nous pouvons exposer l’API de vault en HTTPS.

Ici, deux méthodes possibles :

  • charger le certificat dans la conf vault, et modifier les endpoints de chaque secret backend
  • utiliser un reverse proxy local pour porter la terminaison TLS (offloading)

Vis-à-vis de notre environnement cible, c’est la seconde option qui nous intéresse. Ceci dit, si vous tentez la première, les commandes ci-dessous devraient vous être utiles (N.B. non testées).

$ vault write rootca/config/urls issuing_certificates="https://vault.aws.octo/v1/rootca"
$ vault write interca/config/urls issuing_certificates="https://vault.aws.octo/v1/interca"

Pour NGINX, il est nécessaire de créer un unique fichier avec le certificat serveur + l’intermediate CA. Il est probable que ce même fichier soit nécessaire pour charger le certificat dans Vault.

J’ai donc le certificat serveur suivi du certificat de la CA intermédiaire dans /etc/nginx/vault.crt et la clé du certificat serveur dans /etc/nginx/vault.key. Ma config NGINX de test est générée dans /etc/nginx/conf.d/vault.conf, et ça donne :

# cat /etc/nginx/conf.d/vault.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 server {
  listen       443;
  server_name  vault.aws.octo;
  
  ssl_certificate           /etc/nginx/vault.crt;
  ssl_certificate_key       /etc/nginx/vault.key;
  
  ssl on;
  ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
  ssl_prefer_server_ciphers on;
  
  access_log            /var/log/nginx/vault.access.log;
  error_log             /var/log/nginx/vault.error.log;
  
  location / {
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    
    proxy_pass          http://localhost:8200;
    proxy_read_timeout  90;
    
    proxy_redirect      http://localhost:8200 https://vault.aws.octo;
  }
}

Une fois la Root CA chargée dans Firefox, accéder à l’API nous donne un joli cadenas vert :

certifvalide-octo

Et en passant par l’API ?

Rien de bien sorcier ici, le CLI reprenant déjà les chemins des endpoints de l’API. Suivant la requête vous aurez besoin d’être authentifié. Par exemple, pour lister les certificats issus de la CA intermédiaire :

$ curl https://vault.aws.octo/v1/interca/certs/?list=true
 {"errors":["missing client token"]}

Pour passer le token dans la requête, ça passe par le header HTTP X-Vault-Token :

$ curl -H "X-Vault-Token:a0cd73c7-4526-6b37-01ed-67aa8d22b3d8" https://vault.aws.octo/v1/interca/certs/?list=true
 {"lease_id":"","renewable":false,"lease_duration":0,"data":{"keys":["7c:ab:ba:f5:a4:15:88:de:30:9a:bd:bd:eb:c4:d4:31:53:b9:77:31","27:c5:a0:d5:22:2d:4c:47:f7:fd:fa:6c:d7:69:a0:f7:23:db:b4:83"]},"wrap_info":null,"warnings":null,"auth":null}

Pensez à importer la Root CA sur votre système pour utiliser curl, ou à passer l’option -k pour passer outre la vérification du certificat.

Autre exemple d’utilisation de l’API, sceller / desceller le vault à distance :

$ curl https://vault.aws.octo/v1/sys/seal-status
 {"sealed":false,"t":3,"n":5,"progress":0}

$ curl -X PUT -H "X-Vault-Token:a0cd73c7-4526-6b37-01ed-67aa8d22b3d8" https://vault.aws.octo/v1/sys/seal

$ curl https://vault.aws.octo/v1/sys/seal-status
 {"sealed":true,"t":3,"n":5,"progress":0}

$ curl -X PUT -H "Content-Type: application/json" -d '{"key":"1cbea470d764e45cfdb8dac9ca587bf4dee4e8942657e6f7cb9350ca0d8074f004"}' https://vault.aws.octo/v1/sys/unseal
 {"sealed":true,"t":3,"n":5,"progress":1}

$ curl -X PUT -H "Content-Type: application/json" -d '{"key":"0fabb4c679d70106751dab87b2bc229c504b3a071599d6813d27c66a1808cfa505"}' https://vault.aws.octo/v1/sys/unseal
 {"sealed":true,"t":3,"n":5,"progress":2}

$ curl -X PUT -H "Content-Type: application/json" -d '{"key":"0e4a76358e707c92dee0246d8355a3409670c63a612caa01a10f3264e537ef3802"}' https://vault.aws.octo/v1/sys/unseal
 {"sealed":false,"t":3,"n":5,"progress":0}

Activation des logs d’audit

Curieusement, l’audit log n’est pas activé par défaut. S’agissant d’une solution tournée vers la sécurité, je m’attendais à ce que ce soit le cas.

Comme indiqué en introduction, deux backends seulement pour les logs d’audit : file ou syslog. Pour l’exemple, l’activation de l’audit sera faite dans un fichier :

$ vault audit-enable file file_path=/var/log/vault/vault_audit.log
 Successfully enabled audit backend 'file' with path 'file'!

Globalement, toutes les opérations sont tracées. Un exemple de ce que l’on peut y trouver (un JSON à plat par ligne) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
   "time":"2016-07-29T09:48:04Z",
   "type":"request",
   "auth":{
      "display_name":"root",
      "policies":[
         "root"
      ],
      "metadata":null
   },
   "request":{
      "operation":"read",
      "client_token":"hmac-sha256:e6d0329cc39edf9c6de542f64bf8e32f72ee78035b95a926c1155ca840f73ca5",
      "path":"sys/audit",
      "data":null,
      "remote_address":"127.0.0.1",
      "wrap_ttl":0
   },
   "error":""
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
   "time":"2016-07-29T09:48:04Z",
   "type":"response",
   "error":"",
   "auth":{
      "display_name":"root",
      "policies":[
         "root"
      ],
      "metadata":null
   },
   "request":{
      "operation":"read",
      "client_token":"hmac-sha256:e6d0329cc39edf9c6de542f64bf8e32f72ee78035b95a926c1155ca840f73ca5",
      "path":"sys/audit",
      "data":null,
      "remote_address":"127.0.0.1",
      "wrap_ttl":0
   },
   "response":{
      "secret":null,
      "data":{
         "file/":{
            "description":"hmac-sha256:ead5aa37a0c93d9052d5b7ba263fa45ac807bc701c103e8e90c7b240e363def8",
            "options":{
               "file_path":"hmac-sha256:4ae60e33e6aff1622a895c1ab946f6c0b239bca7f249451a1c0cf90bbd98fff4"
            },
            "path":"hmac-sha256:0fed33ad9d6f7046c061115022491dce64a741a8f83dd5540e3a917bc5bbf1ce",
            "type":"hmac-sha256:f5b5bf7bb11d0b0d09d61e78747a96c90986e3a0fc789092d7970225c62f585e"
         }
      },
      "redirect":""
   }
}

Et la suite ?

Vault répond plutôt bien aux besoins énoncés en intro, et nous le déployons depuis dans un contexte AWS / Puppet.
Le mode cluster n’est pas encore d’actualité, mais l’intégration avec AWS et le Puppet Master comportent déjà son lot d’éléments intéressants. Nous vous les partagerons très probablement dans un second article.

Sources / liens