summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2019-04-13 23:31:30 +0200
committerChristian Pointner <equinox@spreadspace.org>2019-04-14 00:07:27 +0200
commit3c55ccb22322f107c8b82a824e5e28a576af9ae0 (patch)
treeff5d57b3ff3684819ddcce4b0abb6d5d47024f1b
parentadd support for raspbian in prepare-dkms role (diff)
added cloud install role/playbook
-rw-r--r--chaos-at-home/cloud-install.yml6
-rw-r--r--chaos-at-home/group_vars/all.yml25
-rw-r--r--chaos-at-home/host_vars/mimas2.yml10
-rw-r--r--chaos-at-home/mimas2.yml8
l---------cloud-install.sh1
-rw-r--r--dan/cloud-install.yml6
-rw-r--r--dan/host_vars/sk-cloudia.yml10
-rw-r--r--dan/sk-cloudia.yml7
-rw-r--r--inventory/group_vars/chaos-at-home/main.yml4
-rw-r--r--inventory/group_vars/hcloud/main.yml4
-rw-r--r--inventory/group_vars/hroot/main.yml4
-rw-r--r--inventory/hosts.ini19
-rw-r--r--roles/cloud-install/defaults/main.yml2
-rw-r--r--roles/cloud-install/filter_plugins/hroot.py33
-rw-r--r--roles/cloud-install/tasks/hetzner_installimage.yml38
-rw-r--r--roles/cloud-install/tasks/install_hcloud.yml83
-rw-r--r--roles/cloud-install/tasks/install_hroot.yml124
-rw-r--r--roles/cloud-install/tasks/main.yml16
-rw-r--r--roles/cloud-install/tasks/post_hcloud.yml6
-rw-r--r--roles/cloud-install/templates/hetzner_installimage.conf.j221
-rw-r--r--roles/cloud-install/templates/hetzner_postinst.sh.j225
21 files changed, 437 insertions, 15 deletions
diff --git a/chaos-at-home/cloud-install.yml b/chaos-at-home/cloud-install.yml
new file mode 100644
index 00000000..24d8d9c2
--- /dev/null
+++ b/chaos-at-home/cloud-install.yml
@@ -0,0 +1,6 @@
+---
+- name: basic installation
+ hosts: "{{ hostname }}"
+ gather_facts: no
+ roles:
+ - role: cloud-install
diff --git a/chaos-at-home/group_vars/all.yml b/chaos-at-home/group_vars/all.yml
index 45690ced..e6e942c8 100644
--- a/chaos-at-home/group_vars/all.yml
+++ b/chaos-at-home/group_vars/all.yml
@@ -1,10 +1,17 @@
$ANSIBLE_VAULT;1.2;AES256;chaos-at-home
-35386561313935656364616336626666346333343132326536313932373038336236653565653563
-3330303666363363636432346131376239376364666565340a336162666433336261363530386234
-34616535343962346530623533353764663336326132663633653065326436643736313537326466
-6261613462626334390a346638303832326534393231376262613033633136336238356439666131
-63663166623838346565393663363839373730326564333335396238613232313466313365346565
-66366262373438326138366466653035386637343763313637643536643265363736393435383132
-35376661393961383962353165626637313565323830336138663662336362333633363936666236
-62306538633362616466663938323033656634383563333534653936626566643932633032646538
-34623932653531383966326438343361393536373965346464616239303961656432
+62343537316165343061336664393031333732343935313430313338353133376264353632323332
+3232383539366231336137613731613734376666393339330a303565396239363265393262303966
+66386137643837316162653038623737326233356235356563363963643532653561313632663733
+3837316362393461380a386663333837323335666161353839623262633433643365653939373033
+63613532373333333266333134303961313362623862386630656463636634656633393137363837
+37623239343339373632663164393936363932333830346138323761386236623834383035393930
+65373638616161643664393865363239636664613161626364333631636464356362626435313333
+35313634323664653039396338653663323233633366363636376632663363626163613364373935
+35653733326465633831363039353362346630326534386566323430373335323233376236636437
+65333434316339633666376162386238393238336534656539663734656535666338353465376131
+33666438636137303265383532613433363135373561353734376636623938393031623461653630
+39633462663738346533663639303231363437366135643664336466613365303033633731343735
+65656534363431633733373932336664356632376234633163363334316539323261326463663566
+64643533336166633530323137616633663436393063316664356466306233343935353862326663
+39326662303462663533353939336239393362363961636336623632343839643262306430386439
+65623162343331616530
diff --git a/chaos-at-home/host_vars/mimas2.yml b/chaos-at-home/host_vars/mimas2.yml
new file mode 100644
index 00000000..d0a9a782
--- /dev/null
+++ b/chaos-at-home/host_vars/mimas2.yml
@@ -0,0 +1,10 @@
+$ANSIBLE_VAULT;1.2;AES256;chaos-at-home
+62306466306134666137366136316236626665313339613732376234346561383936363637636230
+3433383630616330653831643264623361643562383831650a333739333231326264383661613339
+64356463306263656263356438346132613438326635643936333039353836383538343331346637
+3633343931646331340a333738313261386130343266373735373461396466653236653130666536
+32303236346637303563623935373030376536666661363465623839613336356635623861376638
+35636563646266633863336263316633643662653961363632393866633137353236636234623463
+37313537613938363631333338646565376132626461666666653265343831383565663163343236
+30383165333961643366373334643432356435646432333338616362353139646639313034656433
+3261
diff --git a/chaos-at-home/mimas2.yml b/chaos-at-home/mimas2.yml
new file mode 100644
index 00000000..bfbe8e57
--- /dev/null
+++ b/chaos-at-home/mimas2.yml
@@ -0,0 +1,8 @@
+---
+- name: Basic Setup
+ hosts: mimas2
+ roles:
+ - role: base
+ - role: sshd
+ - role: zsh
+ - role: admin-user
diff --git a/cloud-install.sh b/cloud-install.sh
new file mode 120000
index 00000000..61e71e22
--- /dev/null
+++ b/cloud-install.sh
@@ -0,0 +1 @@
+install.sh \ No newline at end of file
diff --git a/dan/cloud-install.yml b/dan/cloud-install.yml
new file mode 100644
index 00000000..24d8d9c2
--- /dev/null
+++ b/dan/cloud-install.yml
@@ -0,0 +1,6 @@
+---
+- name: basic installation
+ hosts: "{{ hostname }}"
+ gather_facts: no
+ roles:
+ - role: cloud-install
diff --git a/dan/host_vars/sk-cloudia.yml b/dan/host_vars/sk-cloudia.yml
new file mode 100644
index 00000000..53629208
--- /dev/null
+++ b/dan/host_vars/sk-cloudia.yml
@@ -0,0 +1,10 @@
+$ANSIBLE_VAULT;1.2;AES256;dan
+64313638393461613535643731303830343539313333643462633232303936346665636536313630
+6261376532663565343434376633613930613331626530380a633235326261306166356166636363
+32636530656665303633373331353565626534646466666336636561376638323834646262633636
+3633656465366263640a653837613439363438653366643763323933366361323938326439373138
+36323638633530323630323133386332303965353866353831383961333363613933373132353663
+35393938326630356261336136633763316436366435313965306166656138393032306434363861
+62383632636239653233626535316361376637646564333861323936343833383030303139346135
+39303735623038633661626238616638373061643762336339366434303162633731646432626364
+3432
diff --git a/dan/sk-cloudia.yml b/dan/sk-cloudia.yml
new file mode 100644
index 00000000..6d22afed
--- /dev/null
+++ b/dan/sk-cloudia.yml
@@ -0,0 +1,7 @@
+---
+- name: Basic Setup
+ hosts: sk-cloudia
+ roles:
+ - role: base
+ - role: sshd
+ - role: zsh
diff --git a/inventory/group_vars/chaos-at-home/main.yml b/inventory/group_vars/chaos-at-home/main.yml
index 8b9ff936..1d44eb7d 100644
--- a/inventory/group_vars/chaos-at-home/main.yml
+++ b/inventory/group_vars/chaos-at-home/main.yml
@@ -1,7 +1,7 @@
---
zsh_banner: chaos-at-home
-admin_user_host:
+admin_user_group:
- "{{ equinox_user }}"
-ssh_allowusers_host: "{{ admin_user_host | map(attribute='name') | list }}"
+ssh_allowusers_host: "{{ admin_user_group | map(attribute='name') | list }}"
diff --git a/inventory/group_vars/hcloud/main.yml b/inventory/group_vars/hcloud/main.yml
new file mode 100644
index 00000000..83219510
--- /dev/null
+++ b/inventory/group_vars/hcloud/main.yml
@@ -0,0 +1,4 @@
+---
+cloud_provider: hcloud
+
+hcloud_api_token: "{{ vault_hcloud_api_token }}"
diff --git a/inventory/group_vars/hroot/main.yml b/inventory/group_vars/hroot/main.yml
new file mode 100644
index 00000000..828a3720
--- /dev/null
+++ b/inventory/group_vars/hroot/main.yml
@@ -0,0 +1,4 @@
+---
+cloud_provider: hroot
+
+hroot_robot_account: "{{ vault_hroot_robot_account }}"
diff --git a/inventory/hosts.ini b/inventory/hosts.ini
index f0458932..df885051 100644
--- a/inventory/hosts.ini
+++ b/inventory/hosts.ini
@@ -18,6 +18,7 @@ ansible_host={{ host_name }}.{{ host_domain }}
atlas
keyserver
pan ansible_host=ch-pan ansible_port=222
+mimas2 ansible_host=ch-mimas2 ansible_port=222
[chaos-at-home:children]
mz-chaos-at-home
@@ -79,10 +80,12 @@ emc-00
[skillz:vars]
host_domain=skillz.biz
env_group=dan
+ansible_port=222
[skillz]
-sk2013 host_name=2013
-sk2016 host_name=2016
+sk2013 host_name=2013 ansible_port=22000
+sk2016 host_name=2016 ansible_port=22000
+sk-cloudia host_name=cloudia
[ele-ap]
@@ -131,9 +134,16 @@ atlas
sk2013
sk2016
-[hetzner]
+[hroot]
sk2013
sk2016
+sk-cloudia
+
+[hcloud]
+emc-00
+mimas2
+
+[hetzner]
emc-stats
emc-master
k8s-test0
@@ -141,7 +151,8 @@ k8s-test2
[hetzner:children]
emc-xx
-
+hroot
+hcloud
[scaleway-kernel]
diff --git a/roles/cloud-install/defaults/main.yml b/roles/cloud-install/defaults/main.yml
new file mode 100644
index 00000000..33e5d165
--- /dev/null
+++ b/roles/cloud-install/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+hetzner_disk_config: "{% if cloud_provider == 'hroot' %}nvme_raid{% elif cloud_provider == 'hcloud' %}hcloud{% endif %}"
diff --git a/roles/cloud-install/filter_plugins/hroot.py b/roles/cloud-install/filter_plugins/hroot.py
new file mode 100644
index 00000000..d2abff1b
--- /dev/null
+++ b/roles/cloud-install/filter_plugins/hroot.py
@@ -0,0 +1,33 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible import errors
+
+
+def extract_ssh_key_fingerprints(data):
+ try:
+ return [k['key']['fingerprint'] for k in data]
+ except Exception as e:
+ raise errors.AnsibleFilterError("hroot_ssh_key_fingerprints(): %s" % str(e))
+
+
+def extract_serverip(data, server_name):
+ try:
+ ips = [s['server']['server_ip'] for s in data if s['server']['server_name'] == server_name]
+ if len(ips):
+ return ips[0]
+ raise errors.AnsibleFilterError("hroot_extract_serverip(): server %s not found" % server_name)
+ except Exception as e:
+ raise errors.AnsibleFilterError("hroot_extract_serverip(): %s" % str(e))
+
+
+class FilterModule(object):
+
+ ''' Ansible math jinja2 filters '''
+
+ def filters(self):
+ return {
+ 'hroot_extract_ssh_key_fingerprints': extract_ssh_key_fingerprints,
+ 'hroot_extract_serverip': extract_serverip,
+ }
diff --git a/roles/cloud-install/tasks/hetzner_installimage.yml b/roles/cloud-install/tasks/hetzner_installimage.yml
new file mode 100644
index 00000000..f54a785b
--- /dev/null
+++ b/roles/cloud-install/tasks/hetzner_installimage.yml
@@ -0,0 +1,38 @@
+---
+- name: determine latest image name
+ shell: |
+ set -o pipefail
+ shopt -s nocaseglob
+ ls /root/.oldroot/nfs/images/{{ install_distro }}-*-{{ install_codename }}-64-minimal.tar.gz | sort -r | head -n 1
+ args:
+ executable: /bin/bash
+ check_mode: no
+ changed_when: false
+ register: latest_image
+
+- name: generate installimage config
+ template:
+ src: hetzner_installimage.conf.j2
+ dest: /root/installimage.conf
+
+- name: generate postinst script
+ template:
+ src: hetzner_postinst.sh.j2
+ dest: /root/postinst.sh
+ mode: 0755
+
+- name: run installimage
+ command: /root/.oldroot/nfs/install/installimage -a -c installimage.conf -x postinst.sh
+ register: hetzner_installimage_cmd
+ changed_when: true
+ args:
+ chdir: /root
+
+- name: "print installimage output"
+ debug:
+ msg: "{{ hetzner_installimage_cmd.stdout_lines + hetzner_installimage_cmd.stderr_lines }}"
+
+- name: "check if installimage succeeded"
+ fail:
+ msg: "failed to run installimage"
+ when: "hetzner_installimage_cmd.rc != 0 or 'postinst.sh finished successfully' not in hetzner_installimage_cmd.stdout_lines"
diff --git a/roles/cloud-install/tasks/install_hcloud.yml b/roles/cloud-install/tasks/install_hcloud.yml
new file mode 100644
index 00000000..a4c61c0f
--- /dev/null
+++ b/roles/cloud-install/tasks/install_hcloud.yml
@@ -0,0 +1,83 @@
+---
+- name: retrieve ssh key ids
+ uri:
+ url: "https://api.hetzner.cloud/v1/ssh_keys"
+ method: GET
+ headers:
+ Authorization: "Bearer {{ hcloud_api_token }}"
+ status_code: 200
+ register: sshkeys
+ delegate_to: localhost
+
+- name: retrieve server id and check if rescue mode is already active
+ uri:
+ url: "https://api.hetzner.cloud/v1/servers?name={{ inventory_hostname }}"
+ method: GET
+ headers:
+ Authorization: "Bearer {{ hcloud_api_token }}"
+ status_code: 200
+ register: serverstatus
+ delegate_to: localhost
+
+- name: do not continue in check mode
+ fail:
+ msg: "can not bootstrap new servers in check mode"
+ when: ansible_check_mode
+ check_mode: no
+
+### TODO: for now we add all ssh keys that are installed for this project - this might not be a good idea!
+- name: activate rescue mode
+ when: not serverstatus.json.servers[0].rescue_enabled
+ uri:
+ url: "https://api.hetzner.cloud/v1/servers/{{ serverstatus.json.servers[0].id }}/actions/enable_rescue"
+ method: POST
+ body: "{{ {'type': 'linux64', 'ssh_keys': (sshkeys.json.ssh_keys | map(attribute='id') | list) } | to_nice_json }}"
+ headers:
+ Authorization: "Bearer {{ hcloud_api_token }}"
+ Content-Type: "application/json"
+ status_code: 201
+ delegate_to: localhost
+
+- name: do a hardware reset
+ uri:
+ url: "https://api.hetzner.cloud/v1/servers/{{ serverstatus.json.servers[0].id }}/actions/reset"
+ method: POST
+ headers:
+ Authorization: "Bearer {{ hcloud_api_token }}"
+ status_code: 201
+ delegate_to: localhost
+
+### TODO: would be nice to get the SSH host key from robot
+- name: completely ignore ssh host keys for now
+ set_fact:
+ old_ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | default('') }}"
+ ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | default('') }} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+
+- name: wait for rescue system to start up
+ wait_for_connection:
+ delay: 30
+ timeout: 120
+
+- import_tasks: hetzner_installimage.yml
+
+- name: reboot
+ shell: sleep 2 && shutdown -r now "triggered by ansible after running installimage"
+ async: 1
+ poll: 0
+ ignore_errors: True
+ changed_when: True
+
+### TODO: SSH host key handling needs to be improved
+- name: automatically accept new ssh host key
+ set_fact:
+ ansible_ssh_extra_args: "{{ old_ansible_ssh_extra_args }} -o StrictHostKeyChecking=no"
+
+- name: wait for host to start up
+ wait_for_connection:
+ delay: 15
+ timeout: 120
+
+### TODO: SSH host key handling needs to be improved
+- name: re-enable ssh host key checking
+ set_fact:
+ ansible_ssh_extra_args: "{{ old_ansible_ssh_extra_args }}"
diff --git a/roles/cloud-install/tasks/install_hroot.yml b/roles/cloud-install/tasks/install_hroot.yml
new file mode 100644
index 00000000..6d4d6017
--- /dev/null
+++ b/roles/cloud-install/tasks/install_hroot.yml
@@ -0,0 +1,124 @@
+---
+- name: retrieve ssh key fingerprints
+ uri:
+ url: "https://robot-ws.your-server.de/key"
+ method: GET
+ user: "{{ hroot_robot_account.username }}"
+ password: "{{ hroot_robot_account.password }}"
+ force_basic_auth: yes
+ status_code: 200
+ register: sshkeys
+ delegate_to: localhost
+ check_mode: no
+
+- name: do not continue in check mode
+ fail:
+ msg: "can not bootstrap new servers in check mode"
+ when: ansible_check_mode
+ check_mode: no
+
+- block:
+ - name: retrieve server list from robot
+ uri:
+ url: "https://robot-ws.your-server.de/server"
+ method: GET
+ user: "{{ hroot_robot_account.username }}"
+ password: "{{ hroot_robot_account.password }}"
+ force_basic_auth: yes
+ status_code: 200
+ register: servers
+ delegate_to: localhost
+ check_mode: no
+
+ - name: extract server IP address from robot result
+ set_fact:
+ hetzner_main_ip: "{{ servers.json | hroot_extract_serverip(host_name) }}"
+
+ when: hetzner_main_ip is not defined
+
+- name: display warning message
+ pause:
+ prompt: |
+ *** Danger ****
+ will be bootstraping host {{ inventory_hostname }} with main IP {{ hetzner_main_ip }} ...
+ ALL DATA WILL BE LOST!!! press CTRL-C then A to abort.
+ seconds: 15
+
+- name: check if rescue mode is already active
+ uri:
+ url: "https://robot-ws.your-server.de/boot/{{ hetzner_main_ip }}/rescue"
+ method: GET
+ user: "{{ hroot_robot_account.username }}"
+ password: "{{ hroot_robot_account.password }}"
+ force_basic_auth: yes
+ status_code: 200
+ register: rescuestatus
+ delegate_to: localhost
+ check_mode: no
+
+### TODO: for now we add all ssh keys that are installed in the robot - this might not be a good idea!
+- name: activate rescue mode
+ when: not rescuestatus.json.rescue.active
+ uri:
+ url: "https://robot-ws.your-server.de/boot/{{ hetzner_main_ip }}/rescue"
+ method: POST
+ user: "{{ hroot_robot_account.username }}"
+ password: "{{ hroot_robot_account.password }}"
+ force_basic_auth: yes
+ body: "os=linux&arch=64&authorized_key[]={{ sshkeys.json | hroot_extract_ssh_key_fingerprints | join('&authorized_key[]=') }}"
+ status_code: 200
+ headers:
+ Content-Type: "application/x-www-form-urlencoded"
+ delegate_to: localhost
+
+- name: wait for the rescue mode to become active
+ pause:
+ seconds: 5
+
+- name: do a hardware reset
+ uri:
+ url: "https://robot-ws.your-server.de/reset/{{ hetzner_main_ip }}"
+ method: POST
+ user: "{{ hroot_robot_account.username }}"
+ password: "{{ hroot_robot_account.password }}"
+ force_basic_auth: yes
+ body: "type=hw"
+ status_code: 200
+ headers:
+ Content-Type: "application/x-www-form-urlencoded"
+ delegate_to: localhost
+
+### TODO: would be nice to get the SSH host key from robot
+- name: completely ignore ssh host keys for now
+ set_fact:
+ old_ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | default('') }}"
+ ansible_ssh_extra_args: "{{ ansible_ssh_extra_args | default('') }} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+
+- name: wait for rescue system to start up
+ wait_for_connection:
+ delay: 30
+ timeout: 120
+
+- import_tasks: hetzner_installimage.yml
+
+- name: reboot
+ shell: sleep 2 && shutdown -r now "triggered by ansible after running installimage"
+ async: 1
+ poll: 0
+ ignore_errors: True
+ changed_when: True
+
+### TODO: SSH host key handling needs to be improved
+- name: automatically accept new ssh host key
+ set_fact:
+ ansible_ssh_extra_args: "{{ old_ansible_ssh_extra_args }} -o StrictHostKeyChecking=no"
+
+- name: wait for host to start up
+ wait_for_connection:
+ delay: 15
+ timeout: 120
+
+### TODO: SSH host key handling needs to be improved
+- name: re-enable ssh host key checking
+ set_fact:
+ ansible_ssh_extra_args: "{{ old_ansible_ssh_extra_args }}"
diff --git a/roles/cloud-install/tasks/main.yml b/roles/cloud-install/tasks/main.yml
new file mode 100644
index 00000000..ef6d42ae
--- /dev/null
+++ b/roles/cloud-install/tasks/main.yml
@@ -0,0 +1,16 @@
+---
+- include_tasks: "{{ item }}"
+ static: no
+ with_first_found:
+ - files:
+ - "install_{{ cloud_provider }}.yml"
+
+- name: force facts cache to get updated
+ setup:
+
+- include_tasks: "{{ item }}"
+ static: no
+ with_first_found:
+ - files:
+ - "post_{{ cloud_provider }}.yml"
+ skip: True
diff --git a/roles/cloud-install/tasks/post_hcloud.yml b/roles/cloud-install/tasks/post_hcloud.yml
new file mode 100644
index 00000000..96108c58
--- /dev/null
+++ b/roles/cloud-install/tasks/post_hcloud.yml
@@ -0,0 +1,6 @@
+---
+- name: install additional packages
+ apt:
+ name:
+ - qemu-guest-agent
+ state: present
diff --git a/roles/cloud-install/templates/hetzner_installimage.conf.j2 b/roles/cloud-install/templates/hetzner_installimage.conf.j2
new file mode 100644
index 00000000..4c834499
--- /dev/null
+++ b/roles/cloud-install/templates/hetzner_installimage.conf.j2
@@ -0,0 +1,21 @@
+HOSTNAME {{ host_name }}
+{% if hetzner_disk_config == "nvme_raid" %}
+DRIVE1 /dev/nvme0n1
+DRIVE2 /dev/nvme1n1
+SWRAID 1
+SWRAIDLEVEL 1
+{% elif hetzner_disk_config == "sata_raid" %}
+DRIVE1 /dev/sda
+DRIVE2 /dev/sdb
+SWRAID 1
+SWRAIDLEVEL 1
+{% elif hetzner_disk_config == "hcloud" %}
+DRIVE1 /dev/sda
+{% endif %}
+BOOTLOADER grub
+PART /boot ext4 512M
+PART lvm {{ host_name }} all
+LV {{ host_name }} root / ext4 2560M
+LV {{ host_name }} var /var ext4 1280M
+LV {{ host_name }} var+log /var/log ext4 768M
+IMAGE {{ latest_image.stdout }}
diff --git a/roles/cloud-install/templates/hetzner_postinst.sh.j2 b/roles/cloud-install/templates/hetzner_postinst.sh.j2
new file mode 100644
index 00000000..3aa33c76
--- /dev/null
+++ b/roles/cloud-install/templates/hetzner_postinst.sh.j2
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -euf -o pipefail
+
+apt-get update
+apt-get full-upgrade -y
+apt-get install -y --no-install-recommends openssh-server python
+
+passwd -d root && passwd -l root
+{% if install_distro == "debian" %}
+sed -e 's/^allow-hotplug/auto/' -i /etc/network/interfaces
+{% endif %}
+sed -r 's#(\s+/var/log\s+ext4\s+)defaults#\1noatime,nodev,noexec#g' -i /etc/fstab
+
+mkdir -p -m 0700 /target/root/.ssh
+cat <<EOK > /root/.ssh/authorized_keys
+{{ ssh_keys_root | join('\n') }}
+EOK
+{% if hostvars[hostname].ansible_port is defined %}
+sed -e 's/^\(\s*#*\s*Port.*\)/Port {{ hostvars[hostname].ansible_port }}/' -i /etc/ssh/sshd_config
+{% endif %}
+
+{# this is actually only needed on ubuntu bionic and beyond but should not hurt on other installations either #}
+swapoff -a; sed -e '/^\/swapfile/d' -i /etc/fstab; rm -f /swapfile
+
+echo "postinst.sh finished successfully"