feat(provision): Phase-0 throwaway test VM via vendored @olsitec/pulumi-hetzner

- Vendor hetzner module (Stage-1, trimmed to @pulumi/hcloud+js-yaml; dropped unused
  types.ts + bcrypt/axios/tls/vault deps). GOTCHA documented: cloud-init moves SSH
  to port 222.
- provision/: isolated stack (platformName foundation-test, no collision with
  olsicloud4-*) — one cx23 in nbg1-dc3 + firewall (222/80/443/2222) + Docker cloud-init.
  Dedicated throwaway ed25519 key (operator id_rsa already registered → uniqueness_error).
- Provisioned + verified: foundation-test @ 91.98.117.152, Docker 29.6.1, docker-over-SSH OK.

Token via ENV (pass), never committed; provision/state gitignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Niemann 2026-06-30 18:57:54 +02:00
parent 6a29db386f
commit 80a99c6f7e
13 changed files with 754 additions and 1 deletions

View file

@ -0,0 +1,126 @@
import * as yaml from "js-yaml";
export interface GetCloudInitConfigArgs {
sshAuthorizedKeys?: string[];
extraPackages?: string[];
additionalFiles?: {
path: string;
permissions: string;
content: string;
}[];
lateCommands?: string[];
}
export const getCloudInitConfig = (config: GetCloudInitConfigArgs) => {
const defaultCloudInitConfig = {
users: [
{
name: "root",
sudo: "ALL=(ALL) NOPASSWD:ALL",
"lock-passwd": false,
passwd:
"$6$hB5RrvU5$ll99.7zgATrkGEbInSRCF7o8t3TatZEYQK4QWqk6Ri.DT3LgG0l38Dz47CT.nBjLhhIUVgzIF6t2ZrWmDOcVl1",
ssh_authorized_keys: config.sshAuthorizedKeys || [],
},
{
name: "andiolsi",
sudo: "ALL=(ALL) NOPASSWD:ALL",
"lock-passwd": false,
passwd:
"$6$sKcNr2f8$AubrAJ..bZOPxnGVEWKQQrWShW/i7ClAZRvGLstrmeDcIS92/.BVCtYzgvg2tg6ub9rz8agfLRJg.ryDoM7jG/",
ssh_authorized_keys: config.sshAuthorizedKeys || [],
},
],
disable_root: false,
ssh_pwauth: false,
locale: "en_US.UTF-8",
mounts: [["tmpfs", "/var/log", "tmpfs", "defaults,size=10%", "0", "0"]],
package_update: true,
package_upgrade: true,
packages: [
"curl",
"apt-transport-https",
"ca-certificates",
"vim",
"jq",
"locales",
...(config.extraPackages || []),
],
write_files: [
{
path: "/etc/ssh/sshd_config.d/99-cloud-init-manual.conf",
permissions: "0600",
content: `
Port 222
PermitRootLogin without-password
PubkeyAuthentication yes
PubkeyAcceptedKeyTypes=+ssh-rsa
`,
},
{
path: "/etc/logrotate.d/var_log",
permissions: "0644",
content: `
/var/log/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 0640 root adm
sharedscripts
postrotate
systemctl reload rsyslog > /dev/null 2>/dev/null || true
endscript
}`,
},
{
path: "/etc/selinux/config",
permissions: "0644",
content: "SELINUX=disabled\nSELINUXTYPE=targeted\n",
},
{
path: "/etc/security/limits.conf",
permissions: "0644",
content: "* soft nofile 32768\n* hard nofile 65536\n",
},
{
path: "/etc/sysctl.d/99-custom.conf",
permissions: "0644",
content: `
net.ipv4.ip_forward=1
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.ip_nonlocal_bind=1
vm.dirty_ratio=10
vm.swappiness=0
vm.dirty_background_ratio=5
net.ipv6.conf.all.disable_ipv6=1
fs.nr_open=3000000
fs.file-max=3000000
fs.inotify.max_user_instances=1048576
fs.inotify.max_queued_events=1048576
fs.inotify.max_user_watches=1048576
vm.max_map_count=262144
net.netfilter.nf_conntrack_max=262144
net.netfilter.nf_conntrack_tcp_timeout_established=600
net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
net.netfilter.nf_conntrack_tcp_timeout_close_wait=30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
`,
},
...(config.additionalFiles || []),
],
runcmd: [
"echo never > /sys/kernel/mm/transparent_hugepage/enabled",
"echo never > /sys/kernel/mm/transparent_hugepage/defrag",
"systemctl stop apparmor || true",
"systemctl disable apparmor || true",
"sysctl --system",
...(config.lateCommands || []),
],
};
return `#cloud-config\n` + yaml.dump(defaultCloudInitConfig);
};