HashiCorp Vault Setup SSH Authentication mit Ansible und Terraform


Bicycle

Die allermeisten HashiCorp Vault Anleitungen - auch die von HashiCorp selbst - benutzen die Tools oder einfach nur curl um HashiCorp Vault zu konfigurieren. Aber eigentlich wollen wir ja beides nicht verwenden, wenn wir stattdessen lieber terraform Code benutzen können.

Terraform hat bereits einen Provider für HashiCorp Vault, der es uns ermöglicht diese Aufgaben durchzuführen, und so unsere Installation zu warten.

Wir werden hier auch Ansible verwenden, um die One Time Password ( OTP ) Authentifizierung auf der Infrastruktur auszurollen. Folgende Möglichkeiten ergeben sich durch die Verwendung von HashiCorp Vault

  • Zugriffskontrolle auf Infrastruktur mit Subnetz-Bereich als Faktor
  • Kontrolle über die Gültigkeitsdauer eines OTP

Es müssen hier weder SSH Schlüssel gewartet noch Passwörter über das Netzwerk verbreitet werden.

Konfiguration von HashiCorp Vault mit Terraform

Wir starten hier mit Terraform Code, um HashiCorp Vault zu konfigurieren so, dass man einen derartigen Endpunkt hat, der diese Passwörter ausgeben sowie validieren kann.

Der erste Block definiert den Zugriff auf unsere Vault Installation und erstellt zugleich ein Backend in Vault vom Typ SSH. Dieses wird auch unter dem Standardpfad ssh verbunden.

Das können wir jederzeit auch ändern, um es unseren Bedürfnissen anzupassen - jedoch muss diese Änderung auch im Ansible Code parallel dazu erfolgen. Innerhalb von Terraform wird diese Konfiguration einfach weitergegeben und bedarf keinerlei Änderungen.

provider "vault" {
  address = "http://vault.local:8200/"
}

resource "vault_mount" "ssh" {
  type = "ssh"
  path = "ssh"

  default_lease_ttl_seconds = "14400"  # 4h
  max_lease_ttl_seconds     = "604800" # 1w

}

Nun müssen wir noch eine Rolle vom Typ otp definieren. Diese Resource ermöglicht es uns gültige Benutzernamen zu definieren, sowie wie lange diese Passwörter gültig sind.

Außerdem definieren wir hier noch einen Netzwerkbereich für den diese Resource gültige Passwörter ausstellen kann. In unserem Beispiel sind die Benutzer centos und ubuntu möglich, sowie der Netzwerkbereich 192.168.0.0/24. Die Gültigkeitsdauer schränken wir auf 30 Minuten ein.

variable "ssh_cidr" {
    default = "192.168.0.0/24"
}

resource "vault_ssh_secret_backend_role" "otp" {
  name     = "otp"
  backend  = vault_mount.ssh.path
  key_type = "otp"

  allowed_extensions = "permit-pty,permit-port-forwarding"
  default_user       = "centos"
  allowed_users      = "centos,ubuntu"
  cidr_list          = var.ssh_cidr

  ttl = "30m"

}

Damit unsere Clients auch diese Resource nutzen können, bedarf es noch einer Regel. Diese kann dann den einzelnen Benutzern bzw Benutzergruppen innerhalb von Vault zugewiesen werden.

resource "vault_policy" "ssh_client_access" {
  name   = "ssh_clients"
  policy = <<EOT
path "${vault_mount.ssh.path}/creds/otp" {
    capabilities = ["create", "read", "update"]
}
EOT
}

Zum Abschluss können wir nun diese Änderungen anwenden:

1export VAULT_TOKEN="my-vault-token"
2terraform apply

Zu diesem Zeitpunkt ist HashiCorp Vault so konfiguriert, dass es uns One Time Passwörter zur SSH Authentifizierung ausstellen kann.

Ansible um SSH One Time Passwort Authentifizierung auszurollen

Ansible Rolle

Für Ansible gibt es bereits einige von der Community geschriebene Rollen für HashiCorp Vault. Allerdings ist keine davon aktuell zu dem Zeitpunkt des Artikels. So haben wir eine Variante geklont und mit ein paar Änderungen versehen, damit sie alle Funktionen unterstützt, die wir brauchen. Diese Rolle kann mit dem folgenden ansible-galaxy Befehl installiert werden:

1ansible-galaxy install https://github.com/infralovers/ansible-vault-otp

Die Rolle verwendet den vault-ssh-helper für eine Konfiguration der Infrastruktur, um hier die Authentifizierung mittels Vault zu ermöglichen. Dieser SSH Helper validiert das passwort beim SSH Login gegen unsere Vault Konfiguration.

Ansible Rollout

Um die Rolle zu verwenden muss in einem Ansible Playbook folgendes verwenden werden:

1- hosts: all
2  become: yes
3  vars:
4  - vault_addr: http://vault.local:8200
5  - ssh_mount_point: ssh
6  roles:
7  - role: infralovers.vault_otp

Wenn ein Vault mit gültigem HTTPS Zertifikat verwendet wird, muss man auch die Variable vault_ca_cert_file auf die jeweilige Zerftifikatsdatei setzen.

Benutzen der One Time Passwort Authentifizierung

Um diese Funktionalität auch benutzen zu können verwenden wir in diesem Beispiel nur curl, wofür wir uns aber eine Funktion schreiben und diese zum Beispiel in .bashrc hinzufügen.

Die folgende Funktion erstellt eine Anfrage an HashiCorp Vault für eine spezifische IP Adresse bzw Hostnamen. Die Funktion sucht auch in unserem Ansible Inventory nach dem Host um diesen eventuell aufzulösen. Wenn es hier nicht fündig wird, wird auch noch die SSH Konfiguration verwendet, um die IP Adresse zu ermitteln. Zu guter Letzt wird noch eine DNS Abfrage versucht. Optional kann auch der Benutzername übergeben werden, dieser wird aber auch versucht aus der SSH Konfiguration auszulesen - standard wird hier einfach ubuntu momentan verwendet.

 1function vault_otp() {
 2
 3    VAULT_ADDR="http://vault.local:8200"
 4    SSH_MOUNT="ssh"
 5    SSH_ROLE="otp"
 6    ANSIBLE_INVENTORY=${ANSIBLE_INVENTORY:-/full/path/to/hosts.ini}
 7    REMOTE_USER="ubuntu"
 8
 9    if [ -z "$VAULT_TOKEN" ]; then
10        echo "missing vault token!"
11        return
12    fi
13    HOSTNAME=$1
14    if [ -n "$2" ]; then
15        REMOTE_USER="$2"
16    fi
17    if [ -z "$VAULT_TOKEN" ]; then
18        echo "missing vault token!"
19        return
20    fi
21
22    IP=$(ansible-inventory -i "$ANSIBLE_INVENTORY" --host "$HOSTNAME" 2>/dev/null | jq -r '.ansible_host')
23
24    if [ -z "$IP" ]; then
25        IP=$(ssh -G "$HOSTNAME" | awk '$1 == "hostname" p{ print $2 }')
26    fi
27    CONFIGURED_USER=$(ssh -G "$HOSTNAME" | awk '$1 == "user" p{ print $2 }')
28    if [ -n "$CONFIGURED_USER" ]; then
29      REMOTE_USER="$CONFIGURED_USER"
30    fi
31
32    if [ -z "$IP" ]; then
33        IP=$HOSTNAME
34    fi
35
36    VALIDATE=$(dig +short "$IP")
37    if [ -n "$VALIDATE" ]; then
38        IP=$VALIDATE
39    fi
40    OTP=$( curl \
41            --header "X-Vault-Token: $VAULT_TOKEN" \
42            --request POST \
43            --data "{ \"username\": \"$REMOTE_USER\", \"ip\": \"${IP}\" }" \
44            $VAULT_ADDR/v1/$SSH_MOUNT/creds/$SSH_ROLE | jq -r '.data.key')
45
46    echo $OTP
47
48}

Danach kann man sich relativ simpel ein derartiges Passwort anfordern und benutzen:

 1$ vault_otp my-server
 2d7a79b69-b3d6-ccce-9d2d-ce9a177b8e73
 3$ ssh ubuntu@my-server
 4Password: d7a79b69-b3d6-ccce-9d2d-ce9a177b8e73
 5Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-1025-raspi aarch64)
 6
 7 * Documentation:  https://help.ubuntu.com
 8 * Management:     https://landscape.canonical.com
 9 * Support:        https://ubuntu.com/advantage
10
11  System information as of Wed Mar  3 10:16:00 UTC 2021
12
13  System load:  0.08               Temperature:           42.3 C
14  Usage of /:   10.6% of 29.05GB   Processes:             147
15  Memory usage: 45%                Users logged in:       0
16  Swap usage:   0%                 IPv4 address for eth0: 1.2.3.4
17
18 * Introducing self-healing high availability clusters in MicroK8s.
19   Simple, hardened, Kubernetes for production, from RaspberryPi to DC.
20
21     https://microk8s.io/high-availability
22
230 updates can be installed immediately.
240 of these updates are security updates.
25
26
27The list of available updates is more than a week old.
28To check for new updates run: sudo apt update
29
30Last login: Wed Mar  3 00:00:00 2021 from 127.0.0.1
31ubuntu at my-server in ~
Zurück Unsere Trainings entdecken

Wir sind für Sie da

Sie interessieren sich für unsere Trainings oder haben einfach eine Frage, die beantwortet werden muss? Sie können uns jederzeit kontaktieren! Wir werden unser Bestes tun, um alle Ihre Fragen zu beantworten.

Hier kontaktieren