Eigentlich hatte ich das Thema ja schon ad acta gelegt aber dann hat es mich doch nicht losgelassen.
Also hier nochmal die Problemstellung: Wenn man das Consul Service Mesh benutzen möchte und dazu auch noch Consul Datacenter über WAN föderieren will, muß man ACLs aktivieren und das führt dazu, dass jeder Consul-Client ein "Node-Identity" Token benötigt, um überhaupt dem Cluster beitreten zu können. In statischen Umgebungen ist das natürlich kein großes Problem. Anders sieht es aus, wenn man mit dem Nomad-Autoscaler dem Cluster dynamisch Nodes hinzufügen (und auch wieder entfernen) will. Dann muß das Token dynamisch erzeugt werden.
Dafür gibt es in der Consul Konfiguration den Abschnitt auto_config
. Netterweise gibt es zu dem Thema auch gleich ein Tutorial.
Im Wesentlichen hilft auto_config
dabei, einen (neuen) Consul Client mit wichtigen Teilen der Konfiguration zu versorgen. Insbesondere den relevanten Zertifikaten, dem Encryption-Key und eben dem Node-Identity Token. Das geht, in dem der Client einem Server ein JWT "vorzeigt" und sich dann die genannten Daten herunterlädt.
Aus unerfindlichen Gründen hatte ich in dem Tutorial zwar den Hinweis auf secint gelesen, hatte dann aber den dazugehörigen "Learning Path" übersehen und war damit nicht weitergekommen. Ich hatte zum Beispiel erst nach dem Durchsehen von
verstanden, dass für jeden (neuen) Node ein eigenes JWT erzeugt werden muß (und nicht eins für alle).
Secint ist für die Leute, die auto_config
machen aber keinen Vault-Cluster dafür aufsetzen wollen. Also installiert man sich auf einem Server im Cluster secint (z. B. mit go install github.com/banks/secint@latest
). Ich habe dazu gleich auch noch einen entsprechenden User angelegt und diesem ein SSH-Schlüsselpaar erzeugt. Auch für den Root-User auf meinen Consul-Clients habe ich ein Schlüsselpaar erzeugt und den öffentlichen Teil im Home-Verzeichnis vom User secint in .ssh/authorized_keys
abgespeichert.
Jetzt kann sich ein Skript beim Start des neuen Consul Clients mit
hostn=`cat /etc/hostname` ; ssh secint@server "secint mint -issuer secint -ttl 1h -node $hostn -priv-key secint-priv-key.pem -audience prod1" > /etc/consul/tokens/jwt
beim Secint-Server per ssh einloggen, ein JWT erzeugen und direkt in die Datei schreiben, die im Consul-Client konfiguriert ist. Die Konfigurationsdatei schmilzt dann etwas zusammen:
datacenter = "prod1"
primary_datacenter = "prod2"
data_dir = "/opt/consul"
log_level = "INFO"
node_name = "nomad-client-0"
server = false
leave_on_terminate = true
ca_file = "/etc/consul/certificates/ca.pem"
bind_addr = "0.0.0.0"
client_addr = "0.0.0.0"
advertise_addr = "{{ GetInterfaceIP \"ens3\" }}"
advertise_addr_wan = "1.2.3.4"
translate_wan_addrs = true
ports {
https = 8501
grpc = 8502
grpc_tls = 8503
}
verify_incoming = false
verify_outgoing = true
verify_server_hostname = true
auto_config {
enabled = true
intro_token_file = "/etc/consul/tokens/jwt"
server_addresses = ["provider=os tag_key=consul-role tag_value=server auth_url=https://prod1.api.pco.get-cloud.io:5000 user_name=auseradm domain_name=adomain password=\"apassword\" region=prod1"]
}
Die zugehörige Serverkonfiguration ist selbstverständlich um den auto_config
Absatz gewachsen:
auto_config {
authorization {
enabled = true
static {
jwt_validation_pub_keys = ["-----BEGIN PUBLIC KEY-----\npublicsecintkey\n-----END PUBLIC KEY-----\n"]
bound_issuer = "secint"
bound_audiences = ["prod1"]
claim_mappings {
sub = "node_name"
}
claim_assertions = [
"value.node_name == \"${node}\""
]
}
}
}
Natürlich muß man auch an dieser Stelle wieder mit systemd kämpfen. Einerseits sollte der Consul-Client erst starten, wenn das JWT an der richtigen Stelle liegt und andererseits funktioniert in meiner aktuellen Konfiguration (mit systemd-resolved
) kein DNS, wenn Consul nicht läuft.
DNS muß aber laufen, damit das Cloud auto_join
des Clients funktioniert. Also muß vermutlich von systemd-resolved
auf dnsmasq
umgestellt werden, da systemd-resolved
ein wenig störrisch ist, wenn es darum geht, für Anfragen für die TLD consul
den lokalen Consul-Client zu fragen und für alle anderen Domains doch bitte die "normalen" DNS-Server.
Aber wenn diese Hürde überwunden ist, wird "wie durch ein Wunder" die gewünschte Konfiguration übertragen und ein Token erzeugt so dass der neue Client dem Cluster beitreten kann.
Im Log steht dann etwas lapidar:
Mar 24 10:39:36 nomad-client-0 consul[9919]: 2024-03-24T10:39:36.771Z [INFO] agent.auto_config: retrieving initial agent auto configuration remotely
Mar 24 10:39:37 nomad-client-0 consul[9919]: 2024-03-24T10:39:37.856Z [INFO] agent.auto_config: auto-config started
Die Token kann man dann auf den Servern mit consul acl token list
sehen:
root@consul-0:~# consul acl token list |grep "Auto Config"
Description: Auto Config Token for Node "nomad-client-0"
Description: Auto Config Token for Node "nomad-client-0"
Description: Auto Config Token for Node "nomad-client-0"
Und die übertragene Konfiguration findet sich als auto-config.json
auf dem Client im data_dir
:
root@nomad-client-0:/opt/consul# ls -la
total 28
drwxr-xr-x 3 consul consul 4096 Mar 24 10:40 .
drwxr-xr-x 6 root root 4096 Mar 24 09:49 ..
-rw-r----- 1 consul consul 7788 Mar 24 10:39 auto-config.json
-rw-r--r-- 1 consul consul 394 Mar 24 10:40 checkpoint-signature
-rw------- 1 consul consul 36 Mar 24 10:39 node-id
drwx------ 2 consul consul 4096 Mar 24 10:39 serf