Ein bisschen sperrig die Überschrift heute, also worum soll es gehen? Ich will sicher und automatisiert verschiedene Dateien von einem Server (troubadix) auf einen anderen (susan) übertragen und dann noch extra Kommandos ausführen und das ganze aber so gut wie möglich absichern. Ganz konkret geht es darum die Zertifikatsdateien die mir acme.sh in der virtuellen Maschine für owncloud beschert, auf die andere VM mit dem reverse proxy zu übertragen.
Dies ist Teil zwei einer Mini-Serie, im ersten Teil ging’s um letsencrypt mit acme.sh und lighttpd. Im zweiten Teil hier beschreibe ich den unbeaufsichtigten Zugriff mit SSH, beispielsweise für mit cron auszuführende Skripte. Im dritten Teil wird das ganze dann aufgebohrt für die Verwendung mit rsync.
SSH-Zugriff mit Zertifikat
Auftritt OpenSSH. Die Secure Shell kann sehr sehr nützlich sein. Für den automatisierten Betrieb ohne Eingriff des Nutzers bieten sich Zertifikate an. Da diese dann kein Passwort haben dürfen, das könnte ja niemand eingeben, will man den Zugriff gesondert absichern, doch zunächst erstellen wir erstmal auf dem Quellsystem so ein Zertifikat mit ssh-keygen
:
Generating public/private rsa key pair. Enter file in which to save the key (/home/alex/.ssh/id_rsa): /home/alex/.ssh/id_rsa_acme Enter passphrase (empty for no passphrase): Your identification has been saved in /home/alex/.ssh/id_rsa_acme. Your public key has been saved in /home/alex/.ssh/id_rsa_acme.pub. The key fingerprint is: d4:9e:67:2b:0a:de:f8:2a:95:b4:ce:cd:8f:07:b8:d1 alex@troubadix
Wie man leicht sieht, hab ich den per default angegebenen Pfad geändert, um einen separaten Key nur für diesen Zweck zu erstellen. Bei der Passphrase drückt man zweimal direkt Enter ohne etwas einzugeben. Wichtig ist dann die Datei ~/.ssh/id_rsa_acme.pub
mit dem public key. Den Inhalt dieser Datei hängen wir auf dem Zielsystem an die Datei ~/.ssh/authorized_keys
an:1
Erst:
alex@troubadix ~ % cat ~/.ssh/id_rsa_acme.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9VKFUIcc+vfL3a9oFNeuITghF7+QYtTvlpsX7CHG/UmEIIIpz31n6TEoSCL0a4sA6Xx2Z+peGbYD1RRzH/6Shis/4L+R2HPSA+1TqLKT4fdyF9Xkbj9E/bbmrXx5fH8DZnYThIJ15mVHUO7eALU2ELxa+mwehxPbV070qFo1Y1lGa93dhJSdXMerFQE7YPf8UXlb1ULJA+AneQNMymvSBJU86VycMdk/Tb7p1dqepKGVEmS/IFwh9Q6eGQ87JWJGa2BEbVpJMrwucN2So6/06fPpEp7ovxJhIlwXny6jfofpBsr0NZe3yknPk3nCxkPTpLMNOTSBfm8Nva4++firV alex@troubadix
Dann copy und paste und:
alex@susan ~ % echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9VKFUIcc+vfL3a9oFNeuITghF7+QYtTvlpsX7CHG/UmEIIIpz31n6TEoSCL0a4sA6Xx2Z+peGbYD1RRzH/6Shis/4L+R2HPSA+1TqLKT4fdyF9Xkbj9E/bbmrXx5fH8DZnYThIJ15mVHUO7eALU2ELxa+mwehxPbV070qFo1Y1lGa93dhJSdXMerFQE7YPf8UXlb1ULJA+AneQNMymvSBJU86VycMdk/Tb7p1dqepKGVEmS/IFwh9Q6eGQ87JWJGa2BEbVpJMrwucN2So6/06fPpEp7ovxJhIlwXny6jfofpBsr0NZe3yknPk3nCxkPTpLMNOTSBfm8Nva4++firV alex@troubadix" >> ~/.ssh/authorized_keys
An dieser Stelle empfiehlt es sich den Zugriff über diesen Key vom Quell- auf das Zielsystem zu testen:
ssh -i ~/.ssh/id_rsa_acme susan
Das sollte ohne Passworteingabe funktionieren!
Posthook für acme.sh
Auf dem Zielsystem benötigen wir für den nginx zwei verschiedene Dateien, die wir mit rsync übertragen wollen. Außerdem soll dort der nginx diese auch einlesen, muss also einmal neu geladen werden. Die entsprechenden Befehle sind schnell ausgemacht und in ein Skript posthook.sh
übertragen, das dann in den Optionen von acme.sh eingetragen wird.
#!/bin/sh ACMEDIR=/home/alex/.acme.sh DOMAIN=owncloud.home.example.com ${ACMEDIR}/create-lighty-pemfile.sh $DOMAIN rsync -e 'ssh -i /home/alex/.ssh/id_rsa_acme' ${ACMEDIR}/${DOMAIN}/fullchain.cer susan:/home/alex/.cert/${DOMAIN}/ rsync -e 'ssh -i /home/alex/.ssh/id_rsa_acme' ${ACMEDIR}/${DOMAIN}/${DOMAIN}.key susan:/home/alex/.cert/${DOMAIN}/ ssh -i /home/alex/.ssh/id_rsa_acme susan sudo -n service nginx reload
Der Zielordner auf dem anderen System soll /home/alex/.cert
sein und wurde zuvor angelegt und natürlich muss der interne DNS susan als hostname sinnvoll auflösen können. Die Änderung an der /etc/sudoers
auf dem Zielrechner ist trivial:
alex ALL = NOPASSWD: /usr/sbin/service nginx reload
Kann man bis hierher nochmal testen, soweit so gut, aber der Zugriff soll jetzt eingeschränkt werden.
SSH auf ein Kommando einschränken
Bis hierhin könnte nun jeder, der auf der Maschine troubadix in den Besitz des Schlüssels id_rsa_acme
gelangt sich auf susan einloggen und dort als Nutzer alex beliebigen Unfug anstellen. Das soll weiter eingeschränkt werden und dazu nutzen wir die bereits bekannte Datei ~/.ssh/authorized_keys
. In der Manpage2 heißt es im Abschnitt AUTHORIZED_KEYS FILE FORMAT zur Option command:
Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify no-pty. A quote may be included in the command by quoting it with a backslash. This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited. The command originally supplied by the client is available in the SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. Also note that this command may be superseded by either a sshd_config(5) ForceCommand directive or a command embedded in a certificate.
Wir öffnen also auf dem Zielsystem nochmal die Datei ~/.ssh/authorized_keys
und suchen die zuvor hinzugefügte Zeile. Durch Voranstellen der Option command lässt sich nun das auszuführende Kommando festlegen. Mit anderen Worten: bei Authentifizierung mit dem zuvor generierten Key, kann der Nutzer nichts anderes mehr ausführen, es wird nur das in authorized_keys festgelegte Kommando ausgeführt. Beispiel:
command="/bin/date" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9VKFUIcc+vfL3a9oFNeuITghF7+QYtTvlpsX7CHG/UmEIIIpz31n6TEoSCL0a4sA6Xx2Z+peGbYD1RRzH/6Shis/4L+R2HPSA+1TqLKT4fdyF9Xkbj9E/bbmrXx5fH8DZnYThIJ15mVHUO7eALU2ELxa+mwehxPbV070qFo1Y1lGa93dhJSdXMerFQE7YPf8UXlb1ULJA+AneQNMymvSBJU86VycMdk/Tb7p1dqepKGVEmS/IFwh9Q6eGQ87JWJGa2BEbVpJMrwucN2So6/06fPpEp7ovxJhIlwXny6jfofpBsr0NZe3yknPk3nCxkPTpLMNOTSBfm8Nva4++firV alex@troubadix
Versuche ich mich jetzt von der Quellmaschine einzuloggen und ein anderes Kommando (hier bspw. uname
) auszuführen:
alex@troubadix ~ % ssh -4 -i ~/.ssh/id_rsa_acme susan uname -a So 30. Okt 10:08:47 CET 2016
Wie man sieht, wurde nicht uname -a
ausgeführt, sondern /bin/date
– diesen Mechanismus werden wir nun nutzen, um nur die in unserem posthook-Skript benötigten Befehle zu erlauben. Ein kleines Problem gibt es noch: wir können nur ein Kommando in authorized_keys eintragen, wollen aber mehrere Kommandos (rsync, Neustart des nginx) erlauben. Dazu schreiben wir uns auf dem Zielsystem ein Wrapper-Skript, das wir dann in der authorized_keys eintragen.
Einfaches Wrapper-Skript für SSH
In der oben zitierten Manpage wurde die Umgebungsvariable SSH_ORIGINAL_COMMAND
erwähnt, die wir uns jetzt zunutze machen werden. Zunächst mal mit einem sehr einfachen Beispiel:
#!/bin/sh if [ -n "$SSH_ORIGINAL_COMMAND" ] then echo "`/bin/date`: $SSH_ORIGINAL_COMMAND" >> $HOME/ssh-command-log exec $SSH_ORIGINAL_COMMAND fi
Auf dem Zielsystem susan wird jetzt in authorized_keys
dieses Skript eingetragen, statt dem zuvor probeweise eingetragenem /bin/date
und dann passiert folgendes: Wenn SSH_ORIGINAL_COMMAND
gesetzt ist, wird dessen Inhalt in der Datei $HOME/ssh-command-log
geloggt und dieser Befehl dann ausgeführt. Test hier: der Befehl muss erfolgreich ausgeführt werden und der korrekte Befehl muss in der Logdatei stehen. Führen wir auf dem Quellsystem troubadix jetzt beispielsweise das zu Anfang erstellte posthook Skript von acme.sh aus, tauchen derartige Einträge in der Logdatei auf susan auf:
Do 8. Dez 22:25:06 CET 2016: rsync --server -e.Lsfx . owncloud.home.example.com/ Do 8. Dez 22:25:06 CET 2016: rsync --server -e.Lsfx . owncloud.home.example.com/ Do 8. Dez 22:25:07 CET 2016: sudo -n service nginx reload
Im dritten Teil der Serie wird es dann darum gehen, die Zugriffe auf Basis von SSH_ORIGINAL_COMMAND einzuschränken.
Pingback: letsencrypt mit acme.sh und lighttpd | antiblau blog
Pingback: Automatisches rsync über SSH absichern | antiblau blog