@lamalibre/create-lamaste 4.0.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/LICENSE.md +118 -0
- package/README.md +114 -0
- package/bin/create-lamaste.js +15 -0
- package/package.json +54 -0
- package/scripts/bundle-vendor.js +110 -0
- package/scripts/lamaste-cert-info +62 -0
- package/scripts/lamaste-certbot +96 -0
- package/scripts/lamaste-priv +693 -0
- package/src/index.js +646 -0
- package/src/lib/accounts.js +86 -0
- package/src/lib/binaries.js +137 -0
- package/src/lib/cert-help-page.js +160 -0
- package/src/lib/env.js +132 -0
- package/src/lib/http-redirect.js +38 -0
- package/src/lib/ownership.js +231 -0
- package/src/lib/relay-services.js +103 -0
- package/src/lib/secrets.js +19 -0
- package/src/lib/service-config.js +311 -0
- package/src/lib/summary.js +131 -0
- package/src/lib/wrappers.js +63 -0
- package/src/tasks/gatekeeper.js +233 -0
- package/src/tasks/harden.js +293 -0
- package/src/tasks/mtls.js +193 -0
- package/src/tasks/nginx.js +265 -0
- package/src/tasks/node.js +110 -0
- package/src/tasks/panel.js +408 -0
- package/src/tasks/redeploy.js +438 -0
- package/vendor/docs/00-introduction/how-it-works.md +447 -0
- package/vendor/docs/00-introduction/quickstart.md +472 -0
- package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
- package/vendor/docs/01-concepts/authentication.md +464 -0
- package/vendor/docs/01-concepts/authorization.md +242 -0
- package/vendor/docs/01-concepts/certificates.md +452 -0
- package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
- package/vendor/docs/01-concepts/mtls.md +490 -0
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
- package/vendor/docs/01-concepts/security-model.md +600 -0
- package/vendor/docs/01-concepts/tickets.md +414 -0
- package/vendor/docs/01-concepts/tunneling.md +367 -0
- package/vendor/docs/02-guides/agent-setup.md +194 -0
- package/vendor/docs/02-guides/certificate-management.md +341 -0
- package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
- package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
- package/vendor/docs/02-guides/disaster-recovery.md +464 -0
- package/vendor/docs/02-guides/first-tunnel.md +230 -0
- package/vendor/docs/02-guides/installation.md +393 -0
- package/vendor/docs/02-guides/managing-users.md +255 -0
- package/vendor/docs/02-guides/onboarding.md +244 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
- package/vendor/docs/02-guides/static-sites.md +364 -0
- package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
- package/vendor/docs/03-architecture/installer-flow.md +151 -0
- package/vendor/docs/03-architecture/installer.md +445 -0
- package/vendor/docs/03-architecture/management-flow.md +288 -0
- package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
- package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
- package/vendor/docs/03-architecture/overview.md +541 -0
- package/vendor/docs/03-architecture/panel-client.md +416 -0
- package/vendor/docs/03-architecture/panel-server.md +695 -0
- package/vendor/docs/03-architecture/state-management.md +529 -0
- package/vendor/docs/03-architecture/system-overview.md +167 -0
- package/vendor/docs/04-api-reference/certificates.md +1141 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
- package/vendor/docs/04-api-reference/onboarding.md +473 -0
- package/vendor/docs/04-api-reference/overview.md +404 -0
- package/vendor/docs/04-api-reference/services.md +267 -0
- package/vendor/docs/04-api-reference/settings.md +343 -0
- package/vendor/docs/04-api-reference/sites.md +716 -0
- package/vendor/docs/04-api-reference/system.md +268 -0
- package/vendor/docs/04-api-reference/tickets.md +618 -0
- package/vendor/docs/04-api-reference/tunnels.md +417 -0
- package/vendor/docs/04-api-reference/users.md +333 -0
- package/vendor/docs/05-operations/backup-and-restore.md +342 -0
- package/vendor/docs/05-operations/monitoring.md +285 -0
- package/vendor/docs/05-operations/uninstalling.md +344 -0
- package/vendor/docs/05-operations/upgrades.md +270 -0
- package/vendor/docs/06-reference/config-files.md +1080 -0
- package/vendor/docs/06-reference/glossary.md +528 -0
- package/vendor/docs/06-reference/installer-flags.md +260 -0
- package/vendor/docs/06-reference/ports-and-services.md +244 -0
- package/vendor/docs/06-reference/troubleshooting.md +665 -0
- package/vendor/docs/_index.json +233 -0
- package/vendor/docs/gatekeeper-design.md +953 -0
- package/vendor/docs/package.json +15 -0
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
- package/vendor/gatekeeper/dist/boot.d.ts +1 -0
- package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/boot.js +17 -0
- package/vendor/gatekeeper/dist/boot.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.js +45 -0
- package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
- package/vendor/gatekeeper/dist/index.d.ts +9 -0
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/index.js +13 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.js +42 -0
- package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.js +79 -0
- package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.js +314 -0
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.js +247 -0
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.js +79 -0
- package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
- package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.js +151 -0
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.js +2 -0
- package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
- package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/index.js +251 -0
- package/vendor/gatekeeper/dist/server/index.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
- package/vendor/gatekeeper/package.json +49 -0
- package/vendor/server/bin/lamaste-server.js +204 -0
- package/vendor/server/package.json +31 -0
- package/vendor/server/src/commands/certs.js +224 -0
- package/vendor/server/src/commands/chisel.js +132 -0
- package/vendor/server/src/commands/logs.js +49 -0
- package/vendor/server/src/commands/plugins.js +321 -0
- package/vendor/server/src/commands/reset-admin.js +478 -0
- package/vendor/server/src/commands/restart.js +94 -0
- package/vendor/server/src/commands/sites.js +257 -0
- package/vendor/server/src/commands/status.js +158 -0
- package/vendor/server/src/commands/tunnels.js +365 -0
- package/vendor/server/src/commands/uninstall.js +326 -0
- package/vendor/server/src/config.js +38 -0
- package/vendor/server/src/exec.js +26 -0
- package/vendor/server/src/logger.js +39 -0
- package/vendor/server/src/ndjson.js +56 -0
- package/vendor/server/src/panel-api.js +85 -0
- package/vendor/server/src/state.js +45 -0
- package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
- package/vendor/server-ui/dist/index.html +13 -0
- package/vendor/serverd/package.json +44 -0
- package/vendor/serverd/src/boot.js +13 -0
- package/vendor/serverd/src/index.js +191 -0
- package/vendor/serverd/src/lib/app-error.js +14 -0
- package/vendor/serverd/src/lib/authelia.js +85 -0
- package/vendor/serverd/src/lib/certbot.js +74 -0
- package/vendor/serverd/src/lib/chisel-args.js +6 -0
- package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
- package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
- package/vendor/serverd/src/lib/chisel-users.js +109 -0
- package/vendor/serverd/src/lib/chisel.js +81 -0
- package/vendor/serverd/src/lib/config.js +190 -0
- package/vendor/serverd/src/lib/constants.js +8 -0
- package/vendor/serverd/src/lib/csr-signing.js +331 -0
- package/vendor/serverd/src/lib/enrollment.js +302 -0
- package/vendor/serverd/src/lib/files.js +19 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
- package/vendor/serverd/src/lib/invite-page.js +281 -0
- package/vendor/serverd/src/lib/ip-utils.js +79 -0
- package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
- package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
- package/vendor/serverd/src/lib/mtls.js +795 -0
- package/vendor/serverd/src/lib/nginx.js +1090 -0
- package/vendor/serverd/src/lib/pki-paths.js +41 -0
- package/vendor/serverd/src/lib/pki-sign.js +103 -0
- package/vendor/serverd/src/lib/plugins.js +538 -0
- package/vendor/serverd/src/lib/push-install.js +390 -0
- package/vendor/serverd/src/lib/revocation.js +112 -0
- package/vendor/serverd/src/lib/services.js +131 -0
- package/vendor/serverd/src/lib/session.js +151 -0
- package/vendor/serverd/src/lib/state-db.js +130 -0
- package/vendor/serverd/src/lib/state.js +140 -0
- package/vendor/serverd/src/lib/storage.js +63 -0
- package/vendor/serverd/src/lib/system-stats.js +58 -0
- package/vendor/serverd/src/lib/tickets.js +1556 -0
- package/vendor/serverd/src/lib/totp.js +76 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
- package/vendor/serverd/src/lib/user-access-session.js +142 -0
- package/vendor/serverd/src/lib/user-access.js +52 -0
- package/vendor/serverd/src/middleware/errors.js +63 -0
- package/vendor/serverd/src/middleware/mtls.js +120 -0
- package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
- package/vendor/serverd/src/middleware/role-guard.js +58 -0
- package/vendor/serverd/src/middleware/twofa-session.js +65 -0
- package/vendor/serverd/src/middleware/user-access-session.js +52 -0
- package/vendor/serverd/src/routes/enrollment.js +120 -0
- package/vendor/serverd/src/routes/health.js +22 -0
- package/vendor/serverd/src/routes/invite.js +163 -0
- package/vendor/serverd/src/routes/management/agents.js +182 -0
- package/vendor/serverd/src/routes/management/certs.js +911 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
- package/vendor/serverd/src/routes/management/identity.js +107 -0
- package/vendor/serverd/src/routes/management/invitations.js +166 -0
- package/vendor/serverd/src/routes/management/logs.js +218 -0
- package/vendor/serverd/src/routes/management/plugins.js +560 -0
- package/vendor/serverd/src/routes/management/services.js +63 -0
- package/vendor/serverd/src/routes/management/settings.js +325 -0
- package/vendor/serverd/src/routes/management/sites.js +522 -0
- package/vendor/serverd/src/routes/management/storage.js +192 -0
- package/vendor/serverd/src/routes/management/system.js +201 -0
- package/vendor/serverd/src/routes/management/tickets.js +457 -0
- package/vendor/serverd/src/routes/management/tunnels.js +709 -0
- package/vendor/serverd/src/routes/management/users.js +377 -0
- package/vendor/serverd/src/routes/management.js +36 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
- package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
- package/vendor/serverd/src/routes/onboarding/index.js +18 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
- package/vendor/serverd/src/routes/onboarding/status.js +12 -0
- package/vendor/serverd/src/routes/plugin-router.js +79 -0
- package/vendor/serverd/src/routes/user-access.js +389 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { CHISEL_RELEASE } from '@lamalibre/lamaste';
|
|
5
|
+
import { getConfig, updateConfig } from '../../lib/config.js';
|
|
6
|
+
import * as chisel from '../../lib/chisel.js';
|
|
7
|
+
import * as authelia from '../../lib/authelia.js';
|
|
8
|
+
import * as certbot from '../../lib/certbot.js';
|
|
9
|
+
import * as nginx from '../../lib/nginx.js';
|
|
10
|
+
import { writeInvitePage } from '../../lib/invite-page.js';
|
|
11
|
+
import { migrateChiselCredentialsIfNeeded } from '../../lib/chisel-users.js';
|
|
12
|
+
import { loadAgentRegistry } from '../../lib/mtls.js';
|
|
13
|
+
import { setChiselRunningPinnedRelease } from '../../lib/chisel-runtime.js';
|
|
14
|
+
|
|
15
|
+
// Module-level state for provisioning
|
|
16
|
+
const emitter = new EventEmitter();
|
|
17
|
+
emitter.setMaxListeners(50);
|
|
18
|
+
|
|
19
|
+
const TASK_DEFINITIONS = [
|
|
20
|
+
{ id: 'install-chisel', title: 'Starting Chisel' },
|
|
21
|
+
{ id: 'install-authelia', title: 'Configuring Authelia' },
|
|
22
|
+
{ id: 'issue-certs', title: 'Issuing TLS certificates' },
|
|
23
|
+
{ id: 'configure-nginx', title: 'Configuring nginx' },
|
|
24
|
+
{ id: 'verify-services', title: 'Verifying services' },
|
|
25
|
+
{ id: 'finalize', title: 'Finalizing setup' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
let provisioningState = {
|
|
29
|
+
isRunning: false,
|
|
30
|
+
tasks: TASK_DEFINITIONS.map((t) => ({ ...t, status: 'pending', message: null, log: null })),
|
|
31
|
+
error: null,
|
|
32
|
+
result: null,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reset provisioning state for a fresh run.
|
|
37
|
+
*/
|
|
38
|
+
function resetState() {
|
|
39
|
+
provisioningState = {
|
|
40
|
+
isRunning: true,
|
|
41
|
+
tasks: TASK_DEFINITIONS.map((t) => ({ ...t, status: 'pending', message: null, log: null })),
|
|
42
|
+
error: null,
|
|
43
|
+
result: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Emit a progress event and update the provisioning state.
|
|
49
|
+
*/
|
|
50
|
+
function emitProgress(taskId, status, message, log = null) {
|
|
51
|
+
const taskIndex = provisioningState.tasks.findIndex((t) => t.id === taskId);
|
|
52
|
+
if (taskIndex !== -1) {
|
|
53
|
+
provisioningState.tasks[taskIndex].status = status;
|
|
54
|
+
provisioningState.tasks[taskIndex].message = message;
|
|
55
|
+
if (log !== null) {
|
|
56
|
+
provisioningState.tasks[taskIndex].log = log;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const current =
|
|
61
|
+
provisioningState.tasks.filter((t) => t.status === 'done').length +
|
|
62
|
+
(status === 'running' ? 1 : 0);
|
|
63
|
+
|
|
64
|
+
const payload = {
|
|
65
|
+
task: taskId,
|
|
66
|
+
title: provisioningState.tasks[taskIndex]?.title || taskId,
|
|
67
|
+
status,
|
|
68
|
+
message,
|
|
69
|
+
log,
|
|
70
|
+
progress: { current, total: TASK_DEFINITIONS.length },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
emitter.emit('progress', payload);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run the full provisioning sequence.
|
|
78
|
+
*/
|
|
79
|
+
async function runProvisioning(log) {
|
|
80
|
+
const config = getConfig();
|
|
81
|
+
const { domain, email } = config;
|
|
82
|
+
|
|
83
|
+
let adminPassword;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Step 1: Chisel. create-lamaste installed the pinned binary and its
|
|
87
|
+
// unit (as root); the panel creates the key and the authfile and starts
|
|
88
|
+
// the service.
|
|
89
|
+
emitProgress('install-chisel', 'running', 'Checking the Chisel installation...');
|
|
90
|
+
if (!(await chisel.isChiselInstalled())) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
'Chisel is not installed. Run `npx @lamalibre/create-lamaste` on the server to repair the installation.',
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const chiselVersion = await chisel.getInstalledChiselVersion();
|
|
96
|
+
await chisel.ensureChiselKey();
|
|
97
|
+
// Initialise the chisel-users authfile before starting the service.
|
|
98
|
+
// On a fresh install the registry is empty, so it holds only the
|
|
99
|
+
// no-grants sentinel user (chisel authenticates, and rejects every
|
|
100
|
+
// connection until an agent is enrolled). On re-provisioning over an
|
|
101
|
+
// existing install it preserves any prior credentials.
|
|
102
|
+
emitProgress('install-chisel', 'running', 'Initialising chisel-users authfile...');
|
|
103
|
+
await migrateChiselCredentialsIfNeeded(loadAgentRegistry, log);
|
|
104
|
+
emitProgress('install-chisel', 'running', 'Starting Chisel service...');
|
|
105
|
+
await chisel.startChisel();
|
|
106
|
+
setChiselRunningPinnedRelease(chiselVersion === CHISEL_RELEASE.version);
|
|
107
|
+
emitProgress('install-chisel', 'done', `Chisel ${chiselVersion ?? ''} running`.trim());
|
|
108
|
+
|
|
109
|
+
// Step 2: Authelia. Installed by create-lamaste (it runs as its own
|
|
110
|
+
// unprivileged account); the panel writes its configuration and starts it.
|
|
111
|
+
emitProgress('install-authelia', 'running', 'Checking the Authelia installation...');
|
|
112
|
+
if (!(await authelia.isAutheliaInstalled())) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
'Authelia is not installed. Run `npx @lamalibre/create-lamaste` on the server to repair the installation.',
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
emitProgress('install-authelia', 'running', 'Writing configuration...');
|
|
118
|
+
|
|
119
|
+
const secrets = {
|
|
120
|
+
jwtSecret: crypto.randomBytes(32).toString('hex'),
|
|
121
|
+
sessionSecret: crypto.randomBytes(32).toString('hex'),
|
|
122
|
+
storageEncryptionKey: crypto.randomBytes(32).toString('hex'),
|
|
123
|
+
};
|
|
124
|
+
await authelia.writeAutheliaConfig(domain, secrets);
|
|
125
|
+
|
|
126
|
+
emitProgress('install-authelia', 'running', 'Creating admin user...');
|
|
127
|
+
adminPassword = crypto.randomBytes(16).toString('base64url');
|
|
128
|
+
await authelia.createUser('admin', adminPassword);
|
|
129
|
+
|
|
130
|
+
emitProgress('install-authelia', 'running', 'Starting Authelia service...');
|
|
131
|
+
await authelia.startAuthelia();
|
|
132
|
+
emitProgress('install-authelia', 'done', 'Authelia configured and running');
|
|
133
|
+
|
|
134
|
+
// Step 3: Issue certificates
|
|
135
|
+
emitProgress('issue-certs', 'running', `Issuing certificate for panel.${domain}...`);
|
|
136
|
+
await certbot.issueCoreCerts(domain, email);
|
|
137
|
+
emitProgress('issue-certs', 'running', 'Setting up auto-renewal...');
|
|
138
|
+
await certbot.setupAutoRenew();
|
|
139
|
+
emitProgress('issue-certs', 'done', 'TLS certificates issued');
|
|
140
|
+
|
|
141
|
+
// Step 4: Configure nginx
|
|
142
|
+
emitProgress('configure-nginx', 'running', 'Writing panel vhost...');
|
|
143
|
+
await nginx.writePanelVhost(domain);
|
|
144
|
+
|
|
145
|
+
emitProgress('configure-nginx', 'running', 'Writing auth vhost...');
|
|
146
|
+
await nginx.writeAuthVhost(domain);
|
|
147
|
+
|
|
148
|
+
emitProgress('configure-nginx', 'running', 'Writing tunnel vhost...');
|
|
149
|
+
await nginx.writeTunnelVhost(domain);
|
|
150
|
+
|
|
151
|
+
emitProgress('configure-nginx', 'running', 'Writing invitation page...');
|
|
152
|
+
await writeInvitePage();
|
|
153
|
+
|
|
154
|
+
emitProgress('configure-nginx', 'running', 'Enabling sites...');
|
|
155
|
+
await nginx.enableSite('lamalibre-lamaste-panel-domain');
|
|
156
|
+
await nginx.enableSite('lamalibre-lamaste-auth');
|
|
157
|
+
await nginx.enableSite('lamalibre-lamaste-tunnel');
|
|
158
|
+
|
|
159
|
+
emitProgress('configure-nginx', 'running', 'Testing nginx configuration...');
|
|
160
|
+
const testResult = await nginx.testConfig();
|
|
161
|
+
if (!testResult.valid) {
|
|
162
|
+
throw new Error(`nginx configuration test failed: ${testResult.error}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
emitProgress('configure-nginx', 'running', 'Reloading nginx...');
|
|
166
|
+
await nginx.reload();
|
|
167
|
+
emitProgress('configure-nginx', 'done', 'nginx configured and reloaded');
|
|
168
|
+
|
|
169
|
+
// Step 5: Verify services
|
|
170
|
+
emitProgress('verify-services', 'running', 'Checking Chisel...');
|
|
171
|
+
const chiselRunning = await chisel.isChiselRunning();
|
|
172
|
+
if (!chiselRunning) {
|
|
173
|
+
throw new Error('Chisel service is not running after provisioning');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
emitProgress('verify-services', 'running', 'Checking Authelia...');
|
|
177
|
+
const autheliaRunning = await authelia.isAutheliaRunning();
|
|
178
|
+
if (!autheliaRunning) {
|
|
179
|
+
throw new Error('Authelia service is not running after provisioning');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
emitProgress('verify-services', 'running', 'Checking nginx...');
|
|
183
|
+
try {
|
|
184
|
+
const { stdout } = await execa('systemctl', ['is-active', 'nginx']);
|
|
185
|
+
if (stdout.trim() !== 'active') {
|
|
186
|
+
throw new Error('nginx is not active');
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
throw new Error('nginx service is not running after provisioning');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
emitProgress('verify-services', 'running', 'Checking panel server...');
|
|
193
|
+
try {
|
|
194
|
+
const { stdout } = await execa('curl', ['-s', 'http://127.0.0.1:3100/api/health']);
|
|
195
|
+
const health = JSON.parse(stdout);
|
|
196
|
+
if (health.status !== 'ok') {
|
|
197
|
+
throw new Error('Panel server health check returned unexpected status');
|
|
198
|
+
}
|
|
199
|
+
} catch (err) {
|
|
200
|
+
throw new Error(`Panel server health check failed: ${err.message}`);
|
|
201
|
+
}
|
|
202
|
+
emitProgress('verify-services', 'done', 'All services running');
|
|
203
|
+
|
|
204
|
+
// Step 6: Finalize
|
|
205
|
+
emitProgress('finalize', 'running', 'Updating configuration...');
|
|
206
|
+
await updateConfig({ onboarding: { status: 'COMPLETED' } });
|
|
207
|
+
|
|
208
|
+
const result = {
|
|
209
|
+
adminUsername: 'admin',
|
|
210
|
+
adminPassword,
|
|
211
|
+
panelUrl: `https://panel.${domain}`,
|
|
212
|
+
authUrl: `https://auth.${domain}`,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
provisioningState.result = result;
|
|
216
|
+
provisioningState.isRunning = false;
|
|
217
|
+
emitProgress('finalize', 'done', 'Provisioning complete');
|
|
218
|
+
|
|
219
|
+
// Send the completion event
|
|
220
|
+
emitter.emit('progress', {
|
|
221
|
+
task: 'complete',
|
|
222
|
+
status: 'done',
|
|
223
|
+
message: 'Provisioning complete',
|
|
224
|
+
result,
|
|
225
|
+
progress: { current: TASK_DEFINITIONS.length, total: TASK_DEFINITIONS.length },
|
|
226
|
+
});
|
|
227
|
+
} catch (err) {
|
|
228
|
+
const failedTask = provisioningState.tasks.find((t) => t.status === 'running');
|
|
229
|
+
const failedTaskId = failedTask?.id || 'unknown';
|
|
230
|
+
|
|
231
|
+
if (failedTask) {
|
|
232
|
+
failedTask.status = 'error';
|
|
233
|
+
failedTask.message = err.message;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
provisioningState.error = { task: failedTaskId, message: err.message };
|
|
237
|
+
provisioningState.isRunning = false;
|
|
238
|
+
|
|
239
|
+
emitter.emit('progress', {
|
|
240
|
+
task: failedTaskId,
|
|
241
|
+
status: 'error',
|
|
242
|
+
message: `Failed: ${failedTask?.title || failedTaskId}`,
|
|
243
|
+
error: err.message,
|
|
244
|
+
progress: {
|
|
245
|
+
current: provisioningState.tasks.filter((t) => t.status === 'done').length,
|
|
246
|
+
total: TASK_DEFINITIONS.length,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
log.error({ err, task: failedTaskId }, 'Provisioning failed');
|
|
251
|
+
} finally {
|
|
252
|
+
// Clear sensitive data from memory after clients have had time to receive it.
|
|
253
|
+
// This runs on both success and error paths to prevent password leaks.
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
adminPassword = undefined;
|
|
256
|
+
if (provisioningState.result) {
|
|
257
|
+
provisioningState.result.adminPassword = null;
|
|
258
|
+
}
|
|
259
|
+
}, 5000);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export default async function provisionRoute(fastify, _opts) {
|
|
264
|
+
// POST /provision — start provisioning
|
|
265
|
+
fastify.post('/provision', async (request, reply) => {
|
|
266
|
+
const config = getConfig();
|
|
267
|
+
const { status } = config.onboarding;
|
|
268
|
+
|
|
269
|
+
if (status === 'FRESH' || status === 'DOMAIN_SET') {
|
|
270
|
+
return reply.code(409).send({
|
|
271
|
+
error: 'DNS must be verified before provisioning',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (status === 'COMPLETED') {
|
|
276
|
+
return reply.code(410).send({
|
|
277
|
+
error: 'Onboarding already completed',
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// If provisioning is actively running, return 409
|
|
282
|
+
if (provisioningState.isRunning) {
|
|
283
|
+
return reply.code(409).send({
|
|
284
|
+
error: 'Provisioning already in progress',
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Set status to PROVISIONING
|
|
289
|
+
await updateConfig({ onboarding: { status: 'PROVISIONING' } });
|
|
290
|
+
|
|
291
|
+
// Reset state and start provisioning in the background
|
|
292
|
+
resetState();
|
|
293
|
+
runProvisioning(request.log);
|
|
294
|
+
|
|
295
|
+
return reply.code(202).send({ ok: true, message: 'Provisioning started' });
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// WebSocket /provision/stream — real-time progress
|
|
299
|
+
fastify.get('/provision/stream', { websocket: true }, (socket, _request) => {
|
|
300
|
+
// Send current state immediately for late-joining clients
|
|
301
|
+
socket.send(
|
|
302
|
+
JSON.stringify({
|
|
303
|
+
type: 'state',
|
|
304
|
+
...provisioningState,
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Subscribe to progress events
|
|
309
|
+
function onProgress(payload) {
|
|
310
|
+
try {
|
|
311
|
+
socket.send(JSON.stringify(payload));
|
|
312
|
+
} catch {
|
|
313
|
+
// Client disconnected — handled by close event
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
emitter.on('progress', onProgress);
|
|
318
|
+
|
|
319
|
+
socket.on('close', () => {
|
|
320
|
+
emitter.off('progress', onProgress);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
socket.on('error', () => {
|
|
324
|
+
emitter.off('progress', onProgress);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getConfig } from '../../lib/config.js';
|
|
2
|
+
|
|
3
|
+
export default async function statusRoute(fastify, _opts) {
|
|
4
|
+
fastify.get('/status', async (_request, _reply) => {
|
|
5
|
+
const config = getConfig();
|
|
6
|
+
return {
|
|
7
|
+
status: config.onboarding.status,
|
|
8
|
+
domain: config.domain ?? null,
|
|
9
|
+
ip: config.ip,
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side plugin router — delegates to pluginHostPlugin from @lamalibre/lamaste.
|
|
3
|
+
*
|
|
4
|
+
* Configures the core plugin host with:
|
|
5
|
+
* - mTLS auth strategy (two-level encapsulation)
|
|
6
|
+
* - Per-plugin auth hook: admits admin certs unconditionally, or
|
|
7
|
+
* agent / plugin-agent certs that hold at least one
|
|
8
|
+
* `plugin:<pluginName>:*` capability. Per-route capability specifics
|
|
9
|
+
* (e.g. `plugin:rodeo:run` vs `plugin:rodeo:submit`) remain each
|
|
10
|
+
* plugin's responsibility via its own preHandlers.
|
|
11
|
+
* - Server paths (/etc/lamalibre/lamaste/)
|
|
12
|
+
* - Storage config resolution per plugin
|
|
13
|
+
* - Onboarding guard (503 before onboarding completes)
|
|
14
|
+
*/
|
|
15
|
+
import { pluginHostPlugin } from '@lamalibre/lamaste';
|
|
16
|
+
import { readPlugins } from '../lib/plugins.js';
|
|
17
|
+
import { getPluginStorageConfig } from '../lib/storage.js';
|
|
18
|
+
import { getConfig } from '../lib/config.js';
|
|
19
|
+
import { getCaPaths } from '../lib/pki-paths.js';
|
|
20
|
+
import { managementOnly } from '../middleware/onboarding-guard.js';
|
|
21
|
+
|
|
22
|
+
const STATE_DIR = process.env.LAMALIBRE_LAMASTE_STATE_DIR || '/etc/lamalibre/lamaste';
|
|
23
|
+
const PLUGINS_DIR = `${STATE_DIR}/plugins`;
|
|
24
|
+
|
|
25
|
+
export default async function pluginRouter(fastify) {
|
|
26
|
+
// Block all plugin routes until onboarding is complete
|
|
27
|
+
fastify.addHook('onRequest', managementOnly());
|
|
28
|
+
|
|
29
|
+
await fastify.register(pluginHostPlugin, {
|
|
30
|
+
baseDir: STATE_DIR,
|
|
31
|
+
pluginsDataDir: PLUGINS_DIR,
|
|
32
|
+
readRegistry: readPlugins,
|
|
33
|
+
auth: 'mtls',
|
|
34
|
+
authHookFactory: (pluginName) => {
|
|
35
|
+
const allowedPrefix = `plugin:${pluginName}:`;
|
|
36
|
+
return async (request, reply) => {
|
|
37
|
+
const role = request.certRole;
|
|
38
|
+
if (role === 'admin') return;
|
|
39
|
+
|
|
40
|
+
if (role === 'agent' || role === 'plugin-agent') {
|
|
41
|
+
const caps = request.certCapabilities || [];
|
|
42
|
+
if (caps.some((c) => c.startsWith(allowedPrefix))) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return reply.code(403).send({
|
|
48
|
+
error: 'Insufficient scope for plugin route',
|
|
49
|
+
details: {
|
|
50
|
+
required: `admin OR agent with at least one "${allowedPrefix}*" capability`,
|
|
51
|
+
current: role ?? 'unknown',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve per-plugin register options: always injects `lamasteCa`
|
|
59
|
+
* ({ certPath, keyPath }) so plugins (e.g. rodeo) can reuse Lamaste's
|
|
60
|
+
* CA for mTLS; adds `storage` only when the plugin has bound storage.
|
|
61
|
+
*/
|
|
62
|
+
resolvePluginOptions: async (plugin) => {
|
|
63
|
+
const panelConfig = getConfig();
|
|
64
|
+
const base = {
|
|
65
|
+
lamasteCa: getCaPaths(),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const storageConfig = await getPluginStorageConfig(plugin.name);
|
|
69
|
+
if (storageConfig) {
|
|
70
|
+
base.storage = {
|
|
71
|
+
...storageConfig,
|
|
72
|
+
prefix: panelConfig.serverId,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return base;
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|