@lamalibre/create-lamaste 4.2.0 → 4.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/package.json +2 -2
- package/scripts/lamaste-priv +205 -2
- package/src/index.js +4 -4
- package/src/lib/node-version.js +114 -0
- package/src/lib/relay-services.js +15 -1
- package/src/lib/service-config.js +113 -0
- package/src/tasks/gatekeeper.js +8 -3
- package/src/tasks/node.js +34 -75
- package/src/tasks/redeploy.js +36 -0
- package/vendor/docs/00-introduction/how-it-works.md +2 -2
- package/vendor/docs/00-introduction/quickstart.md +5 -5
- package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
- package/vendor/docs/01-concepts/authentication.md +6 -3
- package/vendor/docs/01-concepts/authorization.md +29 -17
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +8 -3
- package/vendor/docs/01-concepts/security-model.md +98 -34
- package/vendor/docs/01-concepts/tickets.md +2 -2
- package/vendor/docs/01-concepts/tunneling.md +4 -4
- package/vendor/docs/02-guides/agent-setup.md +34 -16
- package/vendor/docs/02-guides/certificate-management.md +2 -2
- package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
- package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
- package/vendor/docs/02-guides/installation.md +3 -3
- package/vendor/docs/02-guides/onboarding.md +2 -2
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
- package/vendor/docs/03-architecture/installer-flow.md +2 -2
- package/vendor/docs/03-architecture/installer.md +4 -4
- package/vendor/docs/03-architecture/management-flow.md +1 -1
- package/vendor/docs/03-architecture/nginx-configuration.md +91 -26
- package/vendor/docs/03-architecture/overview.md +8 -8
- package/vendor/docs/03-architecture/panel-client.md +6 -6
- package/vendor/docs/03-architecture/panel-server.md +2 -2
- package/vendor/docs/03-architecture/state-management.md +2 -2
- package/vendor/docs/03-architecture/system-overview.md +1 -1
- package/vendor/docs/04-api-reference/agent-hosted-plugins.md +407 -0
- package/vendor/docs/04-api-reference/app-host.md +360 -0
- package/vendor/docs/04-api-reference/audit-and-notifications.md +57 -0
- package/vendor/docs/04-api-reference/certificates.md +95 -38
- package/vendor/docs/04-api-reference/gatekeeper.md +5 -3
- package/vendor/docs/04-api-reference/lifecycle-hooks.md +333 -0
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/overview.md +137 -111
- package/vendor/docs/04-api-reference/registry-and-oidc.md +7 -3
- package/vendor/docs/04-api-reference/tunnels.md +15 -13
- package/vendor/docs/05-operations/backup-and-restore.md +1 -1
- package/vendor/docs/05-operations/uninstalling.md +1 -1
- package/vendor/docs/05-operations/upgrades.md +25 -20
- package/vendor/docs/06-reference/config-files.md +190 -97
- package/vendor/docs/06-reference/glossary.md +3 -3
- package/vendor/docs/06-reference/installer-flags.md +13 -13
- package/vendor/docs/06-reference/ports-and-services.md +1 -1
- package/vendor/docs/06-reference/troubleshooting.md +33 -8
- package/vendor/docs/_index.json +20 -0
- package/vendor/docs/gatekeeper-design.md +22 -19
- package/vendor/docs/identity-directory-design.md +57 -56
- package/vendor/docs/package.json +1 -1
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
- package/vendor/gatekeeper/dist/lib/types.d.ts +12 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.d.ts +2 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/index.js +47 -8
- package/vendor/gatekeeper/dist/server/index.js.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
- package/vendor/gatekeeper/dist/server/routes/authz.js +233 -141
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
- package/vendor/gatekeeper/package.json +4 -3
- package/vendor/server/bin/lamaste-server.js +18 -1
- package/vendor/server/package.json +2 -2
- package/vendor/server/src/commands/audit.js +60 -0
- package/vendor/server/src/commands/plugins.js +963 -12
- package/vendor/server/src/commands/push-install.js +317 -0
- package/vendor/server/src/commands/registry-credential.js +2 -1
- package/vendor/server/src/commands/uninstall.js +28 -1
- package/vendor/server/src/ndjson.js +4 -2
- package/vendor/server/src/panel-api.js +32 -4
- package/vendor/server-ui/dist/assets/index-Bc5NCMSv.css +1 -0
- package/vendor/server-ui/dist/assets/index-DBp3cmaU.js +84 -0
- package/vendor/server-ui/dist/index.html +2 -2
- package/vendor/serverd/package.json +2 -2
- package/vendor/serverd/src/index.js +56 -4
- package/vendor/serverd/src/lib/actor.js +24 -0
- package/vendor/serverd/src/lib/agent-jobs.js +330 -0
- package/vendor/serverd/src/lib/app-error.js +10 -0
- package/vendor/serverd/src/lib/audit.js +145 -0
- package/vendor/serverd/src/lib/hook-effects.js +123 -0
- package/vendor/serverd/src/lib/hook-pipelines.js +902 -0
- package/vendor/serverd/src/lib/migrations/state/0006_plugin_agent.sql +4 -0
- package/vendor/serverd/src/lib/migrations/state/0007_audit_and_notifications.sql +29 -0
- package/vendor/serverd/src/lib/migrations/state/0008_plugin_hooks.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0009_plugin_agent_half.sql +53 -0
- package/vendor/serverd/src/lib/migrations/state/0010_plugin_routes_and_retained_data.sql +47 -0
- package/vendor/serverd/src/lib/migrations/state/0011_agent_job_signature.sql +14 -0
- package/vendor/serverd/src/lib/mtls.js +89 -8
- package/vendor/serverd/src/lib/nginx.js +298 -44
- package/vendor/serverd/src/lib/notifications.js +107 -0
- package/vendor/serverd/src/lib/plugin-hooks.js +560 -0
- package/vendor/serverd/src/lib/plugin-hosting.js +688 -0
- package/vendor/serverd/src/lib/plugin-lifecycle.js +603 -0
- package/vendor/serverd/src/lib/plugin-operations.js +295 -0
- package/vendor/serverd/src/lib/plugin-packages.js +249 -0
- package/vendor/serverd/src/lib/plugins.js +298 -68
- package/vendor/serverd/src/lib/push-install.js +88 -2
- package/vendor/serverd/src/lib/relay-hooks.js +100 -0
- package/vendor/serverd/src/lib/relay-signing.js +43 -0
- package/vendor/serverd/src/lib/request-hooks.js +79 -0
- package/vendor/serverd/src/lib/retained-data.js +99 -0
- package/vendor/serverd/src/lib/trust-proxy.js +34 -0
- package/vendor/serverd/src/lib/tunnel-hooks.js +88 -0
- package/vendor/serverd/src/lib/vhost-reconcile.js +260 -0
- package/vendor/serverd/src/middleware/twofa-session.js +11 -1
- package/vendor/serverd/src/routes/agent-forwarder.js +37 -0
- package/vendor/serverd/src/routes/management/agent-panel-authz.js +97 -0
- package/vendor/serverd/src/routes/management/agents.js +262 -1
- package/vendor/serverd/src/routes/management/audit.js +81 -0
- package/vendor/serverd/src/routes/management/certs.js +1 -15
- package/vendor/serverd/src/routes/management/plugins.js +569 -54
- package/vendor/serverd/src/routes/management/tunnels.js +26 -0
- package/vendor/serverd/src/routes/management.js +4 -0
- package/vendor/server-ui/dist/assets/index-B5Wp8lu-.js +0 -257
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +0 -1
|
@@ -153,6 +153,18 @@ export interface TunnelInfo {
|
|
|
153
153
|
readonly fqdn: string;
|
|
154
154
|
readonly accessMode: AccessMode;
|
|
155
155
|
readonly enabled: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Set on a plugin tunnel (`type: 'plugin'` with an owning agent): the
|
|
158
|
+
* request goes to that agent's lamaste-agentd, which accepts the user only
|
|
159
|
+
* with a relay assertion Gatekeeper signs (see authz.ts).
|
|
160
|
+
*/
|
|
161
|
+
readonly plugin?: PluginTunnelTarget | undefined;
|
|
162
|
+
}
|
|
163
|
+
/** Where a plugin tunnel's requests go: the agent, its daemon's port, the plugin route. */
|
|
164
|
+
export interface PluginTunnelTarget {
|
|
165
|
+
readonly agentLabel: string;
|
|
166
|
+
readonly port: number;
|
|
167
|
+
readonly route: string;
|
|
156
168
|
}
|
|
157
169
|
/**
|
|
158
170
|
* Cached Authelia session data.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,sBAAsB,CAAC;CAC5C;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CAClD;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../boot.js';
|
|
2
2
|
import Fastify from 'fastify';
|
|
3
|
+
import type { RelaySigner } from '@lamalibre/lamaste';
|
|
3
4
|
import type { TunnelInfo, GatekeeperSettings, AutheliaSession } from '../lib/types.js';
|
|
4
5
|
export declare function createServer(): Promise<ReturnType<typeof Fastify>>;
|
|
5
6
|
declare module 'fastify' {
|
|
@@ -8,6 +9,7 @@ declare module 'fastify' {
|
|
|
8
9
|
getSettings(): GatekeeperSettings;
|
|
9
10
|
getSessionCache(): Map<string, AutheliaSession>;
|
|
10
11
|
getCacheVersion(): number;
|
|
12
|
+
getRelaySigner(): RelaySigner;
|
|
11
13
|
bustCache(): void;
|
|
12
14
|
updateSettings(s: GatekeeperSettings): void;
|
|
13
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,OAAO,OAAO,MAAM,SAAS,CAAC;AAW9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAStD,OAAO,KAAK,EACV,UAAU,EAEV,kBAAkB,EAClB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AA4JzB,wBAAsB,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAkJxE;AAkCD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,UAAU,IAAI,UAAU,EAAE,CAAC;QAC3B,WAAW,IAAI,kBAAkB,CAAC;QAClC,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAChD,eAAe,IAAI,MAAM,CAAC;QAC1B,cAAc,IAAI,WAAW,CAAC;QAC9B,SAAS,IAAI,IAAI,CAAC;QAClB,cAAc,CAAC,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;KAC7C;CACF"}
|
|
@@ -5,7 +5,7 @@ import rateLimit from '@fastify/rate-limit';
|
|
|
5
5
|
import { watch } from 'node:fs';
|
|
6
6
|
import { readFile } from 'node:fs/promises';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import { atomicWriteJSON } from '@lamalibre/lamaste';
|
|
8
|
+
import { atomicWriteJSON, AGENT_LABEL_REGEX, createRelaySigner, RELAY_SIGNING_KEY_FILE, } from '@lamalibre/lamaste';
|
|
9
9
|
import { DEFAULT_DATA_DIR, GROUPS_FILE, GRANTS_FILE, SETTINGS_FILE, GATEKEEPER_PORT, SESSION_CACHE_TTL_MS, } from '../lib/constants.js';
|
|
10
10
|
import { registerCacheInvalidator } from '../lib/cache-bust.js';
|
|
11
11
|
import { authzRoutes } from './routes/authz.js';
|
|
@@ -16,6 +16,14 @@ import { accessRequestRoutes } from './routes/access-request.js';
|
|
|
16
16
|
const dataDir = process.env.LAMALIBRE_LAMASTE_DATA_DIR ?? DEFAULT_DATA_DIR;
|
|
17
17
|
const tunnelsPath = path.join(dataDir, 'tunnels.json');
|
|
18
18
|
const settingsPath = path.join(dataDir, SETTINGS_FILE);
|
|
19
|
+
/**
|
|
20
|
+
* The relay's assertion signing key, created by the panel (never here). A
|
|
21
|
+
* plugin tunnel's user is vouched for to the agent with it; the file is
|
|
22
|
+
* re-read when it changes.
|
|
23
|
+
*/
|
|
24
|
+
const relaySigner = createRelaySigner(path.join(dataDir, RELAY_SIGNING_KEY_FILE), {
|
|
25
|
+
create: false,
|
|
26
|
+
});
|
|
19
27
|
// ---------------------------------------------------------------------------
|
|
20
28
|
// In-memory caches (refreshed by file watch + TTL)
|
|
21
29
|
// ---------------------------------------------------------------------------
|
|
@@ -30,17 +38,43 @@ let cacheVersion = 0;
|
|
|
30
38
|
// ---------------------------------------------------------------------------
|
|
31
39
|
// File loaders
|
|
32
40
|
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* The agent-side target of a plugin tunnel, or undefined. Only a well-formed
|
|
43
|
+
* one is returned: its fields end up in a signed assertion.
|
|
44
|
+
*/
|
|
45
|
+
function pluginTunnelTarget(t) {
|
|
46
|
+
const { type, agentLabel, port, pluginRoute } = t;
|
|
47
|
+
if (type !== 'plugin' ||
|
|
48
|
+
typeof agentLabel !== 'string' ||
|
|
49
|
+
!AGENT_LABEL_REGEX.test(agentLabel) ||
|
|
50
|
+
typeof port !== 'number' ||
|
|
51
|
+
!Number.isInteger(port) ||
|
|
52
|
+
port < 1 ||
|
|
53
|
+
port > 65535 ||
|
|
54
|
+
typeof pluginRoute !== 'string' ||
|
|
55
|
+
!/^[a-z0-9][a-z0-9-]{0,63}$/.test(pluginRoute)) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return { agentLabel, port, route: pluginRoute };
|
|
59
|
+
}
|
|
33
60
|
async function loadTunnels() {
|
|
34
61
|
try {
|
|
35
62
|
const raw = await readFile(tunnelsPath, 'utf-8');
|
|
36
63
|
const parsed = JSON.parse(raw);
|
|
37
64
|
if (!Array.isArray(parsed))
|
|
38
65
|
return [];
|
|
39
|
-
return parsed
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
return (parsed
|
|
67
|
+
// An internal plugin tunnel has no hostname: nothing to authorize.
|
|
68
|
+
.filter((t) => t.exposure !== 'internal' && t.fqdn)
|
|
69
|
+
.map((t) => {
|
|
70
|
+
const plugin = pluginTunnelTarget(t);
|
|
71
|
+
return {
|
|
72
|
+
id: String(t.id ?? ''),
|
|
73
|
+
fqdn: String(t.fqdn ?? ''),
|
|
74
|
+
accessMode: t.accessMode ?? 'restricted',
|
|
75
|
+
enabled: t.enabled !== false,
|
|
76
|
+
...(plugin ? { plugin } : {}),
|
|
77
|
+
};
|
|
44
78
|
}));
|
|
45
79
|
}
|
|
46
80
|
catch {
|
|
@@ -112,7 +146,11 @@ function watchFile(filePath, reload, logger) {
|
|
|
112
146
|
export async function createServer() {
|
|
113
147
|
const server = Fastify({
|
|
114
148
|
logger: true,
|
|
115
|
-
|
|
149
|
+
// Forwarding headers are honoured only from loopback: the gatekeeper
|
|
150
|
+
// listens on 127.0.0.1 and its callers (nginx, the panel proxy) are
|
|
151
|
+
// local. Trust by address, not hop count — a hop count cannot tell a
|
|
152
|
+
// proxy from a direct client spoofing X-Forwarded-*.
|
|
153
|
+
trustProxy: '127.0.0.1',
|
|
116
154
|
});
|
|
117
155
|
// Global rate limit. The gatekeeper binds to 127.0.0.1 and is reached
|
|
118
156
|
// exclusively by nginx auth_request and the panel proxy — both are
|
|
@@ -120,7 +158,7 @@ export async function createServer() {
|
|
|
120
158
|
// per-IP limiter would collapse them into a single bucket and either
|
|
121
159
|
// be useless or punish legitimate nginx traffic. We collapse the key
|
|
122
160
|
// explicitly to one shared bucket and set a high ceiling; the hot
|
|
123
|
-
//
|
|
161
|
+
// authorization routes then override this with their own ceiling that
|
|
124
162
|
// makes the "nginx-only" assumption explicit.
|
|
125
163
|
await server.register(rateLimit, {
|
|
126
164
|
max: 1000,
|
|
@@ -135,6 +173,7 @@ export async function createServer() {
|
|
|
135
173
|
server.decorate('getSettings', () => settingsCache);
|
|
136
174
|
server.decorate('getSessionCache', () => sessionCache);
|
|
137
175
|
server.decorate('getCacheVersion', () => cacheVersion);
|
|
176
|
+
server.decorate('getRelaySigner', () => relaySigner);
|
|
138
177
|
const bustCache = () => {
|
|
139
178
|
cacheVersion++;
|
|
140
179
|
sessionCache.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AACpB,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,SAAS,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../../src/server/routes/authz.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../../src/server/routes/authz.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA0J7E,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { request as undiciRequest } from 'undici';
|
|
3
|
+
import { RELAY_ASSERTION_HEADER, signRelayAssertion } from '@lamalibre/lamaste';
|
|
3
4
|
import { AUTHELIA_VERIFY_URL, SESSION_CACHE_TTL_MS } from '../../lib/constants.js';
|
|
4
5
|
import { checkAccess } from '../../lib/authz.js';
|
|
5
6
|
import { buildAccessRequestPage } from '../../lib/templates.js';
|
|
@@ -88,11 +89,30 @@ function getHeader(headers, name) {
|
|
|
88
89
|
return val;
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
|
-
* Look up a tunnel by its FQDN from the in-memory cache.
|
|
92
|
+
* Look up a tunnel by its FQDN from the in-memory cache. Hostnames compare
|
|
93
|
+
* case-insensitively.
|
|
92
94
|
*/
|
|
93
95
|
function findTunnelByFqdn(tunnels, fqdn) {
|
|
94
|
-
|
|
96
|
+
const wanted = fqdn.toLowerCase();
|
|
97
|
+
return tunnels.find((t) => t.fqdn.toLowerCase() === wanted);
|
|
95
98
|
}
|
|
99
|
+
/** A tunnel hostname as a vhost writes it into the subrequest's path. */
|
|
100
|
+
const TUNNEL_FQDN_RE = /^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/;
|
|
101
|
+
/**
|
|
102
|
+
* nginx is the only client of the authorization routes (Gatekeeper binds
|
|
103
|
+
* 127.0.0.1) and calls them on every protected request. Per-IP throttling
|
|
104
|
+
* would punish legitimate traffic because every caller arrives as
|
|
105
|
+
* 127.0.0.1, so the key is one bucket with an effectively-unlimited 10k/min
|
|
106
|
+
* ceiling: the rate limit is structurally present (CodeQL
|
|
107
|
+
* js/missing-rate-limiting) without breaking the hot path.
|
|
108
|
+
*/
|
|
109
|
+
const INTERNAL_RATE_LIMIT = {
|
|
110
|
+
rateLimit: {
|
|
111
|
+
max: 10_000,
|
|
112
|
+
timeWindow: '1 minute',
|
|
113
|
+
keyGenerator: () => 'gatekeeper-internal',
|
|
114
|
+
},
|
|
115
|
+
};
|
|
96
116
|
/**
|
|
97
117
|
* Parse an X-Original-URL header into its hostname and host components.
|
|
98
118
|
* Returns null when the value is not a parseable absolute URL.
|
|
@@ -111,160 +131,232 @@ function parseOriginalUrl(originalUrl) {
|
|
|
111
131
|
// ---------------------------------------------------------------------------
|
|
112
132
|
export async function authzRoutes(fastify) {
|
|
113
133
|
/**
|
|
114
|
-
* GET /authz/
|
|
134
|
+
* GET /authz/tunnel/:fqdn — the nginx auth_request target of a tunnel vhost
|
|
135
|
+
*
|
|
136
|
+
* The vhost names its tunnel in the path, written into its configuration
|
|
137
|
+
* (`proxy_pass http://127.0.0.1:9294/authz/tunnel/<fqdn>;`), as the
|
|
138
|
+
* agent-panel vhost names its agent: nothing in the request can choose
|
|
139
|
+
* another tunnel. X-Original-URL — the URL Authelia is asked about, with
|
|
140
|
+
* the request's path and query — and Host carry the same hostname and must
|
|
141
|
+
* agree with it.
|
|
115
142
|
*
|
|
116
143
|
* Flow:
|
|
117
|
-
* 1.
|
|
118
|
-
* 2. Validate Authelia session (cached in memory)
|
|
119
|
-
* 3.
|
|
120
|
-
* 4.
|
|
121
|
-
* 5. Return 200 (allowed), 401 (not authenticated), or 403 (not authorized)
|
|
144
|
+
* 1. Check the tunnel named, X-Original-URL and Host agree
|
|
145
|
+
* 2. Validate the Authelia session (cached in memory)
|
|
146
|
+
* 3. Check access mode and grants
|
|
147
|
+
* 4. Return 200 (allowed), 401 (not authenticated), or 403 (not authorized)
|
|
122
148
|
*
|
|
123
149
|
* On 403, the response body is the full access-request HTML page
|
|
124
150
|
* (nginx serves this inline on the tunnel's FQDN via error_page 403 =).
|
|
125
151
|
*/
|
|
126
|
-
fastify.get('/authz/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
152
|
+
fastify.get('/authz/tunnel/:fqdn', { config: INTERNAL_RATE_LIMIT }, async (request, reply) => authorizeTunnelRequest(fastify, request, reply, request.params.fqdn));
|
|
153
|
+
/**
|
|
154
|
+
* GET /authz/check — what tunnel vhosts written before 4.3 call. They name
|
|
155
|
+
* the tunnel by the client's Host header (`$http_host`), which an
|
|
156
|
+
* absolute-form request line can point at another tunnel than the one
|
|
157
|
+
* nginx serves, so their requests are refused: until the panel rewrites
|
|
158
|
+
* such a vhost (it does when it starts) its tunnel is closed, rather than
|
|
159
|
+
* open to the users of every other tunnel.
|
|
160
|
+
*/
|
|
161
|
+
fastify.get('/authz/check', { config: INTERNAL_RATE_LIMIT }, async (request, reply) => {
|
|
162
|
+
request.log.error({
|
|
163
|
+
requestId: request.id,
|
|
164
|
+
reason: 'vhost_predates_host_binding',
|
|
165
|
+
hostHeader: request.headers['host'],
|
|
166
|
+
}, 'authz refused: this tunnel vhost names its tunnel by the client Host header; restart lamalibre-lamaste-serverd to rewrite it');
|
|
167
|
+
return reply
|
|
168
|
+
.code(403)
|
|
169
|
+
.header('Cache-Control', 'no-store')
|
|
170
|
+
.type('text/plain; charset=utf-8')
|
|
171
|
+
.send('This tunnel is unavailable until the relay rewrites its configuration.\n');
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Authorize one request on the tunnel named `fqdn` (see the
|
|
176
|
+
* `/authz/tunnel/:fqdn` route).
|
|
177
|
+
*/
|
|
178
|
+
async function authorizeTunnelRequest(fastify, request, reply, fqdn) {
|
|
179
|
+
const requestId = request.id;
|
|
180
|
+
if (!TUNNEL_FQDN_RE.test(fqdn)) {
|
|
181
|
+
request.log.info({ requestId, reason: 'invalid_tunnel_name' }, 'authz denied');
|
|
182
|
+
return reply.code(401).send();
|
|
183
|
+
}
|
|
184
|
+
const cookie = request.headers['cookie'] ?? '';
|
|
185
|
+
const originalUrl = request.headers['x-original-url'] ?? '';
|
|
186
|
+
if (!cookie || !originalUrl) {
|
|
187
|
+
return reply.code(401).send();
|
|
188
|
+
}
|
|
189
|
+
const parsed = parseOriginalUrl(originalUrl);
|
|
190
|
+
if (!parsed) {
|
|
191
|
+
request.log.info({ requestId, reason: 'invalid_original_url' }, 'authz denied');
|
|
192
|
+
return reply.code(401).send();
|
|
193
|
+
}
|
|
194
|
+
// The vhost writes the same hostname into the path, X-Original-URL and
|
|
195
|
+
// Host. Disagreement means a request that did not come from a tunnel
|
|
196
|
+
// vhost of this version: refuse it before Authelia is asked anything.
|
|
197
|
+
const named = fqdn.toLowerCase();
|
|
198
|
+
const hostHeader = request.headers['host'] ?? '';
|
|
199
|
+
const hostOnly = hostHeader.split(':')[0]?.toLowerCase() ?? '';
|
|
200
|
+
if (parsed.hostname.toLowerCase() !== named || hostOnly !== named) {
|
|
201
|
+
request.log.info({
|
|
202
|
+
requestId,
|
|
203
|
+
reason: hostOnly ? 'tunnel_name_mismatch' : 'missing_host_header',
|
|
204
|
+
tunnel: fqdn,
|
|
205
|
+
originalHost: parsed.hostname,
|
|
206
|
+
hostHeader,
|
|
207
|
+
}, 'authz denied');
|
|
208
|
+
return reply.code(401).send();
|
|
209
|
+
}
|
|
210
|
+
const tunnels = fastify.getTunnels();
|
|
211
|
+
const tunnel = findTunnelByFqdn(tunnels, fqdn);
|
|
212
|
+
if (!tunnel) {
|
|
213
|
+
request.log.info({ requestId, reason: 'unknown_tunnel_fqdn', fqdn }, 'authz denied');
|
|
214
|
+
return reply.code(401).send();
|
|
215
|
+
}
|
|
216
|
+
// Check session cache (keyed by Authelia session cookie only to prevent
|
|
217
|
+
// cache pollution via unrelated cookies like CSRF tokens or analytics).
|
|
218
|
+
const autheliaCookie = extractAutheliaCookie(cookie);
|
|
219
|
+
if (!autheliaCookie) {
|
|
220
|
+
// No authelia_session — short-circuit before any cache work or the
|
|
221
|
+
// upstream Authelia /verify call. Without this, an unauthenticated
|
|
222
|
+
// request would still cost us an Authelia round-trip.
|
|
223
|
+
request.log.info({ requestId, reason: 'missing_authelia_cookie', fqdn: parsed.hostname }, 'authz denied');
|
|
224
|
+
return reply.code(401).send();
|
|
225
|
+
}
|
|
226
|
+
const cacheKey = hashCookie(autheliaCookie);
|
|
227
|
+
const sessionCache = fastify.getSessionCache();
|
|
228
|
+
const cached = sessionCache.get(cacheKey);
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
let session;
|
|
231
|
+
if (cached && cached.expiresAt > now) {
|
|
232
|
+
session = cached;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
if (cached) {
|
|
236
|
+
// Lazy TTL eviction — one stale entry pruned per request.
|
|
237
|
+
sessionCache.delete(cacheKey);
|
|
180
238
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (!autheliaCookie) {
|
|
185
|
-
// No authelia_session — short-circuit before any cache work or the
|
|
186
|
-
// upstream Authelia /verify call. Without this, an unauthenticated
|
|
187
|
-
// request would still cost us an Authelia round-trip.
|
|
188
|
-
request.log.info({ requestId, reason: 'missing_authelia_cookie', fqdn: parsed.hostname }, 'authz denied');
|
|
239
|
+
const validated = await validateWithAuthelia(cookie, originalUrl, requestId);
|
|
240
|
+
if (!validated) {
|
|
241
|
+
request.log.info({ requestId, reason: 'authelia_rejected', fqdn: parsed.hostname }, 'authz denied');
|
|
189
242
|
return reply.code(401).send();
|
|
190
243
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
session = cached;
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
if (cached) {
|
|
201
|
-
// Lazy TTL eviction — one stale entry pruned per request.
|
|
202
|
-
sessionCache.delete(cacheKey);
|
|
203
|
-
}
|
|
204
|
-
const validated = await validateWithAuthelia(cookie, originalUrl, requestId);
|
|
205
|
-
if (!validated) {
|
|
206
|
-
request.log.info({ requestId, reason: 'authelia_rejected', fqdn: parsed.hostname }, 'authz denied');
|
|
207
|
-
return reply.code(401).send();
|
|
208
|
-
}
|
|
209
|
-
// Opportunistic eviction — drop oldest (insertion-order head) when at cap.
|
|
210
|
-
while (sessionCache.size >= MAX_SESSION_CACHE_ENTRIES) {
|
|
211
|
-
const oldestKey = sessionCache.keys().next().value;
|
|
212
|
-
if (oldestKey === undefined)
|
|
213
|
-
break;
|
|
214
|
-
sessionCache.delete(oldestKey);
|
|
215
|
-
}
|
|
216
|
-
sessionCache.set(cacheKey, validated);
|
|
217
|
-
session = validated;
|
|
218
|
-
}
|
|
219
|
-
// Check access mode
|
|
220
|
-
if (tunnel.accessMode === 'public' || tunnel.accessMode === 'authenticated') {
|
|
221
|
-
setAutheliaHeaders(reply, session);
|
|
222
|
-
reply.header('Cache-Control', `public, max-age=${AUTHZ_SUCCESS_MAX_AGE_SECONDS}`);
|
|
223
|
-
reply.header('X-Gatekeeper-Cache-Version', String(fastify.getCacheVersion()));
|
|
224
|
-
return reply.code(200).send();
|
|
244
|
+
// Opportunistic eviction — drop oldest (insertion-order head) when at cap.
|
|
245
|
+
while (sessionCache.size >= MAX_SESSION_CACHE_ENTRIES) {
|
|
246
|
+
const oldestKey = sessionCache.keys().next().value;
|
|
247
|
+
if (oldestKey === undefined)
|
|
248
|
+
break;
|
|
249
|
+
sessionCache.delete(oldestKey);
|
|
225
250
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
sessionCache.set(cacheKey, validated);
|
|
252
|
+
session = validated;
|
|
253
|
+
}
|
|
254
|
+
// Check access mode
|
|
255
|
+
if (tunnel.accessMode === 'public' || tunnel.accessMode === 'authenticated') {
|
|
256
|
+
return allow(fastify, request, reply, tunnel, session);
|
|
257
|
+
}
|
|
258
|
+
// restricted — check grants
|
|
259
|
+
const result = await checkAccess(session.username, 'tunnel', tunnel.id, {
|
|
260
|
+
adminContact: fastify.getSettings().adminEmail,
|
|
261
|
+
adminName: fastify.getSettings().adminName,
|
|
262
|
+
});
|
|
263
|
+
if (result.allowed) {
|
|
264
|
+
return allow(fastify, request, reply, tunnel, session);
|
|
265
|
+
}
|
|
266
|
+
request.log.info({
|
|
267
|
+
requestId,
|
|
268
|
+
reason: 'no_matching_grant',
|
|
269
|
+
username: session.username,
|
|
270
|
+
resourceType: 'tunnel',
|
|
271
|
+
resourceId: tunnel.id,
|
|
272
|
+
requiredGrant: `tunnel:${tunnel.id}`,
|
|
273
|
+
fqdn: parsed.hostname,
|
|
274
|
+
}, 'authz denied');
|
|
275
|
+
if (fastify.getSettings().accessLoggingEnabled) {
|
|
276
|
+
logAccessRequest({
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
240
278
|
username: session.username,
|
|
241
279
|
resourceType: 'tunnel',
|
|
242
280
|
resourceId: tunnel.id,
|
|
243
|
-
|
|
244
|
-
fqdn: parsed.hostname,
|
|
245
|
-
}, 'authz denied');
|
|
246
|
-
if (fastify.getSettings().accessLoggingEnabled) {
|
|
247
|
-
logAccessRequest({
|
|
248
|
-
timestamp: new Date().toISOString(),
|
|
249
|
-
username: session.username,
|
|
250
|
-
resourceType: 'tunnel',
|
|
251
|
-
resourceId: tunnel.id,
|
|
252
|
-
resourceFqdn: parsed.hostname,
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
// Access denied — return inline HTML page as 403 body.
|
|
256
|
-
// Cache-Control: no-store so nginx never caches the denial; the matching
|
|
257
|
-
// proxy_cache_valid 403 0 in the snippet is the authoritative setting.
|
|
258
|
-
const html = buildAccessRequestPage(session.username, parsed.hostname, {
|
|
259
|
-
adminContact: fastify.getSettings().adminEmail,
|
|
260
|
-
adminName: fastify.getSettings().adminName,
|
|
281
|
+
resourceFqdn: parsed.hostname,
|
|
261
282
|
});
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
283
|
+
}
|
|
284
|
+
// Access denied — return inline HTML page as 403 body.
|
|
285
|
+
// Cache-Control: no-store so nginx never caches the denial; the matching
|
|
286
|
+
// proxy_cache_valid 403 0 in the snippet is the authoritative setting.
|
|
287
|
+
const html = buildAccessRequestPage(session.username, parsed.hostname, {
|
|
288
|
+
adminContact: fastify.getSettings().adminEmail,
|
|
289
|
+
adminName: fastify.getSettings().adminName,
|
|
267
290
|
});
|
|
291
|
+
return reply
|
|
292
|
+
.code(403)
|
|
293
|
+
.header('Cache-Control', 'no-store')
|
|
294
|
+
.type('text/html; charset=utf-8')
|
|
295
|
+
.send(html);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Answer an allowed request: the Authelia identity headers nginx forwards
|
|
299
|
+
* to the tunnel backend.
|
|
300
|
+
*
|
|
301
|
+
* A plugin tunnel's backend is the agent's lamaste-agentd, which takes the
|
|
302
|
+
* user only from a relay assertion — anything that reaches its loopback port
|
|
303
|
+
* could write `Remote-User`. Sign one for this request: the agent, its
|
|
304
|
+
* daemon's port, the plugin route, the method, a nonce. It is single use,
|
|
305
|
+
* so the answer must never be cached.
|
|
306
|
+
*/
|
|
307
|
+
async function allow(fastify, request, reply, tunnel, session) {
|
|
308
|
+
reply.header('X-Gatekeeper-Cache-Version', String(fastify.getCacheVersion()));
|
|
309
|
+
if (!tunnel.plugin) {
|
|
310
|
+
setAutheliaHeaders(reply, session);
|
|
311
|
+
reply.header('Cache-Control', `public, max-age=${AUTHZ_SUCCESS_MAX_AGE_SECONDS}`);
|
|
312
|
+
return reply.code(200).send();
|
|
313
|
+
}
|
|
314
|
+
reply.header('Cache-Control', 'no-store');
|
|
315
|
+
let key = null;
|
|
316
|
+
try {
|
|
317
|
+
key = await fastify.getRelaySigner().current();
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
request.log.error({ err: err instanceof Error ? err.message : String(err) }, 'Relay signing key unusable');
|
|
321
|
+
}
|
|
322
|
+
if (!key) {
|
|
323
|
+
request.log.error({ requestId: request.id, reason: 'relay_signing_key_unavailable', fqdn: tunnel.fqdn }, 'authz failed');
|
|
324
|
+
return reply.code(503).send();
|
|
325
|
+
}
|
|
326
|
+
const originalMethod = request.headers['x-original-method'];
|
|
327
|
+
let assertion;
|
|
328
|
+
try {
|
|
329
|
+
assertion = signRelayAssertion({
|
|
330
|
+
aud: tunnel.plugin.agentLabel,
|
|
331
|
+
port: tunnel.plugin.port,
|
|
332
|
+
caller: {
|
|
333
|
+
type: 'user',
|
|
334
|
+
username: session.username,
|
|
335
|
+
groups: session.groups
|
|
336
|
+
.split(',')
|
|
337
|
+
.map((g) => g.trim())
|
|
338
|
+
.filter((g) => g.length > 0),
|
|
339
|
+
name: session.displayName,
|
|
340
|
+
email: session.email,
|
|
341
|
+
},
|
|
342
|
+
method: typeof originalMethod === 'string' ? originalMethod : 'GET',
|
|
343
|
+
target: { route: tunnel.plugin.route },
|
|
344
|
+
}, key);
|
|
345
|
+
}
|
|
346
|
+
catch (err) {
|
|
347
|
+
// An identity an assertion cannot carry (a username outside
|
|
348
|
+
// [A-Za-z0-9_.-], control characters, ...) — the agent would refuse it.
|
|
349
|
+
request.log.info({
|
|
350
|
+
requestId: request.id,
|
|
351
|
+
reason: 'identity_not_assertable',
|
|
352
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
353
|
+
fqdn: tunnel.fqdn,
|
|
354
|
+
}, 'authz denied');
|
|
355
|
+
return reply.code(403).send();
|
|
356
|
+
}
|
|
357
|
+
setAutheliaHeaders(reply, session);
|
|
358
|
+
reply.header(RELAY_ASSERTION_HEADER, assertion);
|
|
359
|
+
return reply.code(200).send();
|
|
268
360
|
}
|
|
269
361
|
/**
|
|
270
362
|
* Set Authelia identity headers on the response so nginx can
|