From 645b1a003c391b348d2f1c2e04ba73c2f575fbda Mon Sep 17 00:00:00 2001
From: Kristian Klausen <kristian@klausen.dk>
Date: Fri, 28 Jul 2023 19:13:55 +0200
Subject: [PATCH] Add small script to fetch SSH keys from the EC2 metadata
 service

This is meant to be used in the Hetzner cloud sandbox project, so SSH
keys can be injected when a new VM is created from e.g. a CI pipeline,
so that the CI pipeline can SSH to the newly created VM.

The EC2 metadata service is used over the Hetzner metadata service, as
it is supported by more providers (including Hetzner).
---
 README.md                                      |  2 +-
 packer/archlinux.pkr.hcl                       | 12 +++++++++++-
 roles/install_arch/files/ec2-public-keys       | 18 ++++++++++++++++++
 .../install_arch/files/ec2-public-keys.service | 13 +++++++++++++
 roles/install_arch/tasks/main.yml              | 10 +++++++++-
 5 files changed, 52 insertions(+), 3 deletions(-)
 create mode 100755 roles/install_arch/files/ec2-public-keys
 create mode 100644 roles/install_arch/files/ec2-public-keys.service

diff --git a/README.md b/README.md
index 7ab03624a..976fda851 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ This will take some time after which a new snapshot will have been created on th
 
 For the sandbox project please run
 
-    packer build -var $(misc/get_key.py misc/vaults/vault_hetzner.yml hetzner_cloud_sandbox_infrastructure_api_key --format env | sed 's/_sandbox_infrastructure//') packer/archlinux.pkr.hcl
+    packer build -var $(misc/get_key.py misc/vaults/vault_hetzner.yml hetzner_cloud_sandbox_infrastructure_api_key --format env | sed 's/_sandbox_infrastructure//') -var install_ec2_public_keys_service=true packer/archlinux.pkr.hcl
 
 #### Note about terraform
 
diff --git a/packer/archlinux.pkr.hcl b/packer/archlinux.pkr.hcl
index 4acbf0f6c..0986b568e 100644
--- a/packer/archlinux.pkr.hcl
+++ b/packer/archlinux.pkr.hcl
@@ -18,6 +18,11 @@ variable "hetzner_cloud_api_key" {
   sensitive = true
 }
 
+variable "install_ec2_public_keys_service" {
+  type    = bool
+  default = false
+}
+
 # https://www.packer.io/docs/templates/hcl_templates/blocks/source
 source "hcloud" "rescue" {
   image       = "ubuntu-22.04"
@@ -40,6 +45,11 @@ build {
     host_alias          = "packer-base-image"
     inventory_directory = "."
     playbook_file       = "playbooks/tasks/install_arch.yml"
-    use_proxy           = false
+    extra_arguments = [
+      "--extra-vars", jsonencode({
+        install_ec2_public_keys_service : var.install_ec2_public_keys_service
+      })
+    ]
+    use_proxy = false
   }
 }
diff --git a/roles/install_arch/files/ec2-public-keys b/roles/install_arch/files/ec2-public-keys
new file mode 100755
index 000000000..d7d538c82
--- /dev/null
+++ b/roles/install_arch/files/ec2-public-keys
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import os
+from pathlib import Path
+
+import requests
+
+data = requests.get("http://169.254.169.254/2009-04-04/meta-data/public-keys")
+data.raise_for_status()
+
+path = Path("/root/.ssh/authorized_keys")
+path.parent.mkdir(mode=0o700, exist_ok=True)
+os.chmod(path.parent, 0o700)
+
+with open(path, "w") as file:
+    for key in data.json():
+        file.write(f"{key}\n")
+os.chmod(path, 0o600)
diff --git a/roles/install_arch/files/ec2-public-keys.service b/roles/install_arch/files/ec2-public-keys.service
new file mode 100644
index 000000000..99d092bf7
--- /dev/null
+++ b/roles/install_arch/files/ec2-public-keys.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Fetch SSH public keys from the metadata service
+Before=sshd.service
+After=systemd-networkd-wait-online.service
+ConditionFirstBoot=yes
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/local/bin/ec2-public-keys
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/install_arch/tasks/main.yml b/roles/install_arch/tasks/main.yml
index c9b8696fa..2bd526a34 100644
--- a/roles/install_arch/tasks/main.yml
+++ b/roles/install_arch/tasks/main.yml
@@ -171,6 +171,13 @@
 - name: Setup pacman-init.service on first boot
   copy: src=pacman-init.service dest=/mnt/etc/systemd/system/ owner=root group=root mode=0644
 
+- name: Setup ec2-public-keys on first boot
+  copy: src={{ item.src }} dest=/mnt/{{ item.dest }} owner=root group=root mode={{ item.mode }}
+  loop:
+    - {src: ec2-public-keys, dest: /usr/local/bin/, mode: 755}
+    - {src: ec2-public-keys.service, dest: /etc/systemd/system/, mode: 644}
+  when: install_ec2_public_keys_service | default(false)
+
 - name: Remove generated keyring in the installation process
   file: path=/mnt/etc/pacman.d/gnupg state=absent
 
@@ -178,7 +185,7 @@
   file: path=/mnt/etc/machine-id state=absent
 
 - name: Enable services inside chroot
-  command: chroot /mnt systemctl enable sshd systemd-networkd systemd-resolved fstrim.timer pacman-init
+  command: chroot /mnt systemctl enable sshd systemd-networkd systemd-resolved fstrim.timer pacman-init {{ 'ec2-public-keys' if install_ec2_public_keys_service | default(false) }}
   register: chroot_systemd_services
   changed_when: "chroot_systemd_services.rc == 0"
 
@@ -187,6 +194,7 @@
     name: root_ssh
   vars:
     root_ssh_directory: /tmp/root.x86_64/mnt/root/.ssh
+  when: not install_ec2_public_keys_service | default(false)
 
 - name: Configure sshd
   template: src=sshd_config.j2 dest=/mnt/etc/ssh/sshd_config owner=root group=root mode=0644
-- 
GitLab