openship 0.6.7 → 0.6.8
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/bare-FA66N7GC.js +29 -0
- package/dist/{chunk-LHFXSC36.js → chunk-3MP6ACKM.js} +183 -87
- package/dist/{chunk-MEE6OZFP.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-PPEBIY5F.js → chunk-5I3COAFH.js} +35 -12
- package/dist/{chunk-VQM6JHJB.js → chunk-5UXHT6ED.js} +9 -9
- package/dist/{chunk-XWHWTHEL.js → chunk-6GU6JSK6.js} +21 -2
- package/dist/{chunk-QHMDCYZ3.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-RDXT4BQB.js → chunk-7YBWXEM6.js} +4 -4
- package/dist/{chunk-H4FS5BED.js → chunk-AHJYP7DG.js} +13 -11
- package/dist/{chunk-VSTFHUCV.js → chunk-BO6CY4V2.js} +109 -23
- package/dist/{chunk-J3K6XABC.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/{chunk-UWEV6XTS.js → chunk-D6HU4VUZ.js} +1 -1
- package/dist/{chunk-S6B2Z3OO.js → chunk-ENKJ7XIF.js} +21 -11
- package/dist/{chunk-BLEXTYRK.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-NRTJRNAE.js → chunk-IAMOUDHY.js} +138 -54
- package/dist/{chunk-CIJV43LH.js → chunk-MUWVC5R4.js} +161 -39
- package/dist/{chunk-ZPJ74LXE.js → chunk-NIYD7MD2.js} +197 -45
- package/dist/{chunk-FZ6QCF6B.js → chunk-NPSRGIHV.js} +2 -2
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-J2XM3QSP.js → chunk-NUT6HKK6.js} +5 -5
- package/dist/{chunk-H3WT3A6R.js → chunk-P7RCYELG.js} +1 -1
- package/dist/{chunk-FYPFEDPZ.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-DDFUESPJ.js → chunk-RCMERVBL.js} +9 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/{chunk-WU54BXFE.js → chunk-SK4FFCFR.js} +1 -1
- package/dist/{chunk-PVP4RGWT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-E2X26PXR.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-5T4UVOM3.js → chunk-TAZZZKYO.js} +1 -1
- package/dist/{chunk-GGIAIGUL.js → chunk-UAWY6X7V.js} +34 -12
- package/dist/{chunk-2ANV5ZQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-2VSW6Q6C.js → chunk-WEZFTL4V.js} +655 -62
- package/dist/{chunk-GIPJNNI5.js → chunk-X53JXRS4.js} +3 -3
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-G4DR3U2N.js → detect-BBFT5GEU.js} +4 -4
- package/dist/{docker-YOVPCIB4.js → docker-EG3QSE6I.js} +10 -10
- package/dist/{docker-edge-executor-QFXD3337.js → docker-edge-executor-E5TOTLMR.js} +7 -7
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-MXERVAMG.js → edge-preflight-QUW4BM7N.js} +13 -13
- package/dist/{ensure-container-edge-F4JUNQIK.js → ensure-container-edge-FOEFQ3PI.js} +13 -11
- package/dist/{executor-CVPAYYHR.js → executor-NTWDJ3DB.js} +6 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +158 -72
- package/dist/{local-executor-ODC6RANU.js → local-executor-OQ2GRN7K.js} +3 -3
- package/dist/{nginx-KLFF43TC.js → nginx-4U4C2XUS.js} +9 -9
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-VKWUV4GB.js → openresty-lua-LLWT5XQW.js} +7 -5
- package/dist/{privilege-7D3SSOL3.js → privilege-MNDVMDST.js} +5 -5
- package/dist/server/index.js +31643 -25553
- package/dist/server/migrations/0109_service_build_args.sql +9 -0
- package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
- package/dist/server/migrations/0111_host_port_claim.sql +381 -0
- package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
- package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
- package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
- package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
- package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
- package/dist/server/migrations/meta/_journal.json +56 -0
- package/dist/{setup-I5AJC6NJ.js → setup-IFIMPQZJ.js} +12 -12
- package/dist/{ssh-client-625VWGKP.js → ssh-client-2XWZCMGI.js} +2 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-7QD623QV.js +0 -29
- package/dist/cloud-KNNABE3O.js +0 -40
- package/dist/edge-import-CRFVSR6P.js +0 -56
- package/dist/host-channel-preflight-QTDNGLCK.js +0 -54
- package/dist/up-QXTOOVXC.js +0 -61
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
isContainerPortForwarder,
|
|
8
8
|
managedDeploymentIdFromUnit,
|
|
9
9
|
tryExec
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-D6HU4VUZ.js";
|
|
11
11
|
import {
|
|
12
12
|
shellQuote
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WEZFTL4V.js";
|
|
14
14
|
|
|
15
15
|
// ../../packages/adapters/src/system/port-listen.ts
|
|
16
|
-
var
|
|
16
|
+
var PROC_NET_TCP_FILES = ["/proc/net/tcp", "/proc/net/tcp6"];
|
|
17
17
|
var TCP_LISTEN = "0A";
|
|
18
18
|
function parseListeningPorts(procText) {
|
|
19
19
|
const ports = /* @__PURE__ */ new Set();
|
|
@@ -34,12 +34,15 @@ function parseListeningPorts(procText) {
|
|
|
34
34
|
return ports;
|
|
35
35
|
}
|
|
36
36
|
async function probePortListeningOnce(executor, port) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
const readable = [];
|
|
38
|
+
for (const file of PROC_NET_TCP_FILES) {
|
|
39
|
+
try {
|
|
40
|
+
readable.push(await executor.exec(`cat ${file} 2>/dev/null`, { timeout: 5e3 }));
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
42
43
|
}
|
|
44
|
+
if (readable.length === 0) return null;
|
|
45
|
+
return parseListeningPorts(readable.join("\n")).has(port);
|
|
43
46
|
}
|
|
44
47
|
function delay(ms) {
|
|
45
48
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -85,12 +88,14 @@ async function waitForPortFree(executor, port, opts = {}) {
|
|
|
85
88
|
|
|
86
89
|
// ../../packages/adapters/src/runtime/port-conflict.ts
|
|
87
90
|
async function resolveListenerOwner(executor, pid) {
|
|
88
|
-
const cgroup = await tryExec(executor, `cat /proc/${pid}/cgroup 2>/dev/null
|
|
91
|
+
const cgroup = (await tryExec(executor, `cat /proc/${pid}/cgroup 2>/dev/null`))?.trim();
|
|
92
|
+
if (!cgroup) return { cgroupVerified: false };
|
|
93
|
+
const evidence = { cgroupVerified: true, processCgroup: cgroup };
|
|
89
94
|
const containerId = containerIdFromCgroup(cgroup);
|
|
90
|
-
if (containerId) return { containerId };
|
|
95
|
+
if (containerId) return { ...evidence, containerId };
|
|
91
96
|
const systemdUnit = cgroup?.match(/(?:^|\/)([^/\n]+\.service)(?:$|\n|\/)/m)?.[1]?.trim();
|
|
92
97
|
if (!systemdUnit || !/^[A-Za-z0-9@._:-]+\.service$/.test(systemdUnit)) {
|
|
93
|
-
return
|
|
98
|
+
return evidence;
|
|
94
99
|
}
|
|
95
100
|
const description = await tryExec(
|
|
96
101
|
executor,
|
|
@@ -98,6 +103,7 @@ async function resolveListenerOwner(executor, pid) {
|
|
|
98
103
|
);
|
|
99
104
|
const deploymentId = managedDeploymentIdFromUnit(systemdUnit);
|
|
100
105
|
return {
|
|
106
|
+
...evidence,
|
|
101
107
|
systemdUnit,
|
|
102
108
|
systemdDescription: description?.trim() || void 0,
|
|
103
109
|
...deploymentId ? { deploymentId, isManagedDeployment: true } : { isManagedDeployment: false }
|
|
@@ -108,25 +114,104 @@ async function resolveListener(executor, port) {
|
|
|
108
114
|
executor,
|
|
109
115
|
`ss -tlnp sport = :${port} 2>/dev/null | grep LISTEN || lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null || true`
|
|
110
116
|
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
117
|
+
const listenerPid = (output) => {
|
|
118
|
+
if (!output) return null;
|
|
119
|
+
const ssMatch = output.match(/pid=(\d+)/);
|
|
120
|
+
const lsofMatch = !ssMatch ? output.match(/^\s*(\d+)\s*$/m) : null;
|
|
121
|
+
const parsed = ssMatch?.[1] ?? lsofMatch?.[1];
|
|
122
|
+
if (!parsed) return null;
|
|
123
|
+
const pid2 = Number.parseInt(parsed, 10);
|
|
124
|
+
return Number.isSafeInteger(pid2) && pid2 > 0 ? pid2 : null;
|
|
125
|
+
};
|
|
126
|
+
const pid = listenerPid(out);
|
|
127
|
+
if (pid) return { pid, occupied: true, checked: true };
|
|
128
|
+
const observedListener = Boolean(out?.trim());
|
|
129
|
+
if (observedListener) {
|
|
130
|
+
const privilegedOut = await tryExec(
|
|
131
|
+
executor,
|
|
132
|
+
`sudo -n ss -tlnp sport = :${port} 2>/dev/null | grep LISTEN || sudo -n lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null || true`,
|
|
133
|
+
{ timeout: 5e3 }
|
|
134
|
+
);
|
|
135
|
+
const privilegedPid = listenerPid(privilegedOut);
|
|
136
|
+
if (privilegedPid) return { pid: privilegedPid, occupied: true, checked: true };
|
|
116
137
|
}
|
|
117
138
|
const listening = await probePortListeningOnce(executor, port);
|
|
118
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
pid: null,
|
|
141
|
+
occupied: observedListener || listening === true,
|
|
142
|
+
checked: observedListener || listening !== null
|
|
143
|
+
};
|
|
119
144
|
}
|
|
120
|
-
|
|
145
|
+
function parseListenerPids(output) {
|
|
146
|
+
const lines = (output ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
147
|
+
if (lines.length === 0) return { observed: false, complete: true, pids: [] };
|
|
148
|
+
const pids = /* @__PURE__ */ new Set();
|
|
149
|
+
let complete = true;
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
const matches = [...line.matchAll(/pid=(\d+)/g)].map((match) => match[1]);
|
|
152
|
+
const bare = matches.length === 0 ? line.match(/^(\d+)$/)?.[1] : void 0;
|
|
153
|
+
const values = bare ? [bare] : matches;
|
|
154
|
+
if (values.length === 0) complete = false;
|
|
155
|
+
for (const value of values) {
|
|
156
|
+
const pid = Number.parseInt(value, 10);
|
|
157
|
+
if (Number.isSafeInteger(pid) && pid > 0) pids.add(pid);
|
|
158
|
+
else complete = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { observed: true, complete, pids: [...pids] };
|
|
162
|
+
}
|
|
163
|
+
async function probeListeningPortOwners(executor, port) {
|
|
164
|
+
const command = `ss -tlnp sport = :${port} 2>/dev/null | grep LISTEN || lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null || true`;
|
|
165
|
+
const ordinary = parseListenerPids(await tryExec(executor, command));
|
|
166
|
+
let evidence = ordinary;
|
|
167
|
+
if (ordinary.observed && !ordinary.complete) {
|
|
168
|
+
const privileged = parseListenerPids(
|
|
169
|
+
await tryExec(
|
|
170
|
+
executor,
|
|
171
|
+
`sudo -n ss -tlnp sport = :${port} 2>/dev/null | grep LISTEN || sudo -n lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null`,
|
|
172
|
+
{ timeout: 5e3 }
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
if (privileged.observed && privileged.complete) evidence = privileged;
|
|
176
|
+
else {
|
|
177
|
+
evidence = {
|
|
178
|
+
observed: true,
|
|
179
|
+
complete: false,
|
|
180
|
+
pids: [.../* @__PURE__ */ new Set([...ordinary.pids, ...privileged.pids])]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!evidence.observed) {
|
|
185
|
+
const listening = await probePortListeningOnce(executor, port);
|
|
186
|
+
if (listening === null) return { occupants: [], checked: false, ownershipComplete: false };
|
|
187
|
+
if (!listening) return { occupants: [], checked: true, ownershipComplete: true };
|
|
188
|
+
return { occupants: [], checked: true, ownershipComplete: false };
|
|
189
|
+
}
|
|
190
|
+
const described = await Promise.all(
|
|
191
|
+
evidence.pids.map((pid) => describeProcess(executor, pid).catch(() => null))
|
|
192
|
+
);
|
|
193
|
+
const occupants = described.filter((row) => row !== null);
|
|
194
|
+
return {
|
|
195
|
+
occupants,
|
|
196
|
+
checked: true,
|
|
197
|
+
ownershipComplete: evidence.complete && occupants.length === evidence.pids.length
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async function probeListeningPortState(executor, port) {
|
|
121
201
|
try {
|
|
122
|
-
const { pid, occupied } = await resolveListener(executor, port);
|
|
123
|
-
if (!occupied) return null;
|
|
124
|
-
if (pid === null)
|
|
125
|
-
|
|
202
|
+
const { pid, occupied, checked } = await resolveListener(executor, port);
|
|
203
|
+
if (!checked || !occupied) return { occupant: null, checked };
|
|
204
|
+
if (pid === null) {
|
|
205
|
+
return { occupant: { pid: null, command: "unknown listener" }, checked: true };
|
|
206
|
+
}
|
|
207
|
+
return { occupant: await describeProcess(executor, pid), checked: true };
|
|
126
208
|
} catch {
|
|
127
|
-
return null;
|
|
209
|
+
return { occupant: null, checked: false };
|
|
128
210
|
}
|
|
129
211
|
}
|
|
212
|
+
async function probeListeningPort(executor, port) {
|
|
213
|
+
return (await probeListeningPortState(executor, port)).occupant;
|
|
214
|
+
}
|
|
130
215
|
async function describeProcess(executor, pid) {
|
|
131
216
|
let command = `PID ${pid}`;
|
|
132
217
|
let rawCommand;
|
|
@@ -173,6 +258,7 @@ function portOccupantDetails(port, occupant, stopTarget) {
|
|
|
173
258
|
export {
|
|
174
259
|
waitForPortListening,
|
|
175
260
|
waitForPortFree,
|
|
261
|
+
probeListeningPortOwners,
|
|
176
262
|
probeListeningPort,
|
|
177
263
|
describeProcess,
|
|
178
264
|
portOccupantDetails
|
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
} from "./chunk-I3DPMTSF.js";
|
|
9
9
|
import {
|
|
10
10
|
LocalExecutor
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-T6DVUX4F.js";
|
|
12
12
|
import {
|
|
13
13
|
logEntry,
|
|
14
14
|
sq
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-P7RCYELG.js";
|
|
16
16
|
import {
|
|
17
17
|
edgeFailureReason
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-X53JXRS4.js";
|
|
19
19
|
|
|
20
20
|
// ../../packages/adapters/src/system/docker-edge-executor.ts
|
|
21
21
|
import Dockerode from "dockerode";
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
resolveProjectDir,
|
|
8
8
|
resolveStaticOutputPath,
|
|
9
9
|
runLocalBuild
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-5DJOB7IX.js";
|
|
11
11
|
import {
|
|
12
12
|
isArtifactPathRef,
|
|
13
13
|
removeManagedArtifact
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SK4FFCFR.js";
|
|
15
15
|
import {
|
|
16
16
|
BuildCancelledError,
|
|
17
17
|
BuildLogger,
|
|
@@ -24,36 +24,36 @@ import {
|
|
|
24
24
|
splitRuntimeEnv,
|
|
25
25
|
sq,
|
|
26
26
|
transferLocalDirectory
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-AHJYP7DG.js";
|
|
28
28
|
import {
|
|
29
29
|
LocalExecutor
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-T6DVUX4F.js";
|
|
31
31
|
import {
|
|
32
32
|
privilegedExecutor
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-T46BQOLY.js";
|
|
34
34
|
import {
|
|
35
35
|
HOST_STATE_DIR,
|
|
36
36
|
envOps,
|
|
37
37
|
hostRefusal,
|
|
38
38
|
opScript
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-GNOMEMNI.js";
|
|
40
40
|
import {
|
|
41
41
|
ensureOwnedDir,
|
|
42
42
|
resolveEnvironment
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-NPSRGIHV.js";
|
|
44
44
|
import {
|
|
45
45
|
sq as sq2,
|
|
46
46
|
wrapLocalBuildCommand
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-P7RCYELG.js";
|
|
48
48
|
import {
|
|
49
49
|
portOccupantDetails,
|
|
50
50
|
probeListeningPort
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-BO6CY4V2.js";
|
|
52
52
|
import {
|
|
53
53
|
managedDeploymentUnitName,
|
|
54
54
|
probeExec,
|
|
55
55
|
withReason
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-D6HU4VUZ.js";
|
|
57
57
|
import {
|
|
58
58
|
formatDuration,
|
|
59
59
|
isRemoteConnectionError,
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
refused,
|
|
74
74
|
safeErrorMessage,
|
|
75
75
|
shellQuote
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-WEZFTL4V.js";
|
|
77
77
|
|
|
78
78
|
// ../../packages/adapters/src/system/remote-journal.ts
|
|
79
79
|
var OPSH_RUN_VERSION = 1;
|
|
@@ -1608,6 +1608,16 @@ var BareRuntime = class {
|
|
|
1608
1608
|
async inContainerExecutor() {
|
|
1609
1609
|
return this.executor;
|
|
1610
1610
|
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Resolve a retained application release for an env-only refresh.
|
|
1613
|
+
* Deployment ids originate in our database, but reject path-shaped input at
|
|
1614
|
+
* this boundary so an imported/corrupt row cannot escape releases/.
|
|
1615
|
+
*/
|
|
1616
|
+
async retainedReleaseArtifact(deploymentId) {
|
|
1617
|
+
if (!deploymentId || deploymentId.includes("/") || deploymentId.includes("\\")) return null;
|
|
1618
|
+
const path = this.releaseDir(deploymentId);
|
|
1619
|
+
return await this.executor.exists(path).catch(() => false) ? path : null;
|
|
1620
|
+
}
|
|
1611
1621
|
/** Get or lazily initialise the process supervisor. */
|
|
1612
1622
|
async supervisor() {
|
|
1613
1623
|
if (this._supervisor) return this._supervisor;
|
|
@@ -17,8 +17,9 @@ import {
|
|
|
17
17
|
detectOpenRestyPaths,
|
|
18
18
|
edgeChallengeVhostConf,
|
|
19
19
|
isCloudFrontedHost,
|
|
20
|
-
luaSourceAvailable
|
|
21
|
-
|
|
20
|
+
luaSourceAvailable,
|
|
21
|
+
reloadBareOpenResty
|
|
22
|
+
} from "./chunk-7PEXM2UI.js";
|
|
22
23
|
import {
|
|
23
24
|
edgeDownExplanation,
|
|
24
25
|
explainEdgeDown,
|
|
@@ -29,17 +30,17 @@ import {
|
|
|
29
30
|
} from "./chunk-R4VDPFLS.js";
|
|
30
31
|
import {
|
|
31
32
|
EXECUTOR_DELEGATE
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-NPSRGIHV.js";
|
|
33
34
|
import {
|
|
34
35
|
logEntry,
|
|
35
36
|
sq
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-P7RCYELG.js";
|
|
37
38
|
import {
|
|
38
39
|
edgeFailureReason
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-X53JXRS4.js";
|
|
40
41
|
import {
|
|
41
42
|
probeListeningPort
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-BO6CY4V2.js";
|
|
43
44
|
import {
|
|
44
45
|
PROXY_DIRECTIVES,
|
|
45
46
|
parseProxyValue,
|
|
@@ -47,7 +48,7 @@ import {
|
|
|
47
48
|
resolveRedirectStatus,
|
|
48
49
|
safeErrorMessage,
|
|
49
50
|
sanitizeProxySettings
|
|
50
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-WEZFTL4V.js";
|
|
51
52
|
|
|
52
53
|
// ../../packages/adapters/src/infra/nginx.ts
|
|
53
54
|
import {
|
|
@@ -593,7 +594,7 @@ function acmeKeyArgs(keyType) {
|
|
|
593
594
|
return [];
|
|
594
595
|
}
|
|
595
596
|
}
|
|
596
|
-
var DOMAIN_RE = /^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$/;
|
|
597
|
+
var DOMAIN_RE = /^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$/;
|
|
597
598
|
function assertValidDomain(domain) {
|
|
598
599
|
if (!DOMAIN_RE.test(domain) || domain.length > 253) {
|
|
599
600
|
throw new Error(`Invalid domain: ${domain}`);
|
|
@@ -601,7 +602,9 @@ function assertValidDomain(domain) {
|
|
|
601
602
|
}
|
|
602
603
|
function assertNoNginxInjection(value, what) {
|
|
603
604
|
if (/[\s;{}#\\'"]/.test(value)) {
|
|
604
|
-
throw new Error(
|
|
605
|
+
throw new Error(
|
|
606
|
+
`Invalid ${what} (contains characters that could inject nginx config): ${value}`
|
|
607
|
+
);
|
|
605
608
|
}
|
|
606
609
|
}
|
|
607
610
|
function assertNoRuntimeVariable(value, what, allowCaptures = false) {
|
|
@@ -698,7 +701,9 @@ function certbotDiagnosis(output, domain) {
|
|
|
698
701
|
diagnosis = `Couldn't start the certificate challenge listener for ${domain} on port ${ACME_HTTP01_PORT}: something on this server is already holding it. That port is used only while a certificate is being issued, so this is almost always a certificate run from an earlier attempt that hasn't exited \u2014 retry, and if it persists find the holder with \`ss -ltnp | grep ${ACME_HTTP01_PORT}\` and stop it. Nothing was requested from Let's Encrypt, so this is not a DNS or routing problem.`;
|
|
699
702
|
} else if (/timeout during connect|connection refused|failed to connect|Timeout/i.test(text)) {
|
|
700
703
|
diagnosis = `Port 80 for ${domain} isn't reachable from the internet \u2014 a firewall / cloud security group is blocking it, the domain doesn't point at this server, or another proxy is still bound to :80.`;
|
|
701
|
-
} else if (/NXDOMAIN|no\s+(A|AAAA)\s+record|DNS problem|could not be resolved|no records? found/i.test(
|
|
704
|
+
} else if (/NXDOMAIN|no\s+(A|AAAA)\s+record|DNS problem|could not be resolved|no records? found/i.test(
|
|
705
|
+
text
|
|
706
|
+
)) {
|
|
702
707
|
diagnosis = `${domain} doesn't resolve to this server yet \u2014 the DNS A record is missing or hasn't propagated. Wait for propagation, then retry from the Domains tab.`;
|
|
703
708
|
} else if (/\b52[56]\b|ssl handshake failed/i.test(text)) {
|
|
704
709
|
diagnosis = `Cloudflare's proxy is answering the certificate challenge for ${domain} instead of forwarding it to this server: it redirects the request to HTTPS, then rejects the origin's TLS. Fix it one of three ways \u2014 set SSL/TLS \u2192 Overview to "Full" (not "Full (strict)") until the first certificate is issued, or turn the orange cloud off for this record until it is, or upload a Cloudflare Origin CA certificate from the Domains tab and skip Let's Encrypt entirely.`;
|
|
@@ -721,6 +726,7 @@ var NginxProvider = class {
|
|
|
721
726
|
acmeTosAgreed;
|
|
722
727
|
certDir;
|
|
723
728
|
executor;
|
|
729
|
+
paths;
|
|
724
730
|
reloadCommand;
|
|
725
731
|
pinPaths;
|
|
726
732
|
containerEdge;
|
|
@@ -744,8 +750,9 @@ var NginxProvider = class {
|
|
|
744
750
|
this.acmeTosAgreed = opts.acmeTosAgreed ?? true;
|
|
745
751
|
this.certDir = opts.certDir ?? DEFAULT_CERT_DIR;
|
|
746
752
|
this.executor = opts.executor ?? null;
|
|
753
|
+
this.paths = opts.paths;
|
|
747
754
|
this.containerEdge = opts.containerEdge ?? false;
|
|
748
|
-
this.reloadCommand = buildReloadCommand(opts.paths
|
|
755
|
+
this.reloadCommand = buildReloadCommand(opts.paths);
|
|
749
756
|
this.pinPaths = opts.pinPaths ?? false;
|
|
750
757
|
this.challengeDir = opts.challengeDir ?? EDGE_CHALLENGE_DIR;
|
|
751
758
|
}
|
|
@@ -1094,6 +1101,31 @@ eab-hmac-key = ${this.acmeEabHmacKey}
|
|
|
1094
1101
|
}
|
|
1095
1102
|
return path;
|
|
1096
1103
|
}
|
|
1104
|
+
/** Materialize generated DNS hooks on the same target where Certbot runs. */
|
|
1105
|
+
async createEphemeralDnsHooks(opts) {
|
|
1106
|
+
if (!opts?.dnsAuthHookScript) return null;
|
|
1107
|
+
const dir = join(dirname(this.certDir), `.openship-dns-${randomBytes(12).toString("hex")}`);
|
|
1108
|
+
const authPath = join(dir, "auth.sh");
|
|
1109
|
+
const cleanupPath = join(dir, "cleanup.sh");
|
|
1110
|
+
const recordPath = join(dir, "record-id.txt");
|
|
1111
|
+
await this._mkdir(dir);
|
|
1112
|
+
try {
|
|
1113
|
+
await this._chmod(dir, 448);
|
|
1114
|
+
await this._writeFile(authPath, opts.dnsAuthHookScript, 448);
|
|
1115
|
+
if (opts.dnsCleanupHookScript) {
|
|
1116
|
+
await this._writeFile(cleanupPath, opts.dnsCleanupHookScript, 448);
|
|
1117
|
+
}
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
await this._rm(dir).catch(() => void 0);
|
|
1120
|
+
throw err;
|
|
1121
|
+
}
|
|
1122
|
+
const recordEnv = `OPENSHIP_DNS_RECORD_FILE=${sq(recordPath)}`;
|
|
1123
|
+
return {
|
|
1124
|
+
dir,
|
|
1125
|
+
authCommand: `${recordEnv} ${sq(authPath)}`,
|
|
1126
|
+
cleanupCommand: opts.dnsCleanupHookScript ? `${recordEnv} ${sq(cleanupPath)}` : void 0
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1097
1129
|
async _captureFile(path) {
|
|
1098
1130
|
if (!await this._exists(path)) {
|
|
1099
1131
|
return { exists: false };
|
|
@@ -1347,7 +1379,9 @@ ${serveLocation}
|
|
|
1347
1379
|
*/
|
|
1348
1380
|
async _lsSites() {
|
|
1349
1381
|
if (!this.executor) return fsReaddir(this.sitesDir);
|
|
1350
|
-
const out = await this.executor.exec(
|
|
1382
|
+
const out = await this.executor.exec(
|
|
1383
|
+
`ls -1 ${sq(this._commandSitesDir())} 2>/dev/null || true`
|
|
1384
|
+
);
|
|
1351
1385
|
return out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1352
1386
|
}
|
|
1353
1387
|
/**
|
|
@@ -1410,8 +1444,11 @@ ${serveLocation}
|
|
|
1410
1444
|
* from the old in-memory config (an inconsistent half-state that would silently
|
|
1411
1445
|
* vanish on the next unrelated reload). The error is re-thrown for retry.
|
|
1412
1446
|
*/
|
|
1413
|
-
async removeRoute(domain) {
|
|
1447
|
+
async removeRoute(domain, opts) {
|
|
1414
1448
|
assertValidDomain(domain);
|
|
1449
|
+
if (opts?.signal?.aborted) {
|
|
1450
|
+
throw new Error(`Route removal aborted before it started: ${domain}`);
|
|
1451
|
+
}
|
|
1415
1452
|
const slug = await this.resolveSlug(domain);
|
|
1416
1453
|
const configPath = join(this.sitesDir, `${slug}.conf`);
|
|
1417
1454
|
const statePath = this.routeStatePath(slug);
|
|
@@ -1420,11 +1457,20 @@ ${serveLocation}
|
|
|
1420
1457
|
await this._rm(configPath);
|
|
1421
1458
|
await this._rm(statePath).catch(() => void 0);
|
|
1422
1459
|
try {
|
|
1423
|
-
await this.reload();
|
|
1460
|
+
await this.reload({ allowStopped: true });
|
|
1424
1461
|
} catch (err) {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1462
|
+
try {
|
|
1463
|
+
if (!opts?.signal?.aborted) await this._restoreFile(configPath, confSnapshot);
|
|
1464
|
+
if (!opts?.signal?.aborted) await this._restoreFile(statePath, stateSnapshot);
|
|
1465
|
+
if (!opts?.signal?.aborted) {
|
|
1466
|
+
await this.reload({ allowStopped: true }).catch(() => void 0);
|
|
1467
|
+
}
|
|
1468
|
+
} finally {
|
|
1469
|
+
if (opts?.signal?.aborted) {
|
|
1470
|
+
await this._rm(configPath).catch(() => void 0);
|
|
1471
|
+
await this._rm(statePath).catch(() => void 0);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1428
1474
|
throw err;
|
|
1429
1475
|
}
|
|
1430
1476
|
await this._rm(this.bootstrapCertDir(domain)).catch(() => void 0);
|
|
@@ -1531,31 +1577,47 @@ ${serveLocation}
|
|
|
1531
1577
|
const emailArgs = validEmail ? ["--email", this.acmeEmail] : ["--register-unsafely-without-email"];
|
|
1532
1578
|
let certonlyOut = "";
|
|
1533
1579
|
const eabConfig = await this.createEphemeralEabConfig();
|
|
1580
|
+
let generatedDnsHooks = null;
|
|
1534
1581
|
try {
|
|
1535
|
-
|
|
1536
|
-
|
|
1582
|
+
generatedDnsHooks = await this.createEphemeralDnsHooks(opts);
|
|
1583
|
+
const isDnsChallenge = opts?.challenge === "dns-01" || domain.startsWith("*.");
|
|
1584
|
+
const challengeArgs = isDnsChallenge ? [
|
|
1537
1585
|
"certonly",
|
|
1538
|
-
"--
|
|
1539
|
-
"--
|
|
1540
|
-
|
|
1541
|
-
"--
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1586
|
+
"--manual",
|
|
1587
|
+
"--preferred-challenges",
|
|
1588
|
+
"dns",
|
|
1589
|
+
...generatedDnsHooks?.authCommand || opts?.dnsAuthHook ? ["--manual-auth-hook", generatedDnsHooks?.authCommand ?? opts.dnsAuthHook] : [],
|
|
1590
|
+
...generatedDnsHooks?.cleanupCommand || opts?.dnsCleanupHook ? [
|
|
1591
|
+
"--manual-cleanup-hook",
|
|
1592
|
+
generatedDnsHooks?.cleanupCommand ?? opts.dnsCleanupHook
|
|
1593
|
+
] : []
|
|
1594
|
+
] : ["certonly", "--standalone", "--http-01-port", String(ACME_HTTP01_PORT)];
|
|
1595
|
+
certonlyOut = await this._execCertbot(
|
|
1596
|
+
[
|
|
1597
|
+
...eabConfig ? ["--config", eabConfig] : [],
|
|
1598
|
+
...challengeArgs,
|
|
1599
|
+
"--cert-name",
|
|
1600
|
+
domain,
|
|
1601
|
+
"-d",
|
|
1602
|
+
domain,
|
|
1603
|
+
...this.acmeDirectoryUrl ? ["--server", this.acmeDirectoryUrl] : [],
|
|
1604
|
+
...acmeKeyArgs(this.acmeKeyType),
|
|
1605
|
+
...emailArgs,
|
|
1606
|
+
...this.acmeTosAgreed ? ["--agree-tos"] : [],
|
|
1607
|
+
"--non-interactive",
|
|
1608
|
+
// Forced reissue: certbot would otherwise print "not due for renewal"
|
|
1609
|
+
// (exit 0) when a lineage exists, leaving the stale cert in place.
|
|
1610
|
+
...opts?.force ? ["--force-renewal"] : []
|
|
1611
|
+
],
|
|
1612
|
+
opts?.onLog
|
|
1613
|
+
);
|
|
1554
1614
|
} catch (err) {
|
|
1555
1615
|
const raw = this.redactAcmeSecrets(safeErrorMessage(err));
|
|
1556
1616
|
let summary = summarizeCertbotFailure(raw, domain);
|
|
1557
1617
|
if (isAcmePortBindFailure(raw) && this.executor) {
|
|
1558
|
-
const occupant = await probeListeningPort(this.executor, ACME_HTTP01_PORT).catch(
|
|
1618
|
+
const occupant = await probeListeningPort(this.executor, ACME_HTTP01_PORT).catch(
|
|
1619
|
+
() => null
|
|
1620
|
+
);
|
|
1559
1621
|
if (occupant) {
|
|
1560
1622
|
summary += ` Currently held by ${occupant.command}` + (occupant.pid !== null ? ` (pid ${occupant.pid})` : "") + (occupant.systemdUnit ? ` via ${occupant.systemdUnit}` : "") + `.`;
|
|
1561
1623
|
}
|
|
@@ -1563,6 +1625,7 @@ ${serveLocation}
|
|
|
1563
1625
|
throw new Error(summary);
|
|
1564
1626
|
} finally {
|
|
1565
1627
|
if (eabConfig) await this._rm(dirname(eabConfig)).catch(() => void 0);
|
|
1628
|
+
if (generatedDnsHooks) await this._rm(generatedDnsHooks.dir).catch(() => void 0);
|
|
1566
1629
|
}
|
|
1567
1630
|
const slug = await this.resolveSlug(domain);
|
|
1568
1631
|
const configPath = join(this.sitesDir, `${slug}.conf`);
|
|
@@ -1630,24 +1693,24 @@ ${tail}` : "")
|
|
|
1630
1693
|
/**
|
|
1631
1694
|
* Renew a TLS certificate using certbot.
|
|
1632
1695
|
*/
|
|
1633
|
-
async renewCert(domain) {
|
|
1696
|
+
async renewCert(domain, opts) {
|
|
1634
1697
|
assertValidDomain(domain);
|
|
1698
|
+
if (opts?.challenge === "dns-01" || domain.startsWith("*.")) {
|
|
1699
|
+
return this.provisionCert(domain, { ...opts, challenge: "dns-01", force: true });
|
|
1700
|
+
}
|
|
1635
1701
|
if (!await this.certsExist(domain)) {
|
|
1636
|
-
return this.provisionCert(domain);
|
|
1702
|
+
return this.provisionCert(domain, opts);
|
|
1637
1703
|
}
|
|
1638
1704
|
if (!await this.hasCertbotLineage(domain)) {
|
|
1639
|
-
return this.provisionCert(domain, { force: true });
|
|
1705
|
+
return this.provisionCert(domain, { ...opts, force: true });
|
|
1640
1706
|
}
|
|
1641
1707
|
if (!await this.lineageServerMatches(domain)) {
|
|
1642
|
-
return this.provisionCert(domain, { force: true });
|
|
1708
|
+
return this.provisionCert(domain, { ...opts, force: true });
|
|
1643
1709
|
}
|
|
1644
|
-
await this._execCertbot(
|
|
1645
|
-
"renew",
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
...acmeKeyArgs(this.acmeKeyType),
|
|
1649
|
-
"--non-interactive"
|
|
1650
|
-
]);
|
|
1710
|
+
await this._execCertbot(
|
|
1711
|
+
["renew", "--cert-name", domain, ...acmeKeyArgs(this.acmeKeyType), "--non-interactive"],
|
|
1712
|
+
opts?.onLog
|
|
1713
|
+
);
|
|
1651
1714
|
await this.reload();
|
|
1652
1715
|
return this.readCertInfo(domain);
|
|
1653
1716
|
}
|
|
@@ -1805,23 +1868,42 @@ ${tail}` : "")
|
|
|
1805
1868
|
* config may move). Re-detecting on every reload keeps the provider
|
|
1806
1869
|
* in sync without requiring an API restart.
|
|
1807
1870
|
*/
|
|
1808
|
-
async reload() {
|
|
1871
|
+
async reload(opts = {}) {
|
|
1809
1872
|
if (this.executor) {
|
|
1810
1873
|
if (!this.pinPaths) {
|
|
1811
1874
|
try {
|
|
1812
1875
|
const freshPaths = await detectOpenRestyPaths(this.executor);
|
|
1876
|
+
this.paths = freshPaths;
|
|
1813
1877
|
this.sitesDir = freshPaths.sitesDir;
|
|
1814
1878
|
this.nginxVersion = freshPaths.nginxVersion ?? this.nginxVersion;
|
|
1815
|
-
this.reloadCommand = buildReloadCommand(freshPaths
|
|
1816
|
-
containerEdge: this.containerEdge
|
|
1817
|
-
});
|
|
1879
|
+
this.reloadCommand = buildReloadCommand(freshPaths);
|
|
1818
1880
|
} catch {
|
|
1819
1881
|
}
|
|
1820
1882
|
}
|
|
1821
|
-
|
|
1883
|
+
if (this.containerEdge) {
|
|
1884
|
+
await this.executor.exec(this.reloadCommand, { timeout: 15e3 });
|
|
1885
|
+
} else {
|
|
1886
|
+
await reloadBareOpenResty(this.executor, this.paths, {
|
|
1887
|
+
allowStopped: opts.allowStopped ?? false
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1822
1890
|
return;
|
|
1823
1891
|
}
|
|
1824
|
-
|
|
1892
|
+
if (this.containerEdge) {
|
|
1893
|
+
await execFileAsync("sh", ["-lc", this.reloadCommand], { timeout: 15e3 });
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
const localExecutor = {
|
|
1897
|
+
exec: async (command, execOpts) => {
|
|
1898
|
+
const { stdout } = await execFileAsync("sh", ["-lc", command], {
|
|
1899
|
+
timeout: execOpts?.timeout
|
|
1900
|
+
});
|
|
1901
|
+
return String(stdout);
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
await reloadBareOpenResty(localExecutor, this.paths, {
|
|
1905
|
+
allowStopped: opts.allowStopped ?? false
|
|
1906
|
+
});
|
|
1825
1907
|
}
|
|
1826
1908
|
async certsExist(domain) {
|
|
1827
1909
|
const certPath = join(this.certDir, domain, "fullchain.pem");
|
|
@@ -2003,7 +2085,9 @@ ${geoEntries.join("\n")}
|
|
|
2003
2085
|
if (!foundDesiredInclude) {
|
|
2004
2086
|
const httpIndex = nextLines.findIndex((line) => /^\s*http\s*\{\s*$/.test(line));
|
|
2005
2087
|
if (httpIndex === -1) {
|
|
2006
|
-
throw new Error(
|
|
2088
|
+
throw new Error(
|
|
2089
|
+
`Failed to ensure rate-limit include: ${confPath} is missing an http block`
|
|
2090
|
+
);
|
|
2007
2091
|
}
|
|
2008
2092
|
const indent = nextLines[httpIndex].match(/^\s*/)?.[0] ?? "";
|
|
2009
2093
|
nextLines.splice(httpIndex + 1, 0, `${indent} ${desiredIncludeLine}`);
|