@ebowwa/hetzner 0.2.1 → 0.3.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.
- package/dist/bootstrap/index.js +1126 -0
- package/dist/bootstrap/index.js.map +15 -0
- package/dist/index.js +3540 -0
- package/dist/index.js.map +31 -0
- package/dist/onboarding/index.js +460 -0
- package/dist/onboarding/index.js.map +14 -0
- package/package.json +53 -16
- package/actions.js +0 -1084
- package/actions.ts +0 -1053
- package/auth.js +0 -39
- package/auth.ts +0 -37
- package/bootstrap/FIREWALL.md +0 -326
- package/bootstrap/KERNEL-HARDENING.md +0 -258
- package/bootstrap/SECURITY-INTEGRATION.md +0 -281
- package/bootstrap/TESTING.md +0 -301
- package/bootstrap/cloud-init.js +0 -323
- package/bootstrap/cloud-init.ts +0 -394
- package/bootstrap/firewall.js +0 -292
- package/bootstrap/firewall.ts +0 -342
- package/bootstrap/genesis.js +0 -424
- package/bootstrap/genesis.ts +0 -518
- package/bootstrap/index.js +0 -59
- package/bootstrap/index.ts +0 -71
- package/bootstrap/kernel-hardening.js +0 -270
- package/bootstrap/kernel-hardening.test.js +0 -182
- package/bootstrap/kernel-hardening.test.ts +0 -230
- package/bootstrap/kernel-hardening.ts +0 -272
- package/bootstrap/security-audit.js +0 -122
- package/bootstrap/security-audit.ts +0 -124
- package/bootstrap/ssh-hardening.js +0 -186
- package/bootstrap/ssh-hardening.ts +0 -192
- package/client.js +0 -234
- package/client.ts +0 -177
- package/config.js +0 -7
- package/config.ts +0 -5
- package/errors.js +0 -345
- package/errors.ts +0 -371
- package/index.js +0 -73
- package/index.ts +0 -59
- package/onboarding/doppler.ts +0 -116
- package/onboarding/git.ts +0 -133
- package/onboarding/index.ts +0 -18
- package/onboarding/onboarding.ts +0 -193
- package/onboarding/tailscale.ts +0 -159
- package/onboarding/types.ts +0 -115
- package/pricing.js +0 -387
- package/pricing.ts +0 -422
- package/schemas.js +0 -667
- package/schemas.ts +0 -765
- package/server-status.js +0 -122
- package/server-status.ts +0 -81
- package/servers.js +0 -667
- package/servers.ts +0 -568
- package/ssh-keys.js +0 -180
- package/ssh-keys.ts +0 -122
- package/ssh-setup.js +0 -253
- package/ssh-setup.ts +0 -218
- package/types.js +0 -99
- package/types.ts +0 -389
- package/volumes.js +0 -295
- package/volumes.ts +0 -229
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
// src/bootstrap/ssh-hardening.ts
|
|
2
|
+
function sshdHardeningPackages() {
|
|
3
|
+
return [
|
|
4
|
+
" - fail2ban"
|
|
5
|
+
];
|
|
6
|
+
}
|
|
7
|
+
function sshdHardeningWriteFiles() {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push(" # Hardened SSH configuration");
|
|
10
|
+
lines.push(" - path: /etc/ssh/sshd_config.d/hardened.conf");
|
|
11
|
+
lines.push(" owner: root:root");
|
|
12
|
+
lines.push(" permissions: '0644'");
|
|
13
|
+
lines.push(" content: |");
|
|
14
|
+
lines.push(" # Hardened SSH config - applied via cloud-init");
|
|
15
|
+
lines.push(" PasswordAuthentication no");
|
|
16
|
+
lines.push(" PermitEmptyPasswords no");
|
|
17
|
+
lines.push(" KbdInteractiveAuthentication no");
|
|
18
|
+
lines.push(" PermitRootLogin prohibit-password");
|
|
19
|
+
lines.push(" LoginGraceTime 30");
|
|
20
|
+
lines.push(" MaxStartups 20:50:60");
|
|
21
|
+
lines.push(" ClientAliveInterval 30");
|
|
22
|
+
lines.push(" ClientAliveCountMax 3");
|
|
23
|
+
lines.push(" MaxAuthTries 3");
|
|
24
|
+
lines.push("");
|
|
25
|
+
lines.push(" # fail2ban SSH jail configuration");
|
|
26
|
+
lines.push(" - path: /etc/fail2ban/jail.local");
|
|
27
|
+
lines.push(" owner: root:root");
|
|
28
|
+
lines.push(" permissions: '0644'");
|
|
29
|
+
lines.push(" content: |");
|
|
30
|
+
lines.push(" [sshd]");
|
|
31
|
+
lines.push(" enabled = true");
|
|
32
|
+
lines.push(" port = ssh");
|
|
33
|
+
lines.push(" backend = systemd");
|
|
34
|
+
lines.push(" maxretry = 3");
|
|
35
|
+
lines.push(" findtime = 600");
|
|
36
|
+
lines.push(" bantime = 3600");
|
|
37
|
+
lines.push(" banaction = nftables-multiport");
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push(" # sshd health monitoring script");
|
|
40
|
+
lines.push(" - path: /opt/monitoring/sshd-health.sh");
|
|
41
|
+
lines.push(" owner: root:root");
|
|
42
|
+
lines.push(" permissions: '0755'");
|
|
43
|
+
lines.push(" content: |");
|
|
44
|
+
lines.push(" #!/bin/bash");
|
|
45
|
+
lines.push(" set -euo pipefail");
|
|
46
|
+
lines.push(' LOGFILE="/var/log/sshd-health.json"');
|
|
47
|
+
lines.push(' sshd_active=$(systemctl is-active ssh 2>/dev/null || echo "inactive")');
|
|
48
|
+
lines.push(' sshd_pid=$(pgrep -x sshd | head -1 || echo "0")');
|
|
49
|
+
lines.push(' f2b_active=$(systemctl is-active fail2ban 2>/dev/null || echo "inactive")');
|
|
50
|
+
lines.push(` f2b_banned=$(fail2ban-client status sshd 2>/dev/null | grep "Currently banned" | awk '{print $NF}' || echo "0")`);
|
|
51
|
+
lines.push(` f2b_total=$(fail2ban-client status sshd 2>/dev/null | grep "Total banned" | awk '{print $NF}' || echo "0")`);
|
|
52
|
+
lines.push(' failed_1h=$(journalctl -u ssh --since "1 hour ago" --no-pager 2>/dev/null | grep -c "Failed" || true)');
|
|
53
|
+
lines.push(' failed_24h=$(journalctl -u ssh --since "24 hours ago" --no-pager 2>/dev/null | grep -c "Failed" || true)');
|
|
54
|
+
lines.push(' active_connections=$(ss -tnp | grep -c ":22 " || echo "0")');
|
|
55
|
+
lines.push(" uptime_seconds=$(awk '{print int($1)}' /proc/uptime)");
|
|
56
|
+
lines.push(" load=$(awk '{print $1}' /proc/loadavg)");
|
|
57
|
+
lines.push(` mem_used_pct=$(free | awk '/Mem:/{printf "%.1f", $3/$2*100}')`);
|
|
58
|
+
lines.push(" timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)");
|
|
59
|
+
lines.push(' cat > "$LOGFILE" <<HEALTHEOF');
|
|
60
|
+
lines.push(" {");
|
|
61
|
+
lines.push(' "timestamp": "$timestamp",');
|
|
62
|
+
lines.push(' "sshd": { "active": "$sshd_active", "pid": $sshd_pid },');
|
|
63
|
+
lines.push(' "fail2ban": { "active": "$f2b_active", "currently_banned": $f2b_banned, "total_banned": $f2b_total },');
|
|
64
|
+
lines.push(' "connections": { "active": $active_connections, "failed_1h": $failed_1h, "failed_24h": $failed_24h },');
|
|
65
|
+
lines.push(' "system": { "uptime_seconds": $uptime_seconds, "load": $load, "memory_used_pct": $mem_used_pct }');
|
|
66
|
+
lines.push(" }");
|
|
67
|
+
lines.push(" HEALTHEOF");
|
|
68
|
+
lines.push(' if [ "$sshd_active" != "active" ]; then');
|
|
69
|
+
lines.push(' logger -t sshd-health -p auth.crit "ALERT: sshd is $sshd_active"');
|
|
70
|
+
lines.push(' systemctl start ssh 2>/dev/null || logger -t sshd-health -p auth.crit "FAILED to restart sshd"');
|
|
71
|
+
lines.push(" fi");
|
|
72
|
+
lines.push("");
|
|
73
|
+
lines.push(" # sshd health check systemd service");
|
|
74
|
+
lines.push(" - path: /etc/systemd/system/sshd-health.service");
|
|
75
|
+
lines.push(" owner: root:root");
|
|
76
|
+
lines.push(" permissions: '0644'");
|
|
77
|
+
lines.push(" content: |");
|
|
78
|
+
lines.push(" [Unit]");
|
|
79
|
+
lines.push(" Description=sshd health check");
|
|
80
|
+
lines.push(" After=ssh.service");
|
|
81
|
+
lines.push(" [Service]");
|
|
82
|
+
lines.push(" Type=oneshot");
|
|
83
|
+
lines.push(" ExecStart=/opt/monitoring/sshd-health.sh");
|
|
84
|
+
lines.push("");
|
|
85
|
+
lines.push(" # sshd health check timer (every 60s)");
|
|
86
|
+
lines.push(" - path: /etc/systemd/system/sshd-health.timer");
|
|
87
|
+
lines.push(" owner: root:root");
|
|
88
|
+
lines.push(" permissions: '0644'");
|
|
89
|
+
lines.push(" content: |");
|
|
90
|
+
lines.push(" [Unit]");
|
|
91
|
+
lines.push(" Description=Run sshd health check every minute");
|
|
92
|
+
lines.push(" [Timer]");
|
|
93
|
+
lines.push(" OnBootSec=30");
|
|
94
|
+
lines.push(" OnUnitActiveSec=60");
|
|
95
|
+
lines.push(" [Install]");
|
|
96
|
+
lines.push(" WantedBy=timers.target");
|
|
97
|
+
lines.push("");
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
function sshdHardeningRunCmd() {
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(" # SSH hardening: reload sshd, enable fail2ban and health monitoring");
|
|
103
|
+
lines.push(" - mkdir -p /opt/monitoring");
|
|
104
|
+
lines.push(" - systemctl reload ssh || systemctl restart ssh");
|
|
105
|
+
lines.push(" - systemctl enable --now fail2ban");
|
|
106
|
+
lines.push(" - systemctl daemon-reload");
|
|
107
|
+
lines.push(" - systemctl enable --now sshd-health.timer");
|
|
108
|
+
lines.push("");
|
|
109
|
+
return lines;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/bootstrap/firewall.ts
|
|
113
|
+
var DEFAULT_UFW_GENESIS_OPTIONS = {
|
|
114
|
+
allowSSHFrom: [],
|
|
115
|
+
allowHTTP: true,
|
|
116
|
+
allowHTTPS: true,
|
|
117
|
+
allowNodeAgent: false,
|
|
118
|
+
verboseLogging: false
|
|
119
|
+
};
|
|
120
|
+
var DEFAULT_UFW_WORKER_OPTIONS = {
|
|
121
|
+
allowSSHFrom: [],
|
|
122
|
+
allowHTTP: false,
|
|
123
|
+
allowHTTPS: false,
|
|
124
|
+
allowNodeAgent: true,
|
|
125
|
+
verboseLogging: false
|
|
126
|
+
};
|
|
127
|
+
function ufwFirewallPackages() {
|
|
128
|
+
return [
|
|
129
|
+
" - ufw"
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
function ufwFirewallWriteFiles(options = {}) {
|
|
133
|
+
const lines = [];
|
|
134
|
+
lines.push(" # UFW before.rules - stateful firewall and network security");
|
|
135
|
+
lines.push(" - path: /etc/ufw/before.rules");
|
|
136
|
+
lines.push(" owner: root:root");
|
|
137
|
+
lines.push(" permissions: '0644'");
|
|
138
|
+
lines.push(" content: |");
|
|
139
|
+
lines.push(" #");
|
|
140
|
+
lines.push(" # UFW before.rules - applied before UFW rules");
|
|
141
|
+
lines.push(" #");
|
|
142
|
+
lines.push(" # Start with the standard configuration");
|
|
143
|
+
lines.push(" *filter");
|
|
144
|
+
lines.push(" :ufw-before-input - [0:0]");
|
|
145
|
+
lines.push(" :ufw-before-output - [0:0]");
|
|
146
|
+
lines.push(" :ufw-before-forward - [0:0]");
|
|
147
|
+
lines.push(" :ufw-not-local - [0:0]");
|
|
148
|
+
lines.push(" # End of lines to adjust");
|
|
149
|
+
lines.push("");
|
|
150
|
+
lines.push(" # Allow all on loopback");
|
|
151
|
+
lines.push(" -A ufw-before-input -i lo -j ACCEPT");
|
|
152
|
+
lines.push(" -A ufw-before-output -o lo -j ACCEPT");
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push(" # Drop invalid packets");
|
|
155
|
+
lines.push(" -A ufw-before-input -m conntrack --ctstate INVALID -j DROP");
|
|
156
|
+
lines.push("");
|
|
157
|
+
lines.push(" # Allow established and related connections");
|
|
158
|
+
lines.push(" -A ufw-before-input -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT");
|
|
159
|
+
lines.push(" -A ufw-before-output -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT");
|
|
160
|
+
lines.push("");
|
|
161
|
+
lines.push(" # Allow ICMP messages (required for PMTU discovery)");
|
|
162
|
+
lines.push(" -A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT");
|
|
163
|
+
lines.push(" -A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT");
|
|
164
|
+
lines.push(" -A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT");
|
|
165
|
+
lines.push(" -A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT");
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push(" # Drop packets with bogus TCP flags (potential scan)");
|
|
168
|
+
lines.push(" -A ufw-before-input -p tcp --tcp-flags ALL NONE -j DROP");
|
|
169
|
+
lines.push(" -A ufw-before-input -p tcp --tcp-flags ALL ALL -j DROP");
|
|
170
|
+
lines.push("");
|
|
171
|
+
lines.push(" # Commit changes");
|
|
172
|
+
lines.push(" COMMIT");
|
|
173
|
+
lines.push("");
|
|
174
|
+
lines.push(" # UFW sysctl.conf - kernel network hardening");
|
|
175
|
+
lines.push(" - path: /etc/ufw/sysctl.conf");
|
|
176
|
+
lines.push(" owner: root:root");
|
|
177
|
+
lines.push(" permissions: '0644'");
|
|
178
|
+
lines.push(" content: |");
|
|
179
|
+
lines.push(" #");
|
|
180
|
+
lines.push(" # UFW sysctl.conf - kernel network security parameters");
|
|
181
|
+
lines.push(" #");
|
|
182
|
+
lines.push("");
|
|
183
|
+
lines.push(" # IP spoofing protection");
|
|
184
|
+
lines.push(" net/ipv4/conf/all/rp_filter=1");
|
|
185
|
+
lines.push(" net/ipv4/conf/default/rp_filter=1");
|
|
186
|
+
lines.push("");
|
|
187
|
+
lines.push(" # Ignore ICMP redirect messages");
|
|
188
|
+
lines.push(" net/ipv4/conf/all/accept_redirects=0");
|
|
189
|
+
lines.push(" net/ipv6/conf/all/accept_redirects=0");
|
|
190
|
+
lines.push(" net/ipv4/conf/default/accept_redirects=0");
|
|
191
|
+
lines.push(" net/ipv6/conf/default/accept_redirects=0");
|
|
192
|
+
lines.push("");
|
|
193
|
+
lines.push(" # Ignore send redirects");
|
|
194
|
+
lines.push(" net/ipv4/conf/all/send_redirects=0");
|
|
195
|
+
lines.push(" net/ipv4/conf/default/send_redirects=0");
|
|
196
|
+
lines.push("");
|
|
197
|
+
lines.push(" # Log martian packets (packets with impossible addresses)");
|
|
198
|
+
lines.push(" net/ipv4/conf/all/log_martians=1");
|
|
199
|
+
lines.push(" net/ipv4/conf/default/log_martians=1");
|
|
200
|
+
lines.push("");
|
|
201
|
+
lines.push(" # SYN cookies protection (SYN flood mitigation)");
|
|
202
|
+
lines.push(" net/ipv4/tcp_syncookies=1");
|
|
203
|
+
lines.push("");
|
|
204
|
+
lines.push(" # Source address verification (spoofing protection)");
|
|
205
|
+
lines.push(" net/ipv4/conf/all/secure_redirects=1");
|
|
206
|
+
lines.push(" net/ipv4/conf/default/secure_redirects=1");
|
|
207
|
+
lines.push("");
|
|
208
|
+
return lines;
|
|
209
|
+
}
|
|
210
|
+
function ufwFirewallRunCmd(options = {}) {
|
|
211
|
+
const {
|
|
212
|
+
allowSSHFrom = [],
|
|
213
|
+
allowHTTP = true,
|
|
214
|
+
allowHTTPS = true,
|
|
215
|
+
allowNodeAgent = false,
|
|
216
|
+
additionalPorts = [],
|
|
217
|
+
verboseLogging = false
|
|
218
|
+
} = options;
|
|
219
|
+
const lines = [];
|
|
220
|
+
lines.push(" # UFW Firewall: Configure and enable secure firewall");
|
|
221
|
+
lines.push("");
|
|
222
|
+
lines.push(" # Set default policies: deny incoming, allow outgoing");
|
|
223
|
+
lines.push(" - ufw --force reset");
|
|
224
|
+
lines.push(" - ufw default deny incoming");
|
|
225
|
+
lines.push(" - ufw default allow outgoing");
|
|
226
|
+
lines.push(" - ufw default deny forwarded");
|
|
227
|
+
lines.push("");
|
|
228
|
+
lines.push(" # Allow loopback interface");
|
|
229
|
+
lines.push(" - ufw allow in on lo");
|
|
230
|
+
lines.push("");
|
|
231
|
+
if (allowSSHFrom.length === 0) {
|
|
232
|
+
lines.push(" # Allow SSH with rate limiting (6 connections in 30 seconds)");
|
|
233
|
+
lines.push(" - ufw limit 22/tcp comment 'Rate-limited SSH'");
|
|
234
|
+
} else {
|
|
235
|
+
for (const source of allowSSHFrom) {
|
|
236
|
+
lines.push(` - ufw allow from ${source} to any port 22 proto tcp comment 'SSH from ${source}'`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
lines.push("");
|
|
240
|
+
if (allowHTTP) {
|
|
241
|
+
lines.push(" # Allow HTTP");
|
|
242
|
+
lines.push(" - ufw allow 80/tcp comment 'HTTP'");
|
|
243
|
+
}
|
|
244
|
+
if (allowHTTPS) {
|
|
245
|
+
lines.push(" # Allow HTTPS");
|
|
246
|
+
lines.push(" - ufw allow 443/tcp comment 'HTTPS'");
|
|
247
|
+
}
|
|
248
|
+
lines.push("");
|
|
249
|
+
if (allowNodeAgent) {
|
|
250
|
+
lines.push(" # Allow Node Agent (internal communication)");
|
|
251
|
+
lines.push(" - ufw allow 8911/tcp comment 'Node Agent'");
|
|
252
|
+
lines.push("");
|
|
253
|
+
}
|
|
254
|
+
lines.push(" # Allow Tailscale VPN");
|
|
255
|
+
lines.push(" - ufw allow 41641/udp comment 'Tailscale'");
|
|
256
|
+
lines.push("");
|
|
257
|
+
if (additionalPorts.length > 0) {
|
|
258
|
+
lines.push(" # Additional custom ports");
|
|
259
|
+
for (const portConfig of additionalPorts) {
|
|
260
|
+
const protocol = portConfig.protocol || "tcp";
|
|
261
|
+
const comment = portConfig.comment || `Custom port ${portConfig.port}`;
|
|
262
|
+
lines.push(` - ufw allow ${portConfig.port}/${protocol} comment '${comment}'`);
|
|
263
|
+
}
|
|
264
|
+
lines.push("");
|
|
265
|
+
}
|
|
266
|
+
if (verboseLogging) {
|
|
267
|
+
lines.push(" # Enable verbose logging");
|
|
268
|
+
lines.push(" - ufw logging on");
|
|
269
|
+
lines.push(" - ufw logging high");
|
|
270
|
+
} else {
|
|
271
|
+
lines.push(" # Enable rate-limited logging (prevent log flooding)");
|
|
272
|
+
lines.push(" - ufw logging on");
|
|
273
|
+
lines.push(" - ufw logging low");
|
|
274
|
+
}
|
|
275
|
+
lines.push("");
|
|
276
|
+
lines.push(" # Enable and reload UFW");
|
|
277
|
+
lines.push(" - ufw --force enable");
|
|
278
|
+
lines.push(" - ufw reload");
|
|
279
|
+
lines.push("");
|
|
280
|
+
lines.push(" # Display firewall status");
|
|
281
|
+
lines.push(" - ufw status verbose > /var/log/ufw-bootstrap-status.log");
|
|
282
|
+
lines.push("");
|
|
283
|
+
return lines;
|
|
284
|
+
}
|
|
285
|
+
function generateUFWFirewallForGenesis(options = DEFAULT_UFW_GENESIS_OPTIONS) {
|
|
286
|
+
return {
|
|
287
|
+
packages: ufwFirewallPackages(),
|
|
288
|
+
writeFiles: ufwFirewallWriteFiles(options),
|
|
289
|
+
runCmd: ufwFirewallRunCmd(options)
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function generateUFWFirewallForWorker(options = DEFAULT_UFW_WORKER_OPTIONS) {
|
|
293
|
+
return {
|
|
294
|
+
packages: ufwFirewallPackages(),
|
|
295
|
+
writeFiles: ufwFirewallWriteFiles(options),
|
|
296
|
+
runCmd: ufwFirewallRunCmd(options)
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/bootstrap/kernel-hardening.ts
|
|
301
|
+
function kernelHardeningPackages() {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
function kernelHardeningWriteFiles() {
|
|
305
|
+
const lines = [];
|
|
306
|
+
lines.push(" # Kernel hardening: sysctl.d configuration for 2026 best practices");
|
|
307
|
+
lines.push(" # This file persists across reboots and overrides /etc/sysctl.conf");
|
|
308
|
+
lines.push(" - path: /etc/sysctl.d/99-security-hardening.conf");
|
|
309
|
+
lines.push(" owner: root:root");
|
|
310
|
+
lines.push(" permissions: '0644'");
|
|
311
|
+
lines.push(" content: |");
|
|
312
|
+
lines.push(" # =================================================================");
|
|
313
|
+
lines.push(" # Kernel Security Hardening Configuration");
|
|
314
|
+
lines.push(" # =================================================================");
|
|
315
|
+
lines.push(" # Applied via cloud-init for com.hetzner.codespaces");
|
|
316
|
+
lines.push(" # Version: 1.0.0 (2026 best practices)");
|
|
317
|
+
lines.push(" #");
|
|
318
|
+
lines.push(" # This configuration follows CIS Benchmark and NIST guidelines");
|
|
319
|
+
lines.push(" # See: /usr/share/doc/linux-doc/sysctl/ for parameter documentation");
|
|
320
|
+
lines.push("");
|
|
321
|
+
lines.push(" # =================================================================");
|
|
322
|
+
lines.push(" # 1. IP SPOOFING PROTECTION");
|
|
323
|
+
lines.push(" # =================================================================");
|
|
324
|
+
lines.push(" # Enable reverse path filtering (validates source addresses)");
|
|
325
|
+
lines.push(" # Prevents IP spoofing attacks by dropping packets with invalid sources");
|
|
326
|
+
lines.push(" net.ipv4.conf.all.rp_filter = 1");
|
|
327
|
+
lines.push(" net.ipv4.conf.default.rp_filter = 1");
|
|
328
|
+
lines.push("");
|
|
329
|
+
lines.push(" # Log martian packets (packets with impossible addresses)");
|
|
330
|
+
lines.push(" # Helps detect spoofing attempts and network misconfigurations");
|
|
331
|
+
lines.push(" net.ipv4.conf.all.log_martians = 1");
|
|
332
|
+
lines.push("");
|
|
333
|
+
lines.push(" # Disable ICMP redirect acceptance (prevent MITM attacks)");
|
|
334
|
+
lines.push(" net.ipv4.conf.all.accept_redirects = 0");
|
|
335
|
+
lines.push(" net.ipv4.conf.default.accept_redirects = 0");
|
|
336
|
+
lines.push(" net.ipv4.conf.all.secure_redirects = 0");
|
|
337
|
+
lines.push(" net.ipv4.conf.default.secure_redirects = 0");
|
|
338
|
+
lines.push("");
|
|
339
|
+
lines.push(" # Disable sending ICMP redirects");
|
|
340
|
+
lines.push(" net.ipv4.conf.all.send_redirects = 0");
|
|
341
|
+
lines.push(" net.ipv4.conf.default.send_redirects = 0");
|
|
342
|
+
lines.push("");
|
|
343
|
+
lines.push(" # =================================================================");
|
|
344
|
+
lines.push(" # 2. SYN FLOOD PROTECTION");
|
|
345
|
+
lines.push(" # =================================================================");
|
|
346
|
+
lines.push(" # Enable SYN cookies (protects against SYN flood attacks)");
|
|
347
|
+
lines.push(" # Allows server to continue accepting connections under SYN flood");
|
|
348
|
+
lines.push(" net.ipv4.tcp_syncookies = 1");
|
|
349
|
+
lines.push("");
|
|
350
|
+
lines.push(" # Reuse TIME_WAIT sockets for new connections (safer, faster)");
|
|
351
|
+
lines.push(" # Reduces connection table exhaustion under high load");
|
|
352
|
+
lines.push(" net.ipv4.tcp_tw_reuse = 1");
|
|
353
|
+
lines.push("");
|
|
354
|
+
lines.push(" # Reduce SYN backlog and timeouts for faster detection");
|
|
355
|
+
lines.push(" net.ipv4.tcp_max_syn_backlog = 2048");
|
|
356
|
+
lines.push(" net.ipv4.tcp_synack_retries = 2");
|
|
357
|
+
lines.push(" net.ipv4.tcp_syn_retries = 5");
|
|
358
|
+
lines.push("");
|
|
359
|
+
lines.push(" # =================================================================");
|
|
360
|
+
lines.push(" # 3. NETWORK STACK HARDENING");
|
|
361
|
+
lines.push(" # =================================================================");
|
|
362
|
+
lines.push(" # Disable ICMP redirect acceptance (IPv6)");
|
|
363
|
+
lines.push(" net.ipv6.conf.all.accept_redirects = 0");
|
|
364
|
+
lines.push(" net.ipv6.conf.default.accept_redirects = 0");
|
|
365
|
+
lines.push("");
|
|
366
|
+
lines.push(" # Ignore ICMP broadcasts (prevent smurf attacks)");
|
|
367
|
+
lines.push(" net.ipv4.icmp_echo_ignore_broadcasts = 1");
|
|
368
|
+
lines.push("");
|
|
369
|
+
lines.push(" # Ignore bogus ICMP error responses (prevent ICMP attacks)");
|
|
370
|
+
lines.push(" net.ipv4.icmp_ignore_bogus_error_responses = 1");
|
|
371
|
+
lines.push("");
|
|
372
|
+
lines.push(" # Enable TCP timestamps (RFC 1323) for better sequence handling");
|
|
373
|
+
lines.push(" # Also protects against wrapped sequence number attacks");
|
|
374
|
+
lines.push(" net.ipv4.tcp_timestamps = 1");
|
|
375
|
+
lines.push("");
|
|
376
|
+
lines.push(" # Enable TCP selective acknowledgments (better performance)");
|
|
377
|
+
lines.push(" net.ipv4.tcp_sack = 1");
|
|
378
|
+
lines.push("");
|
|
379
|
+
lines.push(" # =================================================================");
|
|
380
|
+
lines.push(" # 4. CORE DUMP RESTRICTIONS");
|
|
381
|
+
lines.push(" # =================================================================");
|
|
382
|
+
lines.push(" # Disable core dumps for setuid programs (prevent privilege escalation)");
|
|
383
|
+
lines.push(" fs.suid_dumpable = 0");
|
|
384
|
+
lines.push("");
|
|
385
|
+
lines.push(" # Limit core dump size to 0 (disable core dumps)");
|
|
386
|
+
lines.push(" # Override in /etc/security/limits.conf if needed for debugging");
|
|
387
|
+
lines.push(" kernel.core_pattern = |/bin/false");
|
|
388
|
+
lines.push("");
|
|
389
|
+
lines.push(" # =================================================================");
|
|
390
|
+
lines.push(" # 5. MEMORY PROTECTION (ASLR)");
|
|
391
|
+
lines.push(" # =================================================================");
|
|
392
|
+
lines.push(" # Enable Address Space Layout Randomization (full)");
|
|
393
|
+
lines.push(" # Makes exploitation of memory corruption vulnerabilities harder");
|
|
394
|
+
lines.push(" # 0: Disabled, 1: Conservative, 2: Full (default)");
|
|
395
|
+
lines.push(" kernel.randomize_va_space = 2");
|
|
396
|
+
lines.push("");
|
|
397
|
+
lines.push(" # =================================================================");
|
|
398
|
+
lines.push(" # 6. FILESYSTEM PROTECTION");
|
|
399
|
+
lines.push(" # =================================================================");
|
|
400
|
+
lines.push(" # Hard link/symlink protection (prevent time-of-check time-of-use)");
|
|
401
|
+
lines.push(" fs.protected_hardlinks = 1");
|
|
402
|
+
lines.push(" fs.protected_symlinks = 1");
|
|
403
|
+
lines.push("");
|
|
404
|
+
lines.push(" # FIFO protection (prevent FIFO attacks on world-writable directories)");
|
|
405
|
+
lines.push(" fs.protected_fifos = 2");
|
|
406
|
+
lines.push("");
|
|
407
|
+
lines.push(" # Regular file protection (prevent file overwrite attacks)");
|
|
408
|
+
lines.push(" fs.protected_regular = 2");
|
|
409
|
+
lines.push("");
|
|
410
|
+
lines.push(" # =================================================================");
|
|
411
|
+
lines.push(" # 7. NETWORK BEHAVIOR TUNING");
|
|
412
|
+
lines.push(" # =================================================================");
|
|
413
|
+
lines.push(" # Enable TCP Fast Open (TFO) for reduced latency");
|
|
414
|
+
lines.push(" net.ipv4.tcp_fastopen = 3");
|
|
415
|
+
lines.push("");
|
|
416
|
+
lines.push(" # Disable source routing (prevent packet routing manipulation)");
|
|
417
|
+
lines.push(" net.ipv4.conf.all.accept_source_route = 0");
|
|
418
|
+
lines.push(" net.ipv4.conf.default.accept_source_route = 0");
|
|
419
|
+
lines.push(" net.ipv6.conf.all.accept_source_route = 0");
|
|
420
|
+
lines.push(" net.ipv6.conf.default.accept_source_route = 0");
|
|
421
|
+
lines.push("");
|
|
422
|
+
lines.push(" # Enable TCP window scaling (RFC 7323) for high-bandwidth links");
|
|
423
|
+
lines.push(" net.ipv4.tcp_window_scaling = 1");
|
|
424
|
+
lines.push("");
|
|
425
|
+
lines.push(" # =================================================================");
|
|
426
|
+
lines.push(" # 8. SECURITY-RELATED KERNEL PARAMETERS");
|
|
427
|
+
lines.push(" # =================================================================");
|
|
428
|
+
lines.push(" # Disable magic sysrq key (prevent console-based attacks)");
|
|
429
|
+
lines.push(" # 0: Disabled, 1: Enable (for debugging only)");
|
|
430
|
+
lines.push(" kernel.sysrq = 0");
|
|
431
|
+
lines.push("");
|
|
432
|
+
lines.push(" # Disable kexec system call (prevent kernel replacement)");
|
|
433
|
+
lines.push(" # 0: Disabled, 1: Enabled");
|
|
434
|
+
lines.push(" kernel.kexec_load = 0");
|
|
435
|
+
lines.push("");
|
|
436
|
+
lines.push(" # Disable user namespaces (prevent container breakouts)");
|
|
437
|
+
lines.push(" # 0: Disabled, 1: Enabled");
|
|
438
|
+
lines.push(" user.max_user_namespaces = 0");
|
|
439
|
+
lines.push("");
|
|
440
|
+
lines.push(" # Enable unprivileged bpf disabled (prevent eBPF-based exploits)");
|
|
441
|
+
lines.push(" # 0: Disabled, 1: Enabled");
|
|
442
|
+
lines.push(" kernel.unprivileged_bpf_disabled = 1");
|
|
443
|
+
lines.push("");
|
|
444
|
+
lines.push(" # =================================================================");
|
|
445
|
+
lines.push(" # 9. ADDITIONAL HARDENING (2026)");
|
|
446
|
+
lines.push(" # =================================================================");
|
|
447
|
+
lines.push(" # Disable IPv6 if not needed (uncomment if IPv6 is disabled)");
|
|
448
|
+
lines.push(" # net.ipv6.conf.all.disable_ipv6 = 1");
|
|
449
|
+
lines.push(" # net.ipv6.conf.default.disable_ipv6 = 1");
|
|
450
|
+
lines.push("");
|
|
451
|
+
lines.push(" # Enable dmesg restriction (prevent kernel info leaks)");
|
|
452
|
+
lines.push(" kernel.dmesg_restrict = 1");
|
|
453
|
+
lines.push("");
|
|
454
|
+
lines.push(" # Restrict ptrace scope (prevent process tracing by non-parent)");
|
|
455
|
+
lines.push(" # 0: Traditional, 1: Restricted, 2: Admin-only, 3: No attach");
|
|
456
|
+
lines.push(" kernel.yama.ptrace_scope = 2");
|
|
457
|
+
lines.push("");
|
|
458
|
+
lines.push(" # =================================================================");
|
|
459
|
+
lines.push(" # 10. PERFORMANCE TUNING (safe defaults)");
|
|
460
|
+
lines.push(" # =================================================================");
|
|
461
|
+
lines.push(" # Increase connection tracking table size (for stateful firewalls)");
|
|
462
|
+
lines.push(" net.netfilter.nf_conntrack_max = 262144");
|
|
463
|
+
lines.push("");
|
|
464
|
+
lines.push(" # Reduce TCP keepalive timeouts for faster dead peer detection");
|
|
465
|
+
lines.push(" net.ipv4.tcp_keepalive_time = 600");
|
|
466
|
+
lines.push(" net.ipv4.tcp_keepalive_intvl = 30");
|
|
467
|
+
lines.push(" net.ipv4.tcp_keepalive_probes = 3");
|
|
468
|
+
lines.push("");
|
|
469
|
+
return lines;
|
|
470
|
+
}
|
|
471
|
+
function kernelHardeningRunCmd() {
|
|
472
|
+
const lines = [];
|
|
473
|
+
lines.push(" # Kernel hardening: apply sysctl settings immediately");
|
|
474
|
+
lines.push(" # Settings are already in /etc/sysctl.d/99-security-hardening.conf");
|
|
475
|
+
lines.push("");
|
|
476
|
+
lines.push(" # Apply all sysctl settings (overrides defaults immediately)");
|
|
477
|
+
lines.push(" - sysctl --system");
|
|
478
|
+
lines.push("");
|
|
479
|
+
lines.push(" # Log applied settings for audit trail");
|
|
480
|
+
lines.push(" - sysctl -a | grep -E '(rp_filter|syncookies|randomize_va|suid_dump)' > /var/log/kernel-hardening.log 2>&1 || true");
|
|
481
|
+
lines.push("");
|
|
482
|
+
lines.push(" # Display summary of critical hardening settings");
|
|
483
|
+
lines.push(" - |");
|
|
484
|
+
lines.push(" echo '========================================'");
|
|
485
|
+
lines.push(" echo 'Kernel Hardening Applied (2026)'");
|
|
486
|
+
lines.push(" echo '========================================'");
|
|
487
|
+
lines.push(" echo 'IP Spoof Protection: '$(sysctl -n net.ipv4.conf.all.rp_filter)");
|
|
488
|
+
lines.push(" echo 'SYN Cookies: '$(sysctl -n net.ipv4.tcp_syncookies)");
|
|
489
|
+
lines.push(" echo 'ASLR Level: '$(sysctl -n kernel.randomize_va_space)");
|
|
490
|
+
lines.push(" echo 'SUID Core Dumps: '$(sysctl -n fs.suid_dumpable)");
|
|
491
|
+
lines.push(" echo 'Hard Links Protected: '$(sysctl -n fs.protected_hardlinks)");
|
|
492
|
+
lines.push(" echo 'Ptrace Scope: '$(sysctl -n kernel.yama.ptrace_scope)");
|
|
493
|
+
lines.push(" echo '========================================'");
|
|
494
|
+
lines.push("");
|
|
495
|
+
return lines;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/bootstrap/security-audit.ts
|
|
499
|
+
function securityAuditPackages() {
|
|
500
|
+
return [
|
|
501
|
+
" - lynis"
|
|
502
|
+
];
|
|
503
|
+
}
|
|
504
|
+
function securityAuditWriteFiles() {
|
|
505
|
+
const lines = [];
|
|
506
|
+
lines.push(" # Security audit script - runs after all hardening");
|
|
507
|
+
lines.push(" - path: /opt/monitoring/security-audit.sh");
|
|
508
|
+
lines.push(" owner: root:root");
|
|
509
|
+
lines.push(" permissions: '0755'");
|
|
510
|
+
lines.push(" content: |");
|
|
511
|
+
lines.push(" #!/bin/bash");
|
|
512
|
+
lines.push(" set -euo pipefail");
|
|
513
|
+
lines.push(' LOGFILE="/var/log/security-audit.log"');
|
|
514
|
+
lines.push(' REPORTFILE="/var/log/security-audit.json"');
|
|
515
|
+
lines.push(' echo "Security Audit started at $(date -Iseconds)" | tee "$LOGFILE"');
|
|
516
|
+
lines.push("");
|
|
517
|
+
lines.push(" # Firewall status");
|
|
518
|
+
lines.push(' echo "" | tee -a "$LOGFILE"');
|
|
519
|
+
lines.push(' echo "=== UFW Firewall Status ===" | tee -a "$LOGFILE"');
|
|
520
|
+
lines.push(' ufw status verbose | tee -a "$LOGFILE" || true');
|
|
521
|
+
lines.push("");
|
|
522
|
+
lines.push(" # Fail2ban status");
|
|
523
|
+
lines.push(' echo "" | tee -a "$LOGFILE"');
|
|
524
|
+
lines.push(' echo "=== Fail2ban Status ===" | tee -a "$LOGFILE"');
|
|
525
|
+
lines.push(' systemctl status fail2ban --no-pager | tee -a "$LOGFILE" || true');
|
|
526
|
+
lines.push(' fail2ban-client status sshd | tee -a "$LOGFILE" || true');
|
|
527
|
+
lines.push("");
|
|
528
|
+
lines.push(" # SSHd status");
|
|
529
|
+
lines.push(' echo "" | tee -a "$LOGFILE"');
|
|
530
|
+
lines.push(' echo "=== SSHd Status ===" | tee -a "$LOGFILE"');
|
|
531
|
+
lines.push(' systemctl status ssh --no-pager | tee -a "$LOGFILE" || true');
|
|
532
|
+
lines.push(` sshd -T | grep -E '(PasswordAuthentication|PermitRootLogin|MaxStartups)' | tee -a "$LOGFILE" || true`);
|
|
533
|
+
lines.push("");
|
|
534
|
+
lines.push(" # Kernel hardening status");
|
|
535
|
+
lines.push(' echo "" | tee -a "$LOGFILE"');
|
|
536
|
+
lines.push(' echo "=== Kernel Hardening Status ===" | tee -a "$LOGFILE"');
|
|
537
|
+
lines.push(' sysctl randomize_va_space kptr_restrict tcp_syncookies | tee -a "$LOGFILE" || true');
|
|
538
|
+
lines.push("");
|
|
539
|
+
lines.push(" # Lynis audit (warnings only, non-interactive)");
|
|
540
|
+
lines.push(' echo "" | tee -a "$LOGFILE"');
|
|
541
|
+
lines.push(' echo "=== Lynis Security Audit ===" | tee -a "$LOGFILE"');
|
|
542
|
+
lines.push(' lynis audit system --warnings-only --quiet 2>&1 | tee -a "$LOGFILE" || true');
|
|
543
|
+
lines.push("");
|
|
544
|
+
lines.push(" # Generate JSON report");
|
|
545
|
+
lines.push(" timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)");
|
|
546
|
+
lines.push(' ufw_active=$(ufw status | grep -c "Status: active" || echo "0")');
|
|
547
|
+
lines.push(' fail2ban_active=$(systemctl is-active fail2ban 2>/dev/null || echo "inactive")');
|
|
548
|
+
lines.push(' sshd_active=$(systemctl is-active ssh 2>/dev/null || echo "inactive")');
|
|
549
|
+
lines.push(' aslr_enabled=$(cat /proc/sys/kernel/randomize_va_space 2>/dev/null || echo "0")');
|
|
550
|
+
lines.push(' cat > "$REPORTFILE" <<AUDEOF');
|
|
551
|
+
lines.push(" {");
|
|
552
|
+
lines.push(' "timestamp": "$timestamp",');
|
|
553
|
+
lines.push(' "firewall": { "active": $ufw_active },');
|
|
554
|
+
lines.push(' "fail2ban": { "active": "$fail2ban_active" },');
|
|
555
|
+
lines.push(' "sshd": { "active": "$sshd_active" },');
|
|
556
|
+
lines.push(' "kernel_hardening": { "aslr_enabled": $aslr_enabled }');
|
|
557
|
+
lines.push(" }");
|
|
558
|
+
lines.push(" AUDEOF");
|
|
559
|
+
lines.push(' echo "Security Audit completed at $(date -Iseconds)" | tee -a "$LOGFILE"');
|
|
560
|
+
lines.push(' echo "Report saved to $REPORTFILE" | tee -a "$LOGFILE"');
|
|
561
|
+
lines.push("");
|
|
562
|
+
return lines;
|
|
563
|
+
}
|
|
564
|
+
function securityAuditRunCmd() {
|
|
565
|
+
const lines = [];
|
|
566
|
+
lines.push(" # Security audit: run comprehensive security check");
|
|
567
|
+
lines.push(" - mkdir -p /opt/monitoring");
|
|
568
|
+
lines.push(" - /opt/monitoring/security-audit.sh");
|
|
569
|
+
lines.push(' - echo "Security audit completed at $(date -Iseconds)" | tee -a /root/.bootstrap-status');
|
|
570
|
+
lines.push("");
|
|
571
|
+
return lines;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/bootstrap/genesis.ts
|
|
575
|
+
function generateGenesisBootstrap(options) {
|
|
576
|
+
const {
|
|
577
|
+
adminSSHKey,
|
|
578
|
+
genesisRepo = "https://github.com/ebowwa/com.hetzner.codespaces",
|
|
579
|
+
genesisBranch = "main",
|
|
580
|
+
hostname = "genesis",
|
|
581
|
+
defaultServerType = "cpx11",
|
|
582
|
+
defaultLocation = "fsn1",
|
|
583
|
+
maxWorkers = "10",
|
|
584
|
+
packages = [],
|
|
585
|
+
additionalCommands = [],
|
|
586
|
+
enableSecurity = true
|
|
587
|
+
} = options;
|
|
588
|
+
if (!adminSSHKey) {
|
|
589
|
+
throw new Error("adminSSHKey is required for Genesis bootstrap");
|
|
590
|
+
}
|
|
591
|
+
const lines = [];
|
|
592
|
+
lines.push("#cloud-config");
|
|
593
|
+
lines.push("# Genesis Server Bootstrap Configuration");
|
|
594
|
+
lines.push("# Version: 1.0.0");
|
|
595
|
+
lines.push("");
|
|
596
|
+
lines.push("# This cloud-init config bootstraps a Genesis server that:");
|
|
597
|
+
lines.push("# - Runs com.hetzner.codespaces web application");
|
|
598
|
+
lines.push("# - Uses the existing Hetzner API to create any server");
|
|
599
|
+
lines.push("# - Can be ephemeral and recreated at any time");
|
|
600
|
+
lines.push("");
|
|
601
|
+
lines.push("# IMPORTANT: Never store secrets in cloud-init! Use Vault/SOPS/external sources.");
|
|
602
|
+
lines.push("");
|
|
603
|
+
lines.push("# =====================================================");
|
|
604
|
+
lines.push("# STAGE 1: Network & Early Setup (Network stage)");
|
|
605
|
+
lines.push("# =====================================================");
|
|
606
|
+
lines.push("");
|
|
607
|
+
lines.push(`hostname: ${hostname}`);
|
|
608
|
+
lines.push("manage_etc_hosts: true");
|
|
609
|
+
lines.push("timezone: UTC");
|
|
610
|
+
lines.push("");
|
|
611
|
+
lines.push("# =====================================================");
|
|
612
|
+
lines.push("# STAGE 2: SSH & Security (Network stage)");
|
|
613
|
+
lines.push("# =====================================================");
|
|
614
|
+
lines.push("");
|
|
615
|
+
lines.push("ssh_pwauth: false");
|
|
616
|
+
lines.push("");
|
|
617
|
+
lines.push("# Create genesis service user");
|
|
618
|
+
lines.push("users:");
|
|
619
|
+
lines.push(" - name: genesis");
|
|
620
|
+
lines.push(" gecos: Genesis Service Account");
|
|
621
|
+
lines.push(" primary_group: genesis");
|
|
622
|
+
lines.push(" groups: docker,wheel");
|
|
623
|
+
lines.push(" sudo: ALL=(ALL) NOPASSWD:ALL");
|
|
624
|
+
lines.push(" shell: /bin/bash");
|
|
625
|
+
lines.push(" lock_passwd: true");
|
|
626
|
+
lines.push(" ssh_authorized_keys:");
|
|
627
|
+
lines.push(` - ${adminSSHKey}`);
|
|
628
|
+
lines.push("");
|
|
629
|
+
lines.push("# =====================================================");
|
|
630
|
+
lines.push("# STAGE 3: Package Management (Config stage)");
|
|
631
|
+
lines.push("# =====================================================");
|
|
632
|
+
lines.push("");
|
|
633
|
+
lines.push("package_update: true");
|
|
634
|
+
lines.push("package_upgrade: false");
|
|
635
|
+
lines.push("package_reboot_if_required: true");
|
|
636
|
+
lines.push("");
|
|
637
|
+
lines.push("packages:");
|
|
638
|
+
lines.push(" - curl");
|
|
639
|
+
lines.push(" - wget");
|
|
640
|
+
lines.push(" - git");
|
|
641
|
+
lines.push(" - unzip");
|
|
642
|
+
lines.push(" - jq");
|
|
643
|
+
lines.push(" - build-essential");
|
|
644
|
+
if (enableSecurity) {
|
|
645
|
+
lines.push(" # Security: UFW Firewall");
|
|
646
|
+
lines.push(...ufwFirewallPackages());
|
|
647
|
+
}
|
|
648
|
+
if (enableSecurity) {
|
|
649
|
+
lines.push(" # Security: Kernel hardening");
|
|
650
|
+
lines.push(...kernelHardeningPackages());
|
|
651
|
+
}
|
|
652
|
+
if (enableSecurity) {
|
|
653
|
+
lines.push(" # Security: SSH hardening");
|
|
654
|
+
lines.push(...sshdHardeningPackages());
|
|
655
|
+
}
|
|
656
|
+
if (enableSecurity) {
|
|
657
|
+
lines.push(" # Security: Security audit");
|
|
658
|
+
lines.push(...securityAuditPackages());
|
|
659
|
+
}
|
|
660
|
+
for (const pkg of packages) {
|
|
661
|
+
lines.push(` - ${pkg}`);
|
|
662
|
+
}
|
|
663
|
+
lines.push("");
|
|
664
|
+
lines.push("# =====================================================");
|
|
665
|
+
lines.push("# STAGE 4: Application Setup (Config stage)");
|
|
666
|
+
lines.push("# =====================================================");
|
|
667
|
+
lines.push("");
|
|
668
|
+
lines.push("write_files:");
|
|
669
|
+
lines.push(" # Genesis application directories");
|
|
670
|
+
lines.push(" - path: /opt/genesis");
|
|
671
|
+
lines.push(" owner: genesis:genesis");
|
|
672
|
+
lines.push(" permissions: '0755'");
|
|
673
|
+
lines.push("");
|
|
674
|
+
lines.push(" - path: /opt/genesis/data");
|
|
675
|
+
lines.push(" owner: genesis:genesis");
|
|
676
|
+
lines.push(" permissions: '0755'");
|
|
677
|
+
lines.push("");
|
|
678
|
+
lines.push(" - path: /var/log/genesis");
|
|
679
|
+
lines.push(" owner: genesis:genesis");
|
|
680
|
+
lines.push(" permissions: '0755'");
|
|
681
|
+
lines.push("");
|
|
682
|
+
lines.push(" # Environment file template (do NOT include actual secrets)");
|
|
683
|
+
lines.push(" - path: /etc/default/genesis.template");
|
|
684
|
+
lines.push(" owner: genesis:genesis");
|
|
685
|
+
lines.push(" permissions: '0640'");
|
|
686
|
+
lines.push(" content: |");
|
|
687
|
+
lines.push(" # Genesis Server Environment Configuration");
|
|
688
|
+
lines.push(" # Copy this to /etc/default/genesis and fill in required values");
|
|
689
|
+
lines.push(" #");
|
|
690
|
+
lines.push(" # DO NOT commit actual secrets to version control!");
|
|
691
|
+
lines.push("");
|
|
692
|
+
lines.push(" # Application Settings");
|
|
693
|
+
lines.push(" NODE_ENV=production");
|
|
694
|
+
lines.push(` PORT=3000`);
|
|
695
|
+
lines.push(` HOST=0.0.0.0`);
|
|
696
|
+
lines.push("");
|
|
697
|
+
lines.push(" # Hetzner API (REQUIRED - use Vault or Secrets Manager in production)");
|
|
698
|
+
lines.push(" # HETZNER_API_TOKEN should be set securely after bootstrap");
|
|
699
|
+
lines.push(" HETZNER_DEFAULT_TYPE=" + defaultServerType);
|
|
700
|
+
lines.push(" HETZNER_DEFAULT_LOCATION=" + defaultLocation);
|
|
701
|
+
lines.push(" MAX_WORKER_NODES=" + maxWorkers);
|
|
702
|
+
lines.push("");
|
|
703
|
+
lines.push(" # Genesis systemd service unit");
|
|
704
|
+
lines.push(" - path: /etc/systemd/system/genesis.service");
|
|
705
|
+
lines.push(" owner: root:root");
|
|
706
|
+
lines.push(" permissions: '0644'");
|
|
707
|
+
lines.push(" content: |");
|
|
708
|
+
lines.push(" [Unit]");
|
|
709
|
+
lines.push(" Description=Genesis Application Server (com.hetzner.codespaces)");
|
|
710
|
+
lines.push(" Documentation=https://github.com/ebowwa/com.hetzner.codespaces");
|
|
711
|
+
lines.push(" After=network-online.target");
|
|
712
|
+
lines.push(" Wants=network-online.target");
|
|
713
|
+
lines.push("");
|
|
714
|
+
lines.push(" [Service]");
|
|
715
|
+
lines.push(" Type=simple");
|
|
716
|
+
lines.push(" User=genesis");
|
|
717
|
+
lines.push(" Group=genesis");
|
|
718
|
+
lines.push(" WorkingDirectory=/opt/genesis");
|
|
719
|
+
lines.push("");
|
|
720
|
+
lines.push(" # Execution");
|
|
721
|
+
lines.push(" ExecStart=/usr/bin/bun start");
|
|
722
|
+
lines.push(" ExecReload=/bin/kill -HUP $MAINPID");
|
|
723
|
+
lines.push("");
|
|
724
|
+
lines.push(" # Restart Policy (with rate limiting)");
|
|
725
|
+
lines.push(" Restart=on-failure");
|
|
726
|
+
lines.push(" RestartSec=5s");
|
|
727
|
+
lines.push(" StartLimitIntervalSec=300");
|
|
728
|
+
lines.push(" StartLimitBurst=5");
|
|
729
|
+
lines.push("");
|
|
730
|
+
lines.push(" # Logging");
|
|
731
|
+
lines.push(" StandardOutput=journal");
|
|
732
|
+
lines.push(" StandardError=journal");
|
|
733
|
+
lines.push(" SyslogIdentifier=genesis");
|
|
734
|
+
lines.push("");
|
|
735
|
+
lines.push(" # Environment");
|
|
736
|
+
lines.push(' Environment="NODE_ENV=production"');
|
|
737
|
+
lines.push(" EnvironmentFile=/etc/default/genesis");
|
|
738
|
+
lines.push(" EnvironmentFile=-/etc/default/genesis.local");
|
|
739
|
+
lines.push("");
|
|
740
|
+
lines.push(" # Resource Limits");
|
|
741
|
+
lines.push(" LimitNOFILE=65536");
|
|
742
|
+
lines.push("");
|
|
743
|
+
if (enableSecurity) {
|
|
744
|
+
lines.push(" # Security Hardening");
|
|
745
|
+
lines.push(" NoNewPrivileges=true");
|
|
746
|
+
lines.push(" PrivateTmp=true");
|
|
747
|
+
lines.push(" ProtectSystem=strict");
|
|
748
|
+
lines.push(" ProtectHome=true");
|
|
749
|
+
lines.push(" ReadWritePaths=/opt/genesis/data /var/log/genesis");
|
|
750
|
+
} else {
|
|
751
|
+
lines.push(" # Security Hardening (minimal)");
|
|
752
|
+
lines.push(" NoNewPrivileges=false");
|
|
753
|
+
lines.push(" PrivateTmp=false");
|
|
754
|
+
}
|
|
755
|
+
lines.push("");
|
|
756
|
+
lines.push(" [Install]");
|
|
757
|
+
lines.push(" WantedBy=multi-user.target");
|
|
758
|
+
lines.push("");
|
|
759
|
+
lines.push(" # Bootstrap status tracking");
|
|
760
|
+
lines.push(" - path: /root/.genesis-bootstrap-status");
|
|
761
|
+
lines.push(" owner: root:root");
|
|
762
|
+
lines.push(" permissions: '0644'");
|
|
763
|
+
lines.push(" content: |");
|
|
764
|
+
lines.push(" status=started");
|
|
765
|
+
lines.push(" started_at=$(date -Iseconds)");
|
|
766
|
+
lines.push(" source=cloud-init");
|
|
767
|
+
lines.push(" version=1.0.0");
|
|
768
|
+
if (enableSecurity) {
|
|
769
|
+
lines.push(" security=enabled");
|
|
770
|
+
}
|
|
771
|
+
lines.push("");
|
|
772
|
+
lines.push(" # Add bun to /etc/environment for all users/shells");
|
|
773
|
+
lines.push(' # Format: Simple KEY="value" pairs, no variable expansion');
|
|
774
|
+
lines.push(" - path: /etc/environment");
|
|
775
|
+
lines.push(" owner: root:root");
|
|
776
|
+
lines.push(" permissions: '0644'");
|
|
777
|
+
lines.push(" content: |");
|
|
778
|
+
lines.push(' PATH="/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"');
|
|
779
|
+
lines.push("");
|
|
780
|
+
if (enableSecurity) {
|
|
781
|
+
lines.push(" # Security Module 1: UFW Firewall configuration");
|
|
782
|
+
lines.push(...ufwFirewallWriteFiles(DEFAULT_UFW_GENESIS_OPTIONS));
|
|
783
|
+
}
|
|
784
|
+
if (enableSecurity) {
|
|
785
|
+
lines.push(" # Security Module 2: Kernel hardening");
|
|
786
|
+
lines.push(...kernelHardeningWriteFiles());
|
|
787
|
+
}
|
|
788
|
+
if (enableSecurity) {
|
|
789
|
+
lines.push(" # Security Module 3: SSH hardening");
|
|
790
|
+
lines.push(...sshdHardeningWriteFiles());
|
|
791
|
+
}
|
|
792
|
+
if (enableSecurity) {
|
|
793
|
+
lines.push(" # Security Module 4: Security audit");
|
|
794
|
+
lines.push(...securityAuditWriteFiles());
|
|
795
|
+
}
|
|
796
|
+
lines.push("# =====================================================");
|
|
797
|
+
lines.push("# STAGE 5: Run Commands (Config stage)");
|
|
798
|
+
lines.push("# =====================================================");
|
|
799
|
+
lines.push("");
|
|
800
|
+
lines.push("runcmd:");
|
|
801
|
+
lines.push(" # Install Bun runtime");
|
|
802
|
+
lines.push(" - curl -fsSL https://bun.sh/install | bash");
|
|
803
|
+
lines.push("");
|
|
804
|
+
lines.push(" # Clone/pull genesis application");
|
|
805
|
+
const cloneCmd = genesisBranch ? `git clone --depth 1 --branch ${genesisBranch} ${genesisRepo} /opt/genesis` : `git clone --depth 1 ${genesisRepo} /opt/genesis`;
|
|
806
|
+
lines.push(` - |`);
|
|
807
|
+
lines.push(` if [ ! -d /opt/genesis/.git ]; then`);
|
|
808
|
+
lines.push(` ${cloneCmd}`);
|
|
809
|
+
lines.push(` else`);
|
|
810
|
+
lines.push(` cd /opt/genesis && git pull`);
|
|
811
|
+
lines.push(` fi`);
|
|
812
|
+
lines.push("");
|
|
813
|
+
lines.push(" # Install dependencies");
|
|
814
|
+
lines.push(" - cd /opt/genesis && bun install");
|
|
815
|
+
lines.push("");
|
|
816
|
+
lines.push(" # Build application (if needed)");
|
|
817
|
+
lines.push(" - cd /opt/genesis && bun run build");
|
|
818
|
+
lines.push("");
|
|
819
|
+
lines.push(" # Configure environment (prompt for secrets or use external source)");
|
|
820
|
+
lines.push(" - |");
|
|
821
|
+
lines.push(` echo "WARNING: HETZNER_API_TOKEN must be configured in /etc/default/genesis"`);
|
|
822
|
+
lines.push("");
|
|
823
|
+
lines.push(" # Enable and start genesis service");
|
|
824
|
+
lines.push(" - systemctl daemon-reload");
|
|
825
|
+
lines.push(" - systemctl enable genesis.service");
|
|
826
|
+
lines.push(" - systemctl start genesis.service");
|
|
827
|
+
lines.push("");
|
|
828
|
+
if (enableSecurity) {
|
|
829
|
+
lines.push(" # Security Module 1: Activate UFW Firewall");
|
|
830
|
+
lines.push(...ufwFirewallRunCmd(DEFAULT_UFW_GENESIS_OPTIONS));
|
|
831
|
+
}
|
|
832
|
+
if (enableSecurity) {
|
|
833
|
+
lines.push(" # Security Module 2: Apply kernel hardening");
|
|
834
|
+
lines.push(...kernelHardeningRunCmd());
|
|
835
|
+
}
|
|
836
|
+
if (enableSecurity) {
|
|
837
|
+
lines.push(" # Security Module 3: Activate SSH hardening");
|
|
838
|
+
lines.push(...sshdHardeningRunCmd());
|
|
839
|
+
}
|
|
840
|
+
if (enableSecurity) {
|
|
841
|
+
lines.push(" # Security Module 4: Run security audit");
|
|
842
|
+
lines.push(...securityAuditRunCmd());
|
|
843
|
+
}
|
|
844
|
+
lines.push(" # Mark bootstrap complete");
|
|
845
|
+
lines.push(' - echo "status=complete" >> /root/.genesis-bootstrap-status');
|
|
846
|
+
lines.push(' - echo "completed_at=$(date -Iseconds)" >> /root/.genesis-bootstrap-status');
|
|
847
|
+
if (enableSecurity) {
|
|
848
|
+
lines.push(' - echo "security_hardening=applied" >> /root/.genesis-bootstrap-status');
|
|
849
|
+
}
|
|
850
|
+
lines.push("");
|
|
851
|
+
if (additionalCommands.length > 0) {
|
|
852
|
+
lines.push(" # Additional custom commands");
|
|
853
|
+
for (const cmd of additionalCommands) {
|
|
854
|
+
lines.push(` - ${cmd}`);
|
|
855
|
+
}
|
|
856
|
+
lines.push("");
|
|
857
|
+
}
|
|
858
|
+
lines.push("# =====================================================");
|
|
859
|
+
lines.push("# STAGE 6: Final (Final stage)");
|
|
860
|
+
lines.push("# =====================================================");
|
|
861
|
+
lines.push("");
|
|
862
|
+
lines.push('final_message: "Genesis server bootstrap completed after $UPTIME seconds"');
|
|
863
|
+
return lines.join(`
|
|
864
|
+
`);
|
|
865
|
+
}
|
|
866
|
+
function generateRemoteGenesisBootstrap(url) {
|
|
867
|
+
return `#include
|
|
868
|
+
${url}`;
|
|
869
|
+
}
|
|
870
|
+
var GenesisBootstrapPresets = {
|
|
871
|
+
default: (adminSSHKey) => generateGenesisBootstrap({
|
|
872
|
+
adminSSHKey
|
|
873
|
+
}),
|
|
874
|
+
arm: (adminSSHKey) => generateGenesisBootstrap({
|
|
875
|
+
adminSSHKey,
|
|
876
|
+
defaultServerType: "cax21"
|
|
877
|
+
}),
|
|
878
|
+
performance: (adminSSHKey) => generateGenesisBootstrap({
|
|
879
|
+
adminSSHKey,
|
|
880
|
+
defaultServerType: "cpx21"
|
|
881
|
+
}),
|
|
882
|
+
dedicated: (adminSSHKey) => generateGenesisBootstrap({
|
|
883
|
+
adminSSHKey,
|
|
884
|
+
defaultServerType: "ccx13"
|
|
885
|
+
}),
|
|
886
|
+
development: (adminSSHKey) => generateGenesisBootstrap({
|
|
887
|
+
adminSSHKey,
|
|
888
|
+
enableSecurity: false,
|
|
889
|
+
packages: ["htop", "vim", "tmux", "strace"],
|
|
890
|
+
additionalCommands: [
|
|
891
|
+
"echo 'Genesis development server ready' | wall"
|
|
892
|
+
]
|
|
893
|
+
}),
|
|
894
|
+
secure: (adminSSHKey) => generateGenesisBootstrap({
|
|
895
|
+
adminSSHKey,
|
|
896
|
+
packages: ["lynis"],
|
|
897
|
+
additionalCommands: [
|
|
898
|
+
"echo 'Genesis secure server ready - security audit completed' | wall"
|
|
899
|
+
]
|
|
900
|
+
})
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
// src/bootstrap/cloud-init.ts
|
|
904
|
+
function generateSeedBootstrap(options = {}) {
|
|
905
|
+
const {
|
|
906
|
+
seedRepo = "https://github.com/ebowwa/seed",
|
|
907
|
+
seedBranch = "dev",
|
|
908
|
+
seedPath = "/root/seed",
|
|
909
|
+
runSetup = true,
|
|
910
|
+
setupEnv = {},
|
|
911
|
+
packages = [],
|
|
912
|
+
additionalCommands = [],
|
|
913
|
+
enableSecurity = true
|
|
914
|
+
} = options;
|
|
915
|
+
const lines = [];
|
|
916
|
+
lines.push("#cloud-config");
|
|
917
|
+
lines.push("");
|
|
918
|
+
lines.push("# Update system packages");
|
|
919
|
+
lines.push("package_update: true");
|
|
920
|
+
lines.push("package_upgrade: true");
|
|
921
|
+
lines.push("");
|
|
922
|
+
lines.push("# Install required packages");
|
|
923
|
+
lines.push("packages:");
|
|
924
|
+
lines.push(" - git");
|
|
925
|
+
lines.push(" - curl");
|
|
926
|
+
lines.push(" - jq");
|
|
927
|
+
lines.push(" - unzip");
|
|
928
|
+
lines.push(" - tmux");
|
|
929
|
+
if (enableSecurity) {
|
|
930
|
+
lines.push(" # Security: UFW Firewall");
|
|
931
|
+
lines.push(...ufwFirewallPackages());
|
|
932
|
+
}
|
|
933
|
+
if (enableSecurity) {
|
|
934
|
+
lines.push(" # Security: Kernel hardening");
|
|
935
|
+
lines.push(...kernelHardeningPackages());
|
|
936
|
+
}
|
|
937
|
+
if (enableSecurity) {
|
|
938
|
+
lines.push(" # Security: SSH hardening");
|
|
939
|
+
lines.push(...sshdHardeningPackages());
|
|
940
|
+
}
|
|
941
|
+
if (enableSecurity) {
|
|
942
|
+
lines.push(" # Security: Security audit");
|
|
943
|
+
lines.push(...securityAuditPackages());
|
|
944
|
+
}
|
|
945
|
+
for (const pkg of packages) {
|
|
946
|
+
lines.push(` - ${pkg}`);
|
|
947
|
+
}
|
|
948
|
+
lines.push("");
|
|
949
|
+
lines.push("# Write bootstrap status file");
|
|
950
|
+
lines.push("write_files:");
|
|
951
|
+
lines.push(" - path: /root/.bootstrap-status");
|
|
952
|
+
lines.push(" owner: root:root");
|
|
953
|
+
lines.push(" permissions: '0644'");
|
|
954
|
+
lines.push(" content: |");
|
|
955
|
+
lines.push(" status=started");
|
|
956
|
+
lines.push(" started_at=$(date -Iseconds)");
|
|
957
|
+
lines.push(" source=cloud-init");
|
|
958
|
+
if (enableSecurity) {
|
|
959
|
+
lines.push(" security=enabled");
|
|
960
|
+
}
|
|
961
|
+
lines.push("");
|
|
962
|
+
lines.push(" # Add bun to /etc/environment for all users/shells");
|
|
963
|
+
lines.push(' # Format: Simple KEY="value" pairs, no variable expansion');
|
|
964
|
+
lines.push(" - path: /etc/environment");
|
|
965
|
+
lines.push(" owner: root:root");
|
|
966
|
+
lines.push(" permissions: '0644'");
|
|
967
|
+
lines.push(" content: |");
|
|
968
|
+
lines.push(' PATH="/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"');
|
|
969
|
+
lines.push("");
|
|
970
|
+
if (enableSecurity) {
|
|
971
|
+
lines.push(" # Security Module 1: UFW Firewall configuration");
|
|
972
|
+
lines.push(...ufwFirewallWriteFiles(DEFAULT_UFW_WORKER_OPTIONS));
|
|
973
|
+
}
|
|
974
|
+
if (enableSecurity) {
|
|
975
|
+
lines.push(" # Security Module 2: Kernel hardening");
|
|
976
|
+
lines.push(...kernelHardeningWriteFiles());
|
|
977
|
+
}
|
|
978
|
+
if (enableSecurity) {
|
|
979
|
+
lines.push(" # Security Module 3: SSH hardening");
|
|
980
|
+
lines.push(...sshdHardeningWriteFiles());
|
|
981
|
+
}
|
|
982
|
+
if (enableSecurity) {
|
|
983
|
+
lines.push(" # Security Module 4: Security audit");
|
|
984
|
+
lines.push(...securityAuditWriteFiles());
|
|
985
|
+
}
|
|
986
|
+
lines.push(" # Node-agent systemd service for Ralph Loop orchestration");
|
|
987
|
+
lines.push(" - path: /etc/systemd/system/node-agent.service");
|
|
988
|
+
lines.push(" owner: root:root");
|
|
989
|
+
lines.push(" permissions: '0644'");
|
|
990
|
+
lines.push(" content: |");
|
|
991
|
+
lines.push(" [Unit]");
|
|
992
|
+
lines.push(" Description=Node Agent for Ralph Loop Orchestration");
|
|
993
|
+
lines.push(" Documentation=https://github.com/ebowwa/seed");
|
|
994
|
+
lines.push(" After=network-online.target");
|
|
995
|
+
lines.push(" Wants=network-online.target");
|
|
996
|
+
lines.push("");
|
|
997
|
+
lines.push(" [Service]");
|
|
998
|
+
lines.push(" Type=simple");
|
|
999
|
+
lines.push(" User=root");
|
|
1000
|
+
lines.push(` WorkingDirectory=${seedPath}/node-agent`);
|
|
1001
|
+
lines.push(" ExecStart=/root/.bun/bin/bun run src/index.ts");
|
|
1002
|
+
lines.push(` EnvironmentFile=-${seedPath}/node-agent/.env`);
|
|
1003
|
+
lines.push(" Environment=PORT=8911");
|
|
1004
|
+
lines.push(" Restart=always");
|
|
1005
|
+
lines.push(" RestartSec=10");
|
|
1006
|
+
lines.push(" StandardOutput=journal");
|
|
1007
|
+
lines.push(" StandardError=journal");
|
|
1008
|
+
lines.push(" SyslogIdentifier=node-agent");
|
|
1009
|
+
lines.push("");
|
|
1010
|
+
if (enableSecurity) {
|
|
1011
|
+
lines.push(" # Security hardening");
|
|
1012
|
+
lines.push(" NoNewPrivileges=true");
|
|
1013
|
+
lines.push(" PrivateTmp=true");
|
|
1014
|
+
lines.push(" ProtectSystem=strict");
|
|
1015
|
+
lines.push(" ProtectHome=true");
|
|
1016
|
+
lines.push(" ReadOnlyPaths=/");
|
|
1017
|
+
lines.push(" ReadWritePaths=/var/log " + seedPath + "/node-agent");
|
|
1018
|
+
}
|
|
1019
|
+
lines.push("");
|
|
1020
|
+
lines.push(" [Install]");
|
|
1021
|
+
lines.push(" WantedBy=multi-user.target");
|
|
1022
|
+
lines.push("");
|
|
1023
|
+
lines.push("# Bootstrap commands");
|
|
1024
|
+
lines.push("runcmd:");
|
|
1025
|
+
lines.push(" # Install Bun");
|
|
1026
|
+
lines.push(" - curl -fsSL https://bun.sh/install | bash");
|
|
1027
|
+
lines.push(" - ln -sf /root/.bun/bin/bun /root/.bun/bin/node # Create 'node' symlink to bun");
|
|
1028
|
+
lines.push("");
|
|
1029
|
+
lines.push(` # Clone seed repository`);
|
|
1030
|
+
lines.push(` - git clone --depth 1 --branch ${seedBranch} ${seedRepo} ${seedPath}`);
|
|
1031
|
+
lines.push("");
|
|
1032
|
+
if (runSetup) {
|
|
1033
|
+
const envVars = ["NONINTERACTIVE=1", ...Object.entries(setupEnv).map(([k, v]) => `${k}=${v}`)];
|
|
1034
|
+
const envString = envVars.join(" ");
|
|
1035
|
+
lines.push(` # Run seed setup non-interactively`);
|
|
1036
|
+
lines.push(` - cd ${seedPath} && ${envString} bash ./setup.sh 2>&1 | tee /var/log/seed-setup.log`);
|
|
1037
|
+
lines.push("");
|
|
1038
|
+
lines.push(` # Mark setup complete`);
|
|
1039
|
+
lines.push(` - touch ${seedPath}/.seed-setup-complete`);
|
|
1040
|
+
lines.push("");
|
|
1041
|
+
}
|
|
1042
|
+
if (additionalCommands.length > 0) {
|
|
1043
|
+
lines.push(` # Additional custom commands`);
|
|
1044
|
+
for (const cmd of additionalCommands) {
|
|
1045
|
+
lines.push(` - ${cmd}`);
|
|
1046
|
+
}
|
|
1047
|
+
lines.push("");
|
|
1048
|
+
}
|
|
1049
|
+
if (enableSecurity) {
|
|
1050
|
+
lines.push(" # Security Module 1: Activate UFW Firewall");
|
|
1051
|
+
lines.push(...ufwFirewallRunCmd(DEFAULT_UFW_WORKER_OPTIONS));
|
|
1052
|
+
}
|
|
1053
|
+
if (enableSecurity) {
|
|
1054
|
+
lines.push(" # Security Module 2: Apply kernel hardening");
|
|
1055
|
+
lines.push(...kernelHardeningRunCmd());
|
|
1056
|
+
}
|
|
1057
|
+
if (enableSecurity) {
|
|
1058
|
+
lines.push(" # Security Module 3: Activate SSH hardening");
|
|
1059
|
+
lines.push(...sshdHardeningRunCmd());
|
|
1060
|
+
}
|
|
1061
|
+
if (enableSecurity) {
|
|
1062
|
+
lines.push(" # Security Module 4: Run security audit");
|
|
1063
|
+
lines.push(...securityAuditRunCmd());
|
|
1064
|
+
}
|
|
1065
|
+
lines.push(` # Mark bootstrap complete`);
|
|
1066
|
+
lines.push(` - echo "status=complete" >> /root/.bootstrap-status`);
|
|
1067
|
+
lines.push(` - echo "completed_at=$(date -Iseconds)" >> /root/.bootstrap-status`);
|
|
1068
|
+
if (enableSecurity) {
|
|
1069
|
+
lines.push(` - echo "security_hardening=applied" >> /root/.bootstrap-status`);
|
|
1070
|
+
}
|
|
1071
|
+
lines.push("");
|
|
1072
|
+
lines.push(" # Start node-agent service");
|
|
1073
|
+
lines.push(" - systemctl daemon-reload");
|
|
1074
|
+
lines.push(" - systemctl enable node-agent");
|
|
1075
|
+
lines.push(" - systemctl start node-agent");
|
|
1076
|
+
return lines.join(`
|
|
1077
|
+
`);
|
|
1078
|
+
}
|
|
1079
|
+
function generateRemoteBootstrap(url) {
|
|
1080
|
+
return `#include
|
|
1081
|
+
${url}`;
|
|
1082
|
+
}
|
|
1083
|
+
var BootstrapPresets = {
|
|
1084
|
+
default: () => generateSeedBootstrap(),
|
|
1085
|
+
secure: () => generateSeedBootstrap({
|
|
1086
|
+
setupEnv: {
|
|
1087
|
+
DEBUG: "1",
|
|
1088
|
+
VERBOSE: "1"
|
|
1089
|
+
}
|
|
1090
|
+
}),
|
|
1091
|
+
cloneOnly: () => generateSeedBootstrap({ runSetup: false }),
|
|
1092
|
+
development: () => generateSeedBootstrap({
|
|
1093
|
+
enableSecurity: false,
|
|
1094
|
+
packages: ["htop", "vim", "strace"]
|
|
1095
|
+
}),
|
|
1096
|
+
verbose: () => generateSeedBootstrap({
|
|
1097
|
+
setupEnv: {
|
|
1098
|
+
DEBUG: "1",
|
|
1099
|
+
VERBOSE: "1"
|
|
1100
|
+
}
|
|
1101
|
+
})
|
|
1102
|
+
};
|
|
1103
|
+
export {
|
|
1104
|
+
ufwFirewallWriteFiles,
|
|
1105
|
+
ufwFirewallRunCmd,
|
|
1106
|
+
ufwFirewallPackages,
|
|
1107
|
+
sshdHardeningWriteFiles,
|
|
1108
|
+
sshdHardeningRunCmd,
|
|
1109
|
+
sshdHardeningPackages,
|
|
1110
|
+
securityAuditWriteFiles,
|
|
1111
|
+
securityAuditRunCmd,
|
|
1112
|
+
securityAuditPackages,
|
|
1113
|
+
kernelHardeningWriteFiles,
|
|
1114
|
+
kernelHardeningRunCmd,
|
|
1115
|
+
kernelHardeningPackages,
|
|
1116
|
+
generateUFWFirewallForWorker,
|
|
1117
|
+
generateUFWFirewallForGenesis,
|
|
1118
|
+
generateSeedBootstrap,
|
|
1119
|
+
generateRemoteGenesisBootstrap,
|
|
1120
|
+
generateRemoteBootstrap,
|
|
1121
|
+
generateGenesisBootstrap,
|
|
1122
|
+
GenesisBootstrapPresets,
|
|
1123
|
+
DEFAULT_UFW_WORKER_OPTIONS,
|
|
1124
|
+
DEFAULT_UFW_GENESIS_OPTIONS,
|
|
1125
|
+
BootstrapPresets
|
|
1126
|
+
};
|