@ebowwa/hetzner 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/actions.js +802 -0
  2. package/actions.ts +1053 -0
  3. package/auth.js +35 -0
  4. package/auth.ts +37 -0
  5. package/bootstrap/FIREWALL.md +326 -0
  6. package/bootstrap/KERNEL-HARDENING.md +258 -0
  7. package/bootstrap/SECURITY-INTEGRATION.md +281 -0
  8. package/bootstrap/TESTING.md +301 -0
  9. package/bootstrap/cloud-init.js +279 -0
  10. package/bootstrap/cloud-init.ts +394 -0
  11. package/bootstrap/firewall.js +279 -0
  12. package/bootstrap/firewall.ts +342 -0
  13. package/bootstrap/genesis.js +406 -0
  14. package/bootstrap/genesis.ts +518 -0
  15. package/bootstrap/index.js +35 -0
  16. package/bootstrap/index.ts +71 -0
  17. package/bootstrap/kernel-hardening.js +266 -0
  18. package/bootstrap/kernel-hardening.test.ts +230 -0
  19. package/bootstrap/kernel-hardening.ts +272 -0
  20. package/bootstrap/security-audit.js +118 -0
  21. package/bootstrap/security-audit.ts +124 -0
  22. package/bootstrap/ssh-hardening.js +182 -0
  23. package/bootstrap/ssh-hardening.ts +192 -0
  24. package/client.js +137 -0
  25. package/client.ts +177 -0
  26. package/config.js +5 -0
  27. package/config.ts +5 -0
  28. package/errors.js +270 -0
  29. package/errors.ts +371 -0
  30. package/index.js +28 -0
  31. package/index.ts +55 -0
  32. package/package.json +56 -0
  33. package/pricing.js +284 -0
  34. package/pricing.ts +422 -0
  35. package/schemas.js +660 -0
  36. package/schemas.ts +765 -0
  37. package/server-status.ts +81 -0
  38. package/servers.js +424 -0
  39. package/servers.ts +568 -0
  40. package/ssh-keys.js +90 -0
  41. package/ssh-keys.ts +122 -0
  42. package/ssh-setup.ts +218 -0
  43. package/types.js +96 -0
  44. package/types.ts +389 -0
  45. package/volumes.js +172 -0
  46. package/volumes.ts +229 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Genesis Server Bootstrap Generator
3
+ *
4
+ * Generates cloud-init YAML scripts for Genesis server provisioning.
5
+ * Genesis is a bootstrap/control plane node that runs com.hetzner.codespaces
6
+ * and manages Hetzner VPS worker nodes.
7
+ *
8
+ * Security Integration:
9
+ * This module integrates all security modules in the correct order:
10
+ * 1. UFW Firewall (network-level defense)
11
+ * 2. Kernel Hardening (system-level hardening)
12
+ * 3. SSH Hardening (service-level hardening)
13
+ * 4. Security Audit (verification and reporting)
14
+ */
15
+ import { sshdHardeningPackages, sshdHardeningWriteFiles, sshdHardeningRunCmd, } from "./ssh-hardening";
16
+ import { kernelHardeningPackages, kernelHardeningWriteFiles, kernelHardeningRunCmd, } from "./kernel-hardening";
17
+ import { ufwFirewallPackages, ufwFirewallWriteFiles, ufwFirewallRunCmd, DEFAULT_UFW_GENESIS_OPTIONS, } from "./firewall";
18
+ import { securityAuditPackages, securityAuditWriteFiles, securityAuditRunCmd, } from "./security-audit";
19
+ /**
20
+ * Generate a cloud-init YAML script for Genesis server bootstrap
21
+ *
22
+ * @param options - Genesis bootstrap configuration options
23
+ * @returns Cloud-init YAML string
24
+ */
25
+ export function generateGenesisBootstrap(options) {
26
+ const { adminSSHKey, genesisRepo = "https://github.com/ebowwa/com.hetzner.codespaces", genesisBranch = "main", hostname = "genesis", defaultServerType = "cpx11", defaultLocation = "fsn1", maxWorkers = "10", packages = [], additionalCommands = [], enableSecurity = true, } = options;
27
+ if (!adminSSHKey) {
28
+ throw new Error("adminSSHKey is required for Genesis bootstrap");
29
+ }
30
+ const lines = [];
31
+ // Cloud-config header
32
+ lines.push("#cloud-config");
33
+ lines.push("# Genesis Server Bootstrap Configuration");
34
+ lines.push("# Version: 1.0.0");
35
+ lines.push("");
36
+ lines.push("# This cloud-init config bootstraps a Genesis server that:");
37
+ lines.push("# - Runs com.hetzner.codespaces web application");
38
+ lines.push("# - Uses the existing Hetzner API to create any server");
39
+ lines.push("# - Can be ephemeral and recreated at any time");
40
+ lines.push("");
41
+ lines.push("# IMPORTANT: Never store secrets in cloud-init! Use Vault/SOPS/external sources.");
42
+ lines.push("");
43
+ // STAGE 1: Network & Early Setup
44
+ lines.push("# =====================================================");
45
+ lines.push("# STAGE 1: Network & Early Setup (Network stage)");
46
+ lines.push("# =====================================================");
47
+ lines.push("");
48
+ lines.push(`hostname: ${hostname}`);
49
+ lines.push("manage_etc_hosts: true");
50
+ lines.push("timezone: UTC");
51
+ lines.push("");
52
+ // STAGE 2: SSH & Security
53
+ lines.push("# =====================================================");
54
+ lines.push("# STAGE 2: SSH & Security (Network stage)");
55
+ lines.push("# =====================================================");
56
+ lines.push("");
57
+ lines.push("ssh_pwauth: false");
58
+ lines.push("");
59
+ lines.push("# Create genesis service user");
60
+ lines.push("users:");
61
+ lines.push(" - name: genesis");
62
+ lines.push(" gecos: Genesis Service Account");
63
+ lines.push(" primary_group: genesis");
64
+ lines.push(" groups: docker,wheel");
65
+ lines.push(" sudo: ALL=(ALL) NOPASSWD:ALL");
66
+ lines.push(" shell: /bin/bash");
67
+ lines.push(" lock_passwd: true");
68
+ lines.push(" ssh_authorized_keys:");
69
+ lines.push(` - ${adminSSHKey}`);
70
+ lines.push("");
71
+ // STAGE 3: Package Management
72
+ lines.push("# =====================================================");
73
+ lines.push("# STAGE 3: Package Management (Config stage)");
74
+ lines.push("# =====================================================");
75
+ lines.push("");
76
+ lines.push("package_update: true");
77
+ lines.push("package_upgrade: false");
78
+ lines.push("package_reboot_if_required: true");
79
+ lines.push("");
80
+ lines.push("packages:");
81
+ lines.push(" - curl");
82
+ lines.push(" - wget");
83
+ lines.push(" - git");
84
+ lines.push(" - unzip");
85
+ lines.push(" - jq");
86
+ lines.push(" - build-essential");
87
+ // Security Module 1: UFW Firewall packages
88
+ if (enableSecurity) {
89
+ lines.push(" # Security: UFW Firewall");
90
+ lines.push(...ufwFirewallPackages());
91
+ }
92
+ // Security Module 2: Kernel hardening packages
93
+ if (enableSecurity) {
94
+ lines.push(" # Security: Kernel hardening");
95
+ lines.push(...kernelHardeningPackages());
96
+ }
97
+ // Security Module 3: SSH hardening packages (fail2ban)
98
+ if (enableSecurity) {
99
+ lines.push(" # Security: SSH hardening");
100
+ lines.push(...sshdHardeningPackages());
101
+ }
102
+ // Security Module 4: Security audit packages (lynis)
103
+ if (enableSecurity) {
104
+ lines.push(" # Security: Security audit");
105
+ lines.push(...securityAuditPackages());
106
+ }
107
+ // Add additional packages
108
+ for (const pkg of packages) {
109
+ lines.push(` - ${pkg}`);
110
+ }
111
+ lines.push("");
112
+ // STAGE 4: Application Setup
113
+ lines.push("# =====================================================");
114
+ lines.push("# STAGE 4: Application Setup (Config stage)");
115
+ lines.push("# =====================================================");
116
+ lines.push("");
117
+ lines.push("write_files:");
118
+ // Genesis directories
119
+ lines.push(" # Genesis application directories");
120
+ lines.push(" - path: /opt/genesis");
121
+ lines.push(" owner: genesis:genesis");
122
+ lines.push(" permissions: '0755'");
123
+ lines.push("");
124
+ lines.push(" - path: /opt/genesis/data");
125
+ lines.push(" owner: genesis:genesis");
126
+ lines.push(" permissions: '0755'");
127
+ lines.push("");
128
+ lines.push(" - path: /var/log/genesis");
129
+ lines.push(" owner: genesis:genesis");
130
+ lines.push(" permissions: '0755'");
131
+ lines.push("");
132
+ // Environment file template
133
+ lines.push(" # Environment file template (do NOT include actual secrets)");
134
+ lines.push(" - path: /etc/default/genesis.template");
135
+ lines.push(" owner: genesis:genesis");
136
+ lines.push(" permissions: '0640'");
137
+ lines.push(" content: |");
138
+ lines.push(" # Genesis Server Environment Configuration");
139
+ lines.push(" # Copy this to /etc/default/genesis and fill in required values");
140
+ lines.push(" #");
141
+ lines.push(" # DO NOT commit actual secrets to version control!");
142
+ lines.push("");
143
+ lines.push(" # Application Settings");
144
+ lines.push(" NODE_ENV=production");
145
+ lines.push(` PORT=3000`);
146
+ lines.push(` HOST=0.0.0.0`);
147
+ lines.push("");
148
+ lines.push(" # Hetzner API (REQUIRED - use Vault or Secrets Manager in production)");
149
+ lines.push(" # HETZNER_API_TOKEN should be set securely after bootstrap");
150
+ lines.push(" HETZNER_DEFAULT_TYPE=" + defaultServerType);
151
+ lines.push(" HETZNER_DEFAULT_LOCATION=" + defaultLocation);
152
+ lines.push(" MAX_WORKER_NODES=" + maxWorkers);
153
+ lines.push("");
154
+ // Systemd service unit
155
+ lines.push(" # Genesis systemd service unit");
156
+ lines.push(" - path: /etc/systemd/system/genesis.service");
157
+ lines.push(" owner: root:root");
158
+ lines.push(" permissions: '0644'");
159
+ lines.push(" content: |");
160
+ lines.push(" [Unit]");
161
+ lines.push(" Description=Genesis Application Server (com.hetzner.codespaces)");
162
+ lines.push(" Documentation=https://github.com/ebowwa/com.hetzner.codespaces");
163
+ lines.push(" After=network-online.target");
164
+ lines.push(" Wants=network-online.target");
165
+ lines.push("");
166
+ lines.push(" [Service]");
167
+ lines.push(" Type=simple");
168
+ lines.push(" User=genesis");
169
+ lines.push(" Group=genesis");
170
+ lines.push(" WorkingDirectory=/opt/genesis");
171
+ lines.push("");
172
+ lines.push(" # Execution");
173
+ lines.push(" ExecStart=/usr/bin/bun start");
174
+ lines.push(" ExecReload=/bin/kill -HUP $MAINPID");
175
+ lines.push("");
176
+ lines.push(" # Restart Policy (with rate limiting)");
177
+ lines.push(" Restart=on-failure");
178
+ lines.push(" RestartSec=5s");
179
+ lines.push(" StartLimitIntervalSec=300");
180
+ lines.push(" StartLimitBurst=5");
181
+ lines.push("");
182
+ lines.push(" # Logging");
183
+ lines.push(" StandardOutput=journal");
184
+ lines.push(" StandardError=journal");
185
+ lines.push(" SyslogIdentifier=genesis");
186
+ lines.push("");
187
+ lines.push(" # Environment");
188
+ lines.push(' Environment="NODE_ENV=production"');
189
+ lines.push(" EnvironmentFile=/etc/default/genesis");
190
+ lines.push(" EnvironmentFile=-/etc/default/genesis.local");
191
+ lines.push("");
192
+ lines.push(" # Resource Limits");
193
+ lines.push(" LimitNOFILE=65536");
194
+ lines.push("");
195
+ // Security hardening for genesis service
196
+ if (enableSecurity) {
197
+ lines.push(" # Security Hardening");
198
+ lines.push(" NoNewPrivileges=true");
199
+ lines.push(" PrivateTmp=true");
200
+ lines.push(" ProtectSystem=strict");
201
+ lines.push(" ProtectHome=true");
202
+ lines.push(" ReadWritePaths=/opt/genesis/data /var/log/genesis");
203
+ }
204
+ else {
205
+ lines.push(" # Security Hardening (minimal)");
206
+ lines.push(" NoNewPrivileges=false");
207
+ lines.push(" PrivateTmp=false");
208
+ }
209
+ lines.push("");
210
+ lines.push(" [Install]");
211
+ lines.push(" WantedBy=multi-user.target");
212
+ lines.push("");
213
+ // Bootstrap status tracking
214
+ lines.push(" # Bootstrap status tracking");
215
+ lines.push(" - path: /root/.genesis-bootstrap-status");
216
+ lines.push(" owner: root:root");
217
+ lines.push(" permissions: '0644'");
218
+ lines.push(" content: |");
219
+ lines.push(" status=started");
220
+ lines.push(" started_at=$(date -Iseconds)");
221
+ lines.push(" source=cloud-init");
222
+ lines.push(" version=1.0.0");
223
+ if (enableSecurity) {
224
+ lines.push(" security=enabled");
225
+ }
226
+ lines.push("");
227
+ // Add bun to /etc/environment
228
+ lines.push(" # Add bun to /etc/environment for all users/shells");
229
+ lines.push(" # Format: Simple KEY=\"value\" pairs, no variable expansion");
230
+ lines.push(" - path: /etc/environment");
231
+ lines.push(" owner: root:root");
232
+ lines.push(" permissions: '0644'");
233
+ lines.push(" content: |");
234
+ lines.push(' PATH="/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"');
235
+ lines.push("");
236
+ // Security Module 1: UFW Firewall configuration files
237
+ if (enableSecurity) {
238
+ lines.push(" # Security Module 1: UFW Firewall configuration");
239
+ lines.push(...ufwFirewallWriteFiles(DEFAULT_UFW_GENESIS_OPTIONS));
240
+ }
241
+ // Security Module 2: Kernel hardening configuration files
242
+ if (enableSecurity) {
243
+ lines.push(" # Security Module 2: Kernel hardening");
244
+ lines.push(...kernelHardeningWriteFiles());
245
+ }
246
+ // Security Module 3: SSH hardening configuration files
247
+ if (enableSecurity) {
248
+ lines.push(" # Security Module 3: SSH hardening");
249
+ lines.push(...sshdHardeningWriteFiles());
250
+ }
251
+ // Security Module 4: Security audit script
252
+ if (enableSecurity) {
253
+ lines.push(" # Security Module 4: Security audit");
254
+ lines.push(...securityAuditWriteFiles());
255
+ }
256
+ // STAGE 5: Run Commands
257
+ lines.push("# =====================================================");
258
+ lines.push("# STAGE 5: Run Commands (Config stage)");
259
+ lines.push("# =====================================================");
260
+ lines.push("");
261
+ lines.push("runcmd:");
262
+ // Install Bun
263
+ lines.push(" # Install Bun runtime");
264
+ lines.push(" - curl -fsSL https://bun.sh/install | bash");
265
+ lines.push("");
266
+ // Clone genesis application
267
+ lines.push(" # Clone/pull genesis application");
268
+ const cloneCmd = genesisBranch
269
+ ? `git clone --depth 1 --branch ${genesisBranch} ${genesisRepo} /opt/genesis`
270
+ : `git clone --depth 1 ${genesisRepo} /opt/genesis`;
271
+ lines.push(` - |`);
272
+ lines.push(` if [ ! -d /opt/genesis/.git ]; then`);
273
+ lines.push(` ${cloneCmd}`);
274
+ lines.push(` else`);
275
+ lines.push(` cd /opt/genesis && git pull`);
276
+ lines.push(` fi`);
277
+ lines.push("");
278
+ // Install dependencies
279
+ lines.push(" # Install dependencies");
280
+ lines.push(" - cd /opt/genesis && bun install");
281
+ lines.push("");
282
+ // Build application
283
+ lines.push(" # Build application (if needed)");
284
+ lines.push(" - cd /opt/genesis && bun run build");
285
+ lines.push("");
286
+ // Configure environment warning
287
+ lines.push(" # Configure environment (prompt for secrets or use external source)");
288
+ lines.push(" - |");
289
+ lines.push(` echo "WARNING: HETZNER_API_TOKEN must be configured in /etc/default/genesis"`);
290
+ lines.push("");
291
+ // Enable and start service
292
+ lines.push(" # Enable and start genesis service");
293
+ lines.push(" - systemctl daemon-reload");
294
+ lines.push(" - systemctl enable genesis.service");
295
+ lines.push(" - systemctl start genesis.service");
296
+ lines.push("");
297
+ // Security Module 1: UFW Firewall activation (runs first)
298
+ if (enableSecurity) {
299
+ lines.push(" # Security Module 1: Activate UFW Firewall");
300
+ lines.push(...ufwFirewallRunCmd(DEFAULT_UFW_GENESIS_OPTIONS));
301
+ }
302
+ // Security Module 2: Kernel hardening activation
303
+ if (enableSecurity) {
304
+ lines.push(" # Security Module 2: Apply kernel hardening");
305
+ lines.push(...kernelHardeningRunCmd());
306
+ }
307
+ // Security Module 3: SSH hardening activation
308
+ if (enableSecurity) {
309
+ lines.push(" # Security Module 3: Activate SSH hardening");
310
+ lines.push(...sshdHardeningRunCmd());
311
+ }
312
+ // Security Module 4: Security audit (runs last)
313
+ if (enableSecurity) {
314
+ lines.push(" # Security Module 4: Run security audit");
315
+ lines.push(...securityAuditRunCmd());
316
+ }
317
+ // Mark bootstrap complete
318
+ lines.push(" # Mark bootstrap complete");
319
+ lines.push(' - echo "status=complete" >> /root/.genesis-bootstrap-status');
320
+ lines.push(' - echo "completed_at=$(date -Iseconds)" >> /root/.genesis-bootstrap-status');
321
+ if (enableSecurity) {
322
+ lines.push(' - echo "security_hardening=applied" >> /root/.genesis-bootstrap-status');
323
+ }
324
+ lines.push("");
325
+ // Additional commands
326
+ if (additionalCommands.length > 0) {
327
+ lines.push(" # Additional custom commands");
328
+ for (const cmd of additionalCommands) {
329
+ lines.push(` - ${cmd}`);
330
+ }
331
+ lines.push("");
332
+ }
333
+ // STAGE 6: Final
334
+ lines.push("# =====================================================");
335
+ lines.push("# STAGE 6: Final (Final stage)");
336
+ lines.push("# =====================================================");
337
+ lines.push("");
338
+ lines.push('final_message: "Genesis server bootstrap completed after $UPTIME seconds"');
339
+ return lines.join("\n");
340
+ }
341
+ /**
342
+ * Generate a minimal cloud-init script that uses #include to fetch from a URL
343
+ *
344
+ * This is useful for larger bootstrap scripts or when you want to update
345
+ * the bootstrap without code changes.
346
+ *
347
+ * @param url - URL to fetch the cloud-init config from
348
+ * @returns Cloud-init YAML string with #include directive
349
+ */
350
+ export function generateRemoteGenesisBootstrap(url) {
351
+ return `#include\n${url}`;
352
+ }
353
+ /**
354
+ * Genesis bootstrap configuration presets for common scenarios
355
+ */
356
+ export const GenesisBootstrapPresets = {
357
+ /**
358
+ * Default Genesis server with standard configuration and full security
359
+ */
360
+ default: (adminSSHKey) => generateGenesisBootstrap({
361
+ adminSSHKey,
362
+ }),
363
+ /**
364
+ * Genesis server with ARM architecture (CAX series - best €/performance)
365
+ */
366
+ arm: (adminSSHKey) => generateGenesisBootstrap({
367
+ adminSSHKey,
368
+ defaultServerType: "cax21",
369
+ }),
370
+ /**
371
+ * Genesis server with high-performance CPU (CPX series)
372
+ */
373
+ performance: (adminSSHKey) => generateGenesisBootstrap({
374
+ adminSSHKey,
375
+ defaultServerType: "cpx21",
376
+ }),
377
+ /**
378
+ * Genesis server with dedicated CPU (CCX series)
379
+ */
380
+ dedicated: (adminSSHKey) => generateGenesisBootstrap({
381
+ adminSSHKey,
382
+ defaultServerType: "ccx13",
383
+ }),
384
+ /**
385
+ * Development Genesis server without security hardening
386
+ */
387
+ development: (adminSSHKey) => generateGenesisBootstrap({
388
+ adminSSHKey,
389
+ enableSecurity: false,
390
+ packages: ["htop", "vim", "tmux", "strace"],
391
+ additionalCommands: [
392
+ "echo 'Genesis development server ready' | wall",
393
+ ],
394
+ }),
395
+ /**
396
+ * Secure Genesis server with full hardening and verbose logging
397
+ */
398
+ secure: (adminSSHKey) => generateGenesisBootstrap({
399
+ adminSSHKey,
400
+ packages: ["lynis"],
401
+ additionalCommands: [
402
+ "echo 'Genesis secure server ready - security audit completed' | wall",
403
+ ],
404
+ }),
405
+ };
406
+ //# sourceMappingURL=genesis.js.map