@ebowwa/hetzner 0.2.2 → 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
package/ssh-keys.js
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Hetzner SSH key operations
|
|
4
|
-
*/
|
|
5
|
-
var __assign = (this && this.__assign) || function () {
|
|
6
|
-
__assign = Object.assign || function(t) {
|
|
7
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
8
|
-
s = arguments[i];
|
|
9
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
10
|
-
t[p] = s[p];
|
|
11
|
-
}
|
|
12
|
-
return t;
|
|
13
|
-
};
|
|
14
|
-
return __assign.apply(this, arguments);
|
|
15
|
-
};
|
|
16
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
18
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
19
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
20
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
21
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
22
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
26
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
27
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
28
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
29
|
-
function step(op) {
|
|
30
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
31
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
32
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
33
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
34
|
-
switch (op[0]) {
|
|
35
|
-
case 0: case 1: t = op; break;
|
|
36
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
37
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
38
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
39
|
-
default:
|
|
40
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
41
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
42
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
43
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
44
|
-
if (t[2]) _.ops.pop();
|
|
45
|
-
_.trys.pop(); continue;
|
|
46
|
-
}
|
|
47
|
-
op = body.call(thisArg, _);
|
|
48
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
49
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
-
exports.SSHKeyOperations = void 0;
|
|
54
|
-
var schemas_js_1 = require("./schemas.js");
|
|
55
|
-
var SSHKeyOperations = /** @class */ (function () {
|
|
56
|
-
function SSHKeyOperations(client) {
|
|
57
|
-
this.client = client;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* List all SSH keys
|
|
61
|
-
*/
|
|
62
|
-
SSHKeyOperations.prototype.list = function () {
|
|
63
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
64
|
-
var response, validated;
|
|
65
|
-
return __generator(this, function (_a) {
|
|
66
|
-
switch (_a.label) {
|
|
67
|
-
case 0: return [4 /*yield*/, this.client.request("/ssh_keys")];
|
|
68
|
-
case 1:
|
|
69
|
-
response = _a.sent();
|
|
70
|
-
validated = schemas_js_1.HetznerListSSHKeysResponseSchema.safeParse(response);
|
|
71
|
-
if (!validated.success) {
|
|
72
|
-
console.warn('Hetzner list SSH keys validation warning:', validated.error.issues);
|
|
73
|
-
return [2 /*return*/, response.ssh_keys]; // Return unvalidated data for backward compatibility
|
|
74
|
-
}
|
|
75
|
-
return [2 /*return*/, validated.data.ssh_keys];
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
/**
|
|
81
|
-
* Get a specific SSH key by ID or name
|
|
82
|
-
*/
|
|
83
|
-
SSHKeyOperations.prototype.get = function (idOrName) {
|
|
84
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
85
|
-
var endpoint, response, validated;
|
|
86
|
-
return __generator(this, function (_a) {
|
|
87
|
-
switch (_a.label) {
|
|
88
|
-
case 0:
|
|
89
|
-
endpoint = typeof idOrName === 'number'
|
|
90
|
-
? "/ssh_keys/".concat(idOrName)
|
|
91
|
-
: "/ssh_keys?name=".concat(encodeURIComponent(idOrName));
|
|
92
|
-
return [4 /*yield*/, this.client.request(endpoint)];
|
|
93
|
-
case 1:
|
|
94
|
-
response = _a.sent();
|
|
95
|
-
validated = schemas_js_1.HetznerGetSSHKeyResponseSchema.safeParse(response);
|
|
96
|
-
if (!validated.success) {
|
|
97
|
-
console.warn('Hetzner get SSH key validation warning:', validated.error.issues);
|
|
98
|
-
return [2 /*return*/, response.ssh_key]; // Return unvalidated data for backward compatibility
|
|
99
|
-
}
|
|
100
|
-
return [2 /*return*/, validated.data.ssh_key];
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Create a new SSH key
|
|
107
|
-
*
|
|
108
|
-
* @param options - SSH key creation options
|
|
109
|
-
* @returns Created SSH key
|
|
110
|
-
*/
|
|
111
|
-
SSHKeyOperations.prototype.create = function (options) {
|
|
112
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
113
|
-
var validatedOptions, body, response, validated;
|
|
114
|
-
return __generator(this, function (_a) {
|
|
115
|
-
switch (_a.label) {
|
|
116
|
-
case 0:
|
|
117
|
-
validatedOptions = schemas_js_1.HetznerCreateSSHKeyRequestSchema.safeParse(options);
|
|
118
|
-
if (!validatedOptions.success) {
|
|
119
|
-
throw new Error("Invalid SSH key options: ".concat(validatedOptions.error.issues.map(function (i) { return i.message; }).join(', ')));
|
|
120
|
-
}
|
|
121
|
-
body = __assign({ name: validatedOptions.data.name, public_key: validatedOptions.data.public_key }, (validatedOptions.data.labels && { labels: validatedOptions.data.labels }));
|
|
122
|
-
return [4 /*yield*/, this.client.request("/ssh_keys", {
|
|
123
|
-
method: "POST",
|
|
124
|
-
body: JSON.stringify(body),
|
|
125
|
-
})];
|
|
126
|
-
case 1:
|
|
127
|
-
response = _a.sent();
|
|
128
|
-
validated = schemas_js_1.HetznerCreateSSHKeyResponseSchema.safeParse(response);
|
|
129
|
-
if (!validated.success) {
|
|
130
|
-
console.warn('Hetzner create SSH key validation warning:', validated.error.issues);
|
|
131
|
-
return [2 /*return*/, response.ssh_key]; // Return unvalidated data for backward compatibility
|
|
132
|
-
}
|
|
133
|
-
return [2 /*return*/, validated.data.ssh_key];
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
};
|
|
138
|
-
/**
|
|
139
|
-
* Delete an SSH key
|
|
140
|
-
*
|
|
141
|
-
* @param id - SSH key ID
|
|
142
|
-
*/
|
|
143
|
-
SSHKeyOperations.prototype.delete = function (id) {
|
|
144
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
145
|
-
return __generator(this, function (_a) {
|
|
146
|
-
switch (_a.label) {
|
|
147
|
-
case 0: return [4 /*yield*/, this.client.request("/ssh_keys/".concat(id), { method: "DELETE" })];
|
|
148
|
-
case 1:
|
|
149
|
-
_a.sent();
|
|
150
|
-
return [2 /*return*/];
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Find an SSH key by name
|
|
157
|
-
* Returns undefined if not found
|
|
158
|
-
*/
|
|
159
|
-
SSHKeyOperations.prototype.findByName = function (name) {
|
|
160
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
161
|
-
var keys, _a;
|
|
162
|
-
return __generator(this, function (_b) {
|
|
163
|
-
switch (_b.label) {
|
|
164
|
-
case 0:
|
|
165
|
-
_b.trys.push([0, 2, , 3]);
|
|
166
|
-
return [4 /*yield*/, this.list()];
|
|
167
|
-
case 1:
|
|
168
|
-
keys = _b.sent();
|
|
169
|
-
return [2 /*return*/, keys.find(function (key) { return key.name === name; })];
|
|
170
|
-
case 2:
|
|
171
|
-
_a = _b.sent();
|
|
172
|
-
return [2 /*return*/, undefined];
|
|
173
|
-
case 3: return [2 /*return*/];
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
return SSHKeyOperations;
|
|
179
|
-
}());
|
|
180
|
-
exports.SSHKeyOperations = SSHKeyOperations;
|
package/ssh-keys.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hetzner SSH key operations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import type {
|
|
7
|
-
HetznerSSHKey,
|
|
8
|
-
CreateSSHKeyOptions,
|
|
9
|
-
} from "./types.js";
|
|
10
|
-
import type { HetznerClient } from "./client.js";
|
|
11
|
-
import {
|
|
12
|
-
HetznerListSSHKeysResponseSchema,
|
|
13
|
-
HetznerGetSSHKeyResponseSchema,
|
|
14
|
-
HetznerCreateSSHKeyRequestSchema,
|
|
15
|
-
HetznerCreateSSHKeyResponseSchema,
|
|
16
|
-
} from "./schemas.js";
|
|
17
|
-
|
|
18
|
-
export class SSHKeyOperations {
|
|
19
|
-
constructor(private client: HetznerClient) {}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* List all SSH keys
|
|
23
|
-
*/
|
|
24
|
-
async list(): Promise<HetznerSSHKey[]> {
|
|
25
|
-
const response = await this.client.request<{ ssh_keys: HetznerSSHKey[] }>(
|
|
26
|
-
"/ssh_keys",
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// Validate response with Zod
|
|
30
|
-
const validated = HetznerListSSHKeysResponseSchema.safeParse(response);
|
|
31
|
-
if (!validated.success) {
|
|
32
|
-
console.warn('Hetzner list SSH keys validation warning:', validated.error.issues);
|
|
33
|
-
return response.ssh_keys; // Return unvalidated data for backward compatibility
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return validated.data.ssh_keys;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get a specific SSH key by ID or name
|
|
41
|
-
*/
|
|
42
|
-
async get(idOrName: number | string): Promise<HetznerSSHKey> {
|
|
43
|
-
const endpoint = typeof idOrName === 'number'
|
|
44
|
-
? `/ssh_keys/${idOrName}`
|
|
45
|
-
: `/ssh_keys?name=${encodeURIComponent(idOrName)}`;
|
|
46
|
-
|
|
47
|
-
const response = await this.client.request<{ ssh_key: HetznerSSHKey }>(
|
|
48
|
-
endpoint,
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Validate response with Zod
|
|
52
|
-
const validated = HetznerGetSSHKeyResponseSchema.safeParse(response);
|
|
53
|
-
if (!validated.success) {
|
|
54
|
-
console.warn('Hetzner get SSH key validation warning:', validated.error.issues);
|
|
55
|
-
return response.ssh_key; // Return unvalidated data for backward compatibility
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return validated.data.ssh_key;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Create a new SSH key
|
|
63
|
-
*
|
|
64
|
-
* @param options - SSH key creation options
|
|
65
|
-
* @returns Created SSH key
|
|
66
|
-
*/
|
|
67
|
-
async create(options: CreateSSHKeyOptions): Promise<HetznerSSHKey> {
|
|
68
|
-
// Validate input with Zod
|
|
69
|
-
const validatedOptions = HetznerCreateSSHKeyRequestSchema.safeParse(options);
|
|
70
|
-
if (!validatedOptions.success) {
|
|
71
|
-
throw new Error(`Invalid SSH key options: ${validatedOptions.error.issues.map(i => i.message).join(', ')}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const body = {
|
|
75
|
-
name: validatedOptions.data.name,
|
|
76
|
-
public_key: validatedOptions.data.public_key,
|
|
77
|
-
...(validatedOptions.data.labels && { labels: validatedOptions.data.labels }),
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const response = await this.client.request<{ ssh_key: HetznerSSHKey }>(
|
|
81
|
-
"/ssh_keys",
|
|
82
|
-
{
|
|
83
|
-
method: "POST",
|
|
84
|
-
body: JSON.stringify(body),
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// Validate response with Zod
|
|
89
|
-
const validated = HetznerCreateSSHKeyResponseSchema.safeParse(response);
|
|
90
|
-
if (!validated.success) {
|
|
91
|
-
console.warn('Hetzner create SSH key validation warning:', validated.error.issues);
|
|
92
|
-
return response.ssh_key; // Return unvalidated data for backward compatibility
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return validated.data.ssh_key;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Delete an SSH key
|
|
100
|
-
*
|
|
101
|
-
* @param id - SSH key ID
|
|
102
|
-
*/
|
|
103
|
-
async delete(id: number): Promise<void> {
|
|
104
|
-
await this.client.request(
|
|
105
|
-
`/ssh_keys/${id}`,
|
|
106
|
-
{ method: "DELETE" }
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Find an SSH key by name
|
|
112
|
-
* Returns undefined if not found
|
|
113
|
-
*/
|
|
114
|
-
async findByName(name: string): Promise<HetznerSSHKey | undefined> {
|
|
115
|
-
try {
|
|
116
|
-
const keys = await this.list();
|
|
117
|
-
return keys.find(key => key.name === name);
|
|
118
|
-
} catch {
|
|
119
|
-
return undefined;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
package/ssh-setup.js
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* SSH Key Management for Hetzner
|
|
4
|
-
*
|
|
5
|
-
* This module ensures SSH keys are properly configured between:
|
|
6
|
-
* 1. Local machine (~/.ssh/)
|
|
7
|
-
* 2. Hetzner Cloud API
|
|
8
|
-
*
|
|
9
|
-
* PROBLEM:
|
|
10
|
-
* - Creating random keys in Hetzner doesn't work because we need the matching private key locally
|
|
11
|
-
* - Password auth is unreliable and often disabled
|
|
12
|
-
* - IP reuse causes known_hosts conflicts
|
|
13
|
-
*
|
|
14
|
-
* SOLUTION:
|
|
15
|
-
* - Always use existing local keys or create new key pairs
|
|
16
|
-
* - Upload public key to Hetzner, keep private key local
|
|
17
|
-
*/
|
|
18
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
19
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
28
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
29
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
30
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
31
|
-
function step(op) {
|
|
32
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
33
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
34
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
35
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
36
|
-
switch (op[0]) {
|
|
37
|
-
case 0: case 1: t = op; break;
|
|
38
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
39
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
40
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
41
|
-
default:
|
|
42
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
43
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
44
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
45
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
46
|
-
if (t[2]) _.ops.pop();
|
|
47
|
-
_.trys.pop(); continue;
|
|
48
|
-
}
|
|
49
|
-
op = body.call(thisArg, _);
|
|
50
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
51
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
55
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
56
|
-
if (ar || !(i in from)) {
|
|
57
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
58
|
-
ar[i] = from[i];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
62
|
-
};
|
|
63
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
-
exports.getOrCreateHetznerSSHKey = getOrCreateHetznerSSHKey;
|
|
65
|
-
exports.ensureHetznerSSHKey = ensureHetznerSSHKey;
|
|
66
|
-
exports.clearKnownHosts = clearKnownHosts;
|
|
67
|
-
exports.testSSHConnection = testSSHConnection;
|
|
68
|
-
exports.prepareSSHKeys = prepareSSHKeys;
|
|
69
|
-
var fs_1 = require("fs");
|
|
70
|
-
var path_1 = require("path");
|
|
71
|
-
var ssh_1 = require("@ebowwa/ssh");
|
|
72
|
-
var HETZNER_SSH_DIR = (0, path_1.join)(process.env.HOME || "", ".ssh");
|
|
73
|
-
var HETZNER_KEY_PREFIX = "hetzner-codespaces";
|
|
74
|
-
/**
|
|
75
|
-
* Get or create a local SSH key pair for Hetzner
|
|
76
|
-
* Returns the key name to use with Hetzner API
|
|
77
|
-
*/
|
|
78
|
-
function getOrCreateHetznerSSHKey() {
|
|
79
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
80
|
-
var existingKey;
|
|
81
|
-
return __generator(this, function (_a) {
|
|
82
|
-
existingKey = findExistingHetznerKey();
|
|
83
|
-
if (existingKey) {
|
|
84
|
-
console.log("\u2713 Using existing SSH key: ".concat(existingKey.name));
|
|
85
|
-
return [2 /*return*/, existingKey];
|
|
86
|
-
}
|
|
87
|
-
// Create new key pair
|
|
88
|
-
console.log("Creating new SSH key pair for Hetzner...");
|
|
89
|
-
return [2 /*return*/, createNewKeyPair()];
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Find existing Hetzner SSH key in local ~/.ssh/
|
|
95
|
-
*/
|
|
96
|
-
function findExistingHetznerKey() {
|
|
97
|
-
var privateKeyPath = (0, path_1.join)(HETZNER_SSH_DIR, "".concat(HETZNER_KEY_PREFIX));
|
|
98
|
-
var publicKeyPath = "".concat(privateKeyPath, ".pub");
|
|
99
|
-
if (!(0, fs_1.existsSync)(privateKeyPath) || !(0, fs_1.existsSync)(publicKeyPath)) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
var publicKey = (0, fs_1.readFileSync)(publicKeyPath, "utf-8").trim();
|
|
103
|
-
return {
|
|
104
|
-
name: HETZNER_KEY_PREFIX,
|
|
105
|
-
publicKey: publicKey,
|
|
106
|
-
privateKeyPath: privateKeyPath,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Create a new SSH key pair for Hetzner
|
|
111
|
-
*/
|
|
112
|
-
function createNewKeyPair() {
|
|
113
|
-
var keyName = "".concat(HETZNER_KEY_PREFIX, "-").concat(Date.now());
|
|
114
|
-
var privateKeyPath = (0, path_1.join)(HETZNER_SSH_DIR, keyName);
|
|
115
|
-
var publicKeyPath = "".concat(privateKeyPath, ".pub");
|
|
116
|
-
// Generate new ed25519 key pair
|
|
117
|
-
try {
|
|
118
|
-
// Bun.spawn automatically escapes arguments to prevent shell injection
|
|
119
|
-
Bun.spawnSync(["ssh-keygen", "-t", "ed25519", "-f", privateKeyPath, "-N", "", "-C", keyName], {
|
|
120
|
-
stdout: "ignore",
|
|
121
|
-
stderr: "ignore",
|
|
122
|
-
});
|
|
123
|
-
// Set proper permissions
|
|
124
|
-
Bun.spawnSync(["chmod", "600", privateKeyPath], { stdout: "ignore", stderr: "ignore" });
|
|
125
|
-
Bun.spawnSync(["chmod", "644", publicKeyPath], { stdout: "ignore", stderr: "ignore" });
|
|
126
|
-
var publicKey = (0, fs_1.readFileSync)(publicKeyPath, "utf-8").trim();
|
|
127
|
-
console.log("\u2713 Created new SSH key: ".concat(keyName));
|
|
128
|
-
console.log(" Private: ".concat(privateKeyPath));
|
|
129
|
-
console.log(" Public: ".concat(publicKeyPath));
|
|
130
|
-
return {
|
|
131
|
-
name: keyName,
|
|
132
|
-
publicKey: publicKey,
|
|
133
|
-
privateKeyPath: privateKeyPath,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
throw new Error("Failed to create SSH key: ".concat(error));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Ensure SSH key exists in Hetzner
|
|
142
|
-
* Returns the SSH key name to use when creating servers
|
|
143
|
-
*/
|
|
144
|
-
function ensureHetznerSSHKey(hetznerAPI) {
|
|
145
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
146
|
-
var localKey, existingKeys, matchingKey, createResponse, error, createdKey;
|
|
147
|
-
return __generator(this, function (_a) {
|
|
148
|
-
switch (_a.label) {
|
|
149
|
-
case 0: return [4 /*yield*/, getOrCreateHetznerSSHKey()];
|
|
150
|
-
case 1:
|
|
151
|
-
localKey = _a.sent();
|
|
152
|
-
return [4 /*yield*/, hetznerAPI("/ssh_keys", "GET", null)
|
|
153
|
-
.then(function (r) { return r.json(); })
|
|
154
|
-
.then(function (data) { return data.ssh_keys; })];
|
|
155
|
-
case 2:
|
|
156
|
-
existingKeys = _a.sent();
|
|
157
|
-
matchingKey = existingKeys.find(function (k) { return k.public_key.trim() === localKey.publicKey.trim(); });
|
|
158
|
-
if (matchingKey) {
|
|
159
|
-
console.log("\u2713 SSH key already exists in Hetzner: ".concat(matchingKey.name, " (").concat(matchingKey.id, ")"));
|
|
160
|
-
return [2 /*return*/, matchingKey.name]; // Use existing key name
|
|
161
|
-
}
|
|
162
|
-
// 4. Upload new key to Hetzner
|
|
163
|
-
console.log("Uploading SSH key to Hetzner: ".concat(localKey.name, "..."));
|
|
164
|
-
return [4 /*yield*/, hetznerAPI("/ssh_keys", "POST", {
|
|
165
|
-
name: localKey.name,
|
|
166
|
-
public_key: localKey.publicKey,
|
|
167
|
-
})];
|
|
168
|
-
case 3:
|
|
169
|
-
createResponse = _a.sent();
|
|
170
|
-
if (!!createResponse.ok) return [3 /*break*/, 5];
|
|
171
|
-
return [4 /*yield*/, createResponse.text()];
|
|
172
|
-
case 4:
|
|
173
|
-
error = _a.sent();
|
|
174
|
-
throw new Error("Failed to upload SSH key to Hetzner: ".concat(error));
|
|
175
|
-
case 5: return [4 /*yield*/, createResponse.json()];
|
|
176
|
-
case 6:
|
|
177
|
-
createdKey = (_a.sent());
|
|
178
|
-
console.log("\u2713 SSH key uploaded: ".concat(createdKey.name, " (").concat(createdKey.id, ")"));
|
|
179
|
-
return [2 /*return*/, createdKey.name];
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Clear known_hosts entry for an IP (to fix IP reuse issues)
|
|
186
|
-
*/
|
|
187
|
-
function clearKnownHosts(ip) {
|
|
188
|
-
try {
|
|
189
|
-
// Bun.spawn automatically escapes arguments to prevent shell injection
|
|
190
|
-
Bun.spawnSync(["ssh-keygen", "-R", ip], { stdout: "ignore", stderr: "ignore" });
|
|
191
|
-
console.log("\u2713 Cleared known_hosts entry for ".concat(ip));
|
|
192
|
-
}
|
|
193
|
-
catch (_a) {
|
|
194
|
-
// Ignore if entry doesn't exist
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Test SSH connection to a server using typed flags
|
|
199
|
-
*/
|
|
200
|
-
function testSSHConnection(ip_1, privateKeyPath_1) {
|
|
201
|
-
return __awaiter(this, arguments, void 0, function (ip, privateKeyPath, username) {
|
|
202
|
-
var flags, sshArgs, proc_1, timeout, _a;
|
|
203
|
-
if (username === void 0) { username = "root"; }
|
|
204
|
-
return __generator(this, function (_b) {
|
|
205
|
-
switch (_b.label) {
|
|
206
|
-
case 0:
|
|
207
|
-
_b.trys.push([0, 2, , 3]);
|
|
208
|
-
flags = __spreadArray(__spreadArray([], ssh_1.SSHPresets.default, true), [
|
|
209
|
-
ssh_1.SSHFlags.identity(privateKeyPath),
|
|
210
|
-
(0, ssh_1.sshConfig)("ConnectTimeout", "10"),
|
|
211
|
-
], false);
|
|
212
|
-
sshArgs = (0, ssh_1.buildSSHArgs)(flags, ip, username);
|
|
213
|
-
sshArgs.push("echo", "connected");
|
|
214
|
-
proc_1 = Bun.spawn(sshArgs, {
|
|
215
|
-
stdout: "ignore",
|
|
216
|
-
stderr: "ignore",
|
|
217
|
-
});
|
|
218
|
-
timeout = setTimeout(function () { return proc_1.kill(); }, 15000);
|
|
219
|
-
return [4 /*yield*/, proc_1.exited];
|
|
220
|
-
case 1:
|
|
221
|
-
_b.sent();
|
|
222
|
-
clearTimeout(timeout);
|
|
223
|
-
return [2 /*return*/, proc_1.exitCode === 0];
|
|
224
|
-
case 2:
|
|
225
|
-
_a = _b.sent();
|
|
226
|
-
return [2 /*return*/, false];
|
|
227
|
-
case 3: return [2 /*return*/];
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Full workflow: Prepare SSH keys before creating servers
|
|
234
|
-
*/
|
|
235
|
-
function prepareSSHKeys(hetznerAPI) {
|
|
236
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
237
|
-
var sshKeyName, localKey;
|
|
238
|
-
return __generator(this, function (_a) {
|
|
239
|
-
switch (_a.label) {
|
|
240
|
-
case 0: return [4 /*yield*/, ensureHetznerSSHKey(hetznerAPI)];
|
|
241
|
-
case 1:
|
|
242
|
-
sshKeyName = _a.sent();
|
|
243
|
-
return [4 /*yield*/, getOrCreateHetznerSSHKey()];
|
|
244
|
-
case 2:
|
|
245
|
-
localKey = _a.sent();
|
|
246
|
-
return [2 /*return*/, {
|
|
247
|
-
sshKeyName: sshKeyName,
|
|
248
|
-
privateKeyPath: localKey.privateKeyPath,
|
|
249
|
-
}];
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
}
|