@openparachute/hub 0.5.7 → 0.5.10-rc.10
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 +1 -1
- package/src/__tests__/admin-clients.test.ts +275 -0
- package/src/__tests__/admin-handlers.test.ts +70 -323
- package/src/__tests__/admin-host-admin-token.test.ts +52 -4
- package/src/__tests__/api-me.test.ts +149 -0
- package/src/__tests__/api-mint-token.test.ts +381 -0
- package/src/__tests__/api-modules-ops.test.ts +658 -0
- package/src/__tests__/api-modules.test.ts +426 -0
- package/src/__tests__/api-revocation-list.test.ts +198 -0
- package/src/__tests__/api-revoke-token.test.ts +320 -0
- package/src/__tests__/api-tokens.test.ts +629 -0
- package/src/__tests__/auth.test.ts +680 -16
- package/src/__tests__/csrf.test.ts +40 -1
- package/src/__tests__/expose-2fa-warning.test.ts +3 -5
- package/src/__tests__/expose-cloudflare.test.ts +1 -1
- package/src/__tests__/expose.test.ts +2 -2
- package/src/__tests__/hub-server.test.ts +584 -67
- package/src/__tests__/hub-settings.test.ts +377 -0
- package/src/__tests__/hub.test.ts +123 -53
- package/src/__tests__/install-source.test.ts +249 -0
- package/src/__tests__/jwt-sign.test.ts +205 -0
- package/src/__tests__/module-manifest.test.ts +48 -0
- package/src/__tests__/oauth-handlers.test.ts +522 -5
- package/src/__tests__/operator-token.test.ts +427 -3
- package/src/__tests__/origin-check.test.ts +220 -0
- package/src/__tests__/request-protocol.test.ts +54 -0
- package/src/__tests__/serve-boot.test.ts +193 -0
- package/src/__tests__/serve.test.ts +100 -0
- package/src/__tests__/sessions.test.ts +25 -2
- package/src/__tests__/setup-gate.test.ts +222 -0
- package/src/__tests__/setup-wizard.test.ts +2089 -0
- package/src/__tests__/status.test.ts +199 -0
- package/src/__tests__/supervisor.test.ts +482 -0
- package/src/__tests__/upgrade.test.ts +247 -4
- package/src/__tests__/vault-name.test.ts +79 -0
- package/src/__tests__/well-known.test.ts +69 -0
- package/src/admin-clients.ts +139 -0
- package/src/admin-handlers.ts +37 -254
- package/src/admin-host-admin-token.ts +25 -10
- package/src/admin-login-ui.ts +256 -0
- package/src/admin-vault-admin-token.ts +1 -1
- package/src/api-me.ts +124 -0
- package/src/api-mint-token.ts +239 -0
- package/src/api-modules-ops.ts +585 -0
- package/src/api-modules.ts +367 -0
- package/src/api-revocation-list.ts +59 -0
- package/src/api-revoke-token.ts +153 -0
- package/src/api-tokens.ts +224 -0
- package/src/cli.ts +28 -0
- package/src/commands/auth.ts +408 -51
- package/src/commands/expose-2fa-warning.ts +6 -6
- package/src/commands/serve-boot.ts +133 -0
- package/src/commands/serve.ts +214 -0
- package/src/commands/status.ts +74 -10
- package/src/commands/upgrade.ts +33 -6
- package/src/csrf.ts +34 -13
- package/src/help.ts +55 -5
- package/src/hub-control.ts +1 -0
- package/src/hub-db.ts +87 -0
- package/src/hub-server.ts +767 -136
- package/src/hub-settings.ts +259 -0
- package/src/hub.ts +298 -150
- package/src/install-source.ts +291 -0
- package/src/jwt-sign.ts +265 -5
- package/src/module-manifest.ts +48 -10
- package/src/oauth-handlers.ts +262 -56
- package/src/oauth-ui.ts +23 -2
- package/src/operator-token.ts +349 -18
- package/src/origin-check.ts +127 -0
- package/src/rate-limit.ts +5 -2
- package/src/request-protocol.ts +48 -0
- package/src/scope-explanations.ts +33 -2
- package/src/sessions.ts +30 -18
- package/src/setup-wizard.ts +2009 -0
- package/src/supervisor.ts +411 -0
- package/src/vault-name.ts +71 -0
- package/src/well-known.ts +54 -1
- package/web/ui/dist/assets/index-BDSEsaBY.css +1 -0
- package/web/ui/dist/assets/index-CP07NbdF.js +61 -0
- package/web/ui/dist/index.html +2 -2
- package/src/__tests__/admin-config.test.ts +0 -281
- package/src/admin-config-ui.ts +0 -534
- package/src/admin-config.ts +0 -226
- package/web/ui/dist/assets/index-BKzPDdB0.js +0 -60
- package/web/ui/dist/assets/index-Dyk6g7vT.css +0 -1
|
@@ -66,6 +66,30 @@ export const SCOPE_EXPLANATIONS: Record<string, ScopeExplanation> = {
|
|
|
66
66
|
"Provision and manage vaults across this host (create new vaults, configure cross-vault settings).",
|
|
67
67
|
level: "admin",
|
|
68
68
|
},
|
|
69
|
+
// Fine-grained host scopes (#213) — the `parachute auth rotate-operator
|
|
70
|
+
// --scope-set <set>` vocabulary. Each is a narrowing of `parachute:host:admin`:
|
|
71
|
+
// an operator who wants a tighter token mints with one of these and uses
|
|
72
|
+
// it in place of the broad operator.token. Operator-only (non-requestable).
|
|
73
|
+
"parachute:host:install": {
|
|
74
|
+
label: "Install or upgrade Parachute modules on this host.",
|
|
75
|
+
level: "admin",
|
|
76
|
+
},
|
|
77
|
+
"parachute:host:start": {
|
|
78
|
+
label: "Lifecycle Parachute modules on this host (start, stop, restart, status).",
|
|
79
|
+
level: "admin",
|
|
80
|
+
},
|
|
81
|
+
"parachute:host:expose": {
|
|
82
|
+
label: "Bring tailnet or public exposure layers up and down on this host.",
|
|
83
|
+
level: "admin",
|
|
84
|
+
},
|
|
85
|
+
"parachute:host:auth": {
|
|
86
|
+
label: "Mint hub-issued tokens and manage user accounts on this host.",
|
|
87
|
+
level: "admin",
|
|
88
|
+
},
|
|
89
|
+
"parachute:host:vault": {
|
|
90
|
+
label: "Administer vaults on this host (create, configure, delete).",
|
|
91
|
+
level: "admin",
|
|
92
|
+
},
|
|
69
93
|
};
|
|
70
94
|
|
|
71
95
|
/**
|
|
@@ -83,7 +107,7 @@ export const FIRST_PARTY_SCOPES = Object.keys(SCOPE_EXPLANATIONS).sort();
|
|
|
83
107
|
* - `parachute auth rotate-operator` writes the long-lived operator token
|
|
84
108
|
* (`~/.parachute/operator.token`, mode 0600) for service accounts.
|
|
85
109
|
* - `GET /admin/host-admin-token` exchanges a valid `parachute_hub_session`
|
|
86
|
-
* cookie (set by `/
|
|
110
|
+
* cookie (set by `/login` after a password check) for a
|
|
87
111
|
* short-lived JWT consumed by the in-tree vault-management SPA.
|
|
88
112
|
*
|
|
89
113
|
* Both surfaces predicate on local-operator identity that the public OAuth
|
|
@@ -104,7 +128,14 @@ export const FIRST_PARTY_SCOPES = Object.keys(SCOPE_EXPLANATIONS).sort();
|
|
|
104
128
|
* intentional: the blast radius of compromised cross-vault admin doesn't
|
|
105
129
|
* justify third-party requestability.
|
|
106
130
|
*/
|
|
107
|
-
export const NON_REQUESTABLE_SCOPES: ReadonlySet<string> = new Set([
|
|
131
|
+
export const NON_REQUESTABLE_SCOPES: ReadonlySet<string> = new Set([
|
|
132
|
+
"parachute:host:admin",
|
|
133
|
+
"parachute:host:install",
|
|
134
|
+
"parachute:host:start",
|
|
135
|
+
"parachute:host:expose",
|
|
136
|
+
"parachute:host:auth",
|
|
137
|
+
"parachute:host:vault",
|
|
138
|
+
]);
|
|
108
139
|
|
|
109
140
|
/**
|
|
110
141
|
* Per-vault `vault:<name>:admin` scopes are also non-requestable: they let
|
package/src/sessions.ts
CHANGED
|
@@ -83,26 +83,38 @@ export function deleteSession(db: Database, id: string): void {
|
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Build a `Set-Cookie` header value for the given session id. HttpOnly +
|
|
86
|
-
* SameSite=Lax + Secure (
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* into this hub", and admin pages outside /oauth/ (config portal, etc.)
|
|
90
|
-
* the same session. State-changing admin POSTs require a CSRF token
|
|
91
|
-
* src/csrf.ts) since SameSite=Lax alone doesn't prevent same-site
|
|
86
|
+
* SameSite=Lax + Secure (conditional) + Path=/.
|
|
87
|
+
*
|
|
88
|
+
* Path=/ covers the whole hub origin: the operator's session is "logged
|
|
89
|
+
* into this hub", and admin pages outside /oauth/ (config portal, etc.)
|
|
90
|
+
* ride the same session. State-changing admin POSTs require a CSRF token
|
|
91
|
+
* (see src/csrf.ts) since SameSite=Lax alone doesn't prevent same-site
|
|
92
|
+
* CSRF.
|
|
93
|
+
*
|
|
94
|
+
* `Secure` defaults to true (production behind a TLS terminator).
|
|
95
|
+
* Callers minting the cookie for a known-HTTP request — `/login` POST
|
|
96
|
+
* over `http://localhost:1939`, the wizard's account POST same — pass
|
|
97
|
+
* `secure: false` (computed from `isHttpsRequest(req)`) so the
|
|
98
|
+
* browser actually keeps the cookie. Setting `Secure` unconditionally
|
|
99
|
+
* over plain HTTP silently drops the cookie and breaks the very next
|
|
100
|
+
* authenticated request.
|
|
92
101
|
*/
|
|
93
|
-
export function buildSessionCookie(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
export function buildSessionCookie(
|
|
103
|
+
sessionId: string,
|
|
104
|
+
maxAgeSeconds: number,
|
|
105
|
+
opts: { secure?: boolean } = {},
|
|
106
|
+
): string {
|
|
107
|
+
const parts = [`${SESSION_COOKIE_NAME}=${sessionId}`, "HttpOnly"];
|
|
108
|
+
if (opts.secure !== false) parts.push("Secure");
|
|
109
|
+
parts.push("SameSite=Lax", "Path=/", `Max-Age=${maxAgeSeconds}`);
|
|
110
|
+
return parts.join("; ");
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
export function buildSessionClearCookie(): string {
|
|
105
|
-
|
|
113
|
+
export function buildSessionClearCookie(opts: { secure?: boolean } = {}): string {
|
|
114
|
+
const parts = [`${SESSION_COOKIE_NAME}=`, "HttpOnly"];
|
|
115
|
+
if (opts.secure !== false) parts.push("Secure");
|
|
116
|
+
parts.push("SameSite=Lax", "Path=/", "Max-Age=0");
|
|
117
|
+
return parts.join("; ");
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
export function parseSessionCookie(cookieHeader: string | null): string | null {
|
|
@@ -121,7 +133,7 @@ export function parseSessionCookie(cookieHeader: string | null): string | null {
|
|
|
121
133
|
* callers don't repeat the parse+find+null-check dance.
|
|
122
134
|
*
|
|
123
135
|
* Caller decides what to do on null — admin pages redirect to
|
|
124
|
-
* `/
|
|
136
|
+
* `/login?next=<path>`, OAuth's DCR endpoint falls through to
|
|
125
137
|
* status=`pending` (closes #199).
|
|
126
138
|
*/
|
|
127
139
|
export function findActiveSession(
|