lucifer-gate 0.7.4 → 0.8.0-alpha.1.02835cd
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/README.md +55 -240
- package/dist/server/create_app.js +83 -1
- package/dist/server/create_app.js.map +1 -1
- package/dist/server/domains/request-proxy/config/proxy_config.js +21 -0
- package/dist/server/domains/request-proxy/config/proxy_config.js.map +1 -1
- package/dist/server/domains/request-proxy/service/proxy_approval_cache.js +48 -0
- package/dist/server/domains/request-proxy/service/proxy_approval_cache.js.map +1 -0
- package/dist/server/domains/request-proxy/service/proxy_auth.js +220 -0
- package/dist/server/domains/request-proxy/service/proxy_auth.js.map +1 -0
- package/dist/server/domains/request-proxy/service/proxy_server.js +79 -13
- package/dist/server/domains/request-proxy/service/proxy_server.js.map +1 -1
- package/dist/server/domains/request-proxy/types/proxy_types.js +2 -0
- package/dist/server/domains/request-proxy/types/proxy_types.js.map +1 -1
- package/dist/server/test/integration-setup.js +3 -1
- package/dist/server/test/integration-setup.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
AI agent command firewall with Telegram-based human approval.
|
|
4
4
|
|
|
5
|
-
Lucifer sits between your AI agent and the shell. It authenticates callers via
|
|
5
|
+
Lucifer sits between your AI agent and the shell. It authenticates callers via
|
|
6
|
+
API keys, matches commands against a policy file, and gates unknown commands
|
|
7
|
+
through Telegram for human approval. Approved commands build up a permission
|
|
8
|
+
library over time. Think "sudo via Telegram."
|
|
6
9
|
|
|
7
10
|
## Quick start (2 minutes, no Telegram needed)
|
|
8
11
|
|
|
@@ -22,185 +25,47 @@ curl -X POST http://localhost:3001/api/v1/execute \
|
|
|
22
25
|
|
|
23
26
|
## How it works
|
|
24
27
|
|
|
25
|
-
1. Caller sends `POST /api/v1/execute` with API key + command
|
|
26
|
-
2. Lucifer authenticates the key and checks IP allowlist
|
|
28
|
+
1. Caller sends `POST /api/v1/execute` with API key + command.
|
|
29
|
+
2. Lucifer authenticates the key and checks the IP allowlist.
|
|
27
30
|
3. Command is matched against `config/command-rules.json`:
|
|
28
|
-
- `always_approve`
|
|
29
|
-
- `always_deny`
|
|
30
|
-
- `manual_approve`
|
|
31
|
-
4. If Telegram approval needed, human sees the command with risk warnings
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
**config/api-keys.json** - API key definitions (hashed, with optional IP allowlists)
|
|
41
|
-
|
|
42
|
-
**config/command-rules.json** - Command policy:
|
|
43
|
-
```json
|
|
44
|
-
{
|
|
45
|
-
"rules": [
|
|
46
|
-
{ "prefix": "echo ", "action": "always_approve" },
|
|
47
|
-
{ "prefix": "git pull", "action": "manual_approve" },
|
|
48
|
-
{ "prefix": "rm ", "action": "always_deny" }
|
|
49
|
-
],
|
|
50
|
-
"defaultAction": "always_deny"
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Rules matched top-to-bottom, first match wins.
|
|
55
|
-
|
|
56
|
-
### Command aliases (optional)
|
|
57
|
-
|
|
58
|
-
`config/lucifer.json` may include an `aliases` map that points a name at a
|
|
59
|
-
script or executable on disk. When the incoming command matches an alias name
|
|
60
|
-
exactly, Lucifer runs the referenced file directly (no shell) with the
|
|
61
|
-
script's parent directory as the working directory. Callers supplying a `cwd`
|
|
62
|
-
have it ignored for alias invocations. If the name does not match an alias,
|
|
63
|
-
execution falls back to the normal shell path.
|
|
64
|
-
|
|
65
|
-
```json
|
|
66
|
-
{
|
|
67
|
-
"aliases": {
|
|
68
|
-
"deploy": { "path": "/opt/ops/deploy.sh", "type": "bash" },
|
|
69
|
-
"healthz": { "path": "/opt/ops/bin/healthz", "type": "elf" }
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
Supported `type` values:
|
|
75
|
-
|
|
76
|
-
- `bash` — launched via `bash -- <path>`. The `--` prevents a path that
|
|
77
|
-
happens to start with `-` from being interpreted as a bash option.
|
|
78
|
-
- `elf` — launched directly (must be executable and on a filesystem without
|
|
79
|
-
`noexec`).
|
|
80
|
-
|
|
81
|
-
Relative alias `path` values are resolved against the **config file's
|
|
82
|
-
directory**, not the daemon's working directory. So `"./scripts/deploy.sh"`
|
|
83
|
-
in `config/lucifer.json` always means `config/scripts/deploy.sh` regardless
|
|
84
|
-
of where the server was started from. Absolute paths are used as-is.
|
|
85
|
-
|
|
86
|
-
Command rules still apply to the alias name as sent by the caller (so you can
|
|
87
|
-
e.g. put `{ "prefix": "deploy", "action": "manual_approve" }` in
|
|
88
|
-
`command-rules.json`). Exact-string match only — an alias `deploy` does not
|
|
89
|
-
match `deploy --dry-run`; that falls through to the shell path.
|
|
90
|
-
|
|
91
|
-
### Transparent HTTP proxy (optional)
|
|
92
|
-
|
|
93
|
-
Lucifer can also run one or more transparent HTTP proxy listeners alongside
|
|
94
|
-
the main gateway. Each listener forwards every request it receives to a
|
|
95
|
-
configured upstream, preserving path, method, query string and body, and
|
|
96
|
-
injecting a fixed set of outgoing headers.
|
|
97
|
-
|
|
98
|
-
Drop `config/proxy-config.json`:
|
|
99
|
-
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"proxies": [
|
|
103
|
-
{
|
|
104
|
-
"port": 6060,
|
|
105
|
-
"baseUrl": "https://api.openai.com",
|
|
106
|
-
"headers": { "Authorization": "Bearer sk-..." }
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
A request to `http://localhost:6060/v1/chat/completions` is forwarded to
|
|
113
|
-
`https://api.openai.com/v1/chat/completions` with the configured
|
|
114
|
-
`Authorization` header attached server-side. Configured headers **overwrite**
|
|
115
|
-
any caller-supplied header of the same name, so callers never need (and
|
|
116
|
-
cannot spoof) the upstream credential.
|
|
117
|
-
|
|
118
|
-
File semantics:
|
|
119
|
-
|
|
120
|
-
- File missing → feature disabled (no behavior change for existing installs).
|
|
121
|
-
- File present with `proxies: []` → feature enabled, no listeners started.
|
|
122
|
-
- Ports must be in 1–65535 and must not collide with the gateway port or
|
|
123
|
-
with each other — validated at startup.
|
|
124
|
-
- `baseUrl` must be a syntactically valid `http://` or `https://` URL.
|
|
125
|
-
- Listeners bind to `127.0.0.1` by default. Set `"host": "0.0.0.0"` on a
|
|
126
|
-
mapping to expose it beyond loopback; when doing so, the operator is
|
|
127
|
-
responsible for fronting the listener with access control.
|
|
128
|
-
|
|
129
|
-
See [docs/specs/transparent-proxy.md](docs/specs/transparent-proxy.md) for
|
|
130
|
-
the full contract.
|
|
31
|
+
- `always_approve` → execute immediately
|
|
32
|
+
- `always_deny` → reject (`403`)
|
|
33
|
+
- `manual_approve` → check SQLite for a cached approval, or send to Telegram
|
|
34
|
+
4. If Telegram approval is needed, a human sees the command with risk warnings
|
|
35
|
+
and taps a button.
|
|
36
|
+
5. Approved commands execute and results are returned to the caller.
|
|
37
|
+
|
|
38
|
+
See [docs/specs/command-execution.md](docs/specs/command-execution.md) for the
|
|
39
|
+
full API contract (request shape, success response, error codes) and
|
|
40
|
+
[docs/specs/approval-channels.md](docs/specs/approval-channels.md) for how
|
|
41
|
+
Telegram, the web admin UI, and auto-approve interact.
|
|
131
42
|
|
|
132
43
|
## Production setup (with Telegram)
|
|
133
44
|
|
|
134
|
-
1. Create a Telegram bot via [@BotFather](https://t.me/BotFather) and get the
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate pair --config ./config/lucifer.json
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The pairing wizard will:
|
|
143
|
-
- Validate your bot token
|
|
144
|
-
- List all chats that have messaged the bot (most recent first)
|
|
145
|
-
- Let you pick which chat to use for approvals
|
|
146
|
-
- Send a 6-digit verification code to that chat
|
|
147
|
-
- Ask you to enter the code to prove you can read the chat
|
|
148
|
-
- Save the chat ID to `config/lucifer.json`
|
|
149
|
-
|
|
150
|
-
4. Start the server (no `LUCIFER_TELEGRAM_CHAT_ID` env var needed — it's in the config):
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate --config ./config/lucifer.json
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
> **Manual alternative:** You can still set `LUCIFER_TELEGRAM_CHAT_ID` as an environment variable instead of using `pair`. The env var takes precedence over the config file value.
|
|
45
|
+
1. Create a Telegram bot via [@BotFather](https://t.me/BotFather) and get the
|
|
46
|
+
token.
|
|
47
|
+
2. Send any message to your bot from the chat you want to use for approvals.
|
|
48
|
+
3. Pair the chat:
|
|
157
49
|
|
|
158
|
-
|
|
50
|
+
```bash
|
|
51
|
+
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate pair --config ./config/lucifer.json
|
|
52
|
+
```
|
|
159
53
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
[Exact 2h] [Exact 8h] [Exact forever]
|
|
165
|
-
["git push" 2h] ["git push" 8h]
|
|
166
|
-
[Deny]
|
|
167
|
-
```
|
|
54
|
+
The wizard lists chats that have messaged the bot, sends a 6-digit
|
|
55
|
+
verification code, and writes the chosen chat ID into `lucifer.json`.
|
|
56
|
+
4. Start the server:
|
|
168
57
|
|
|
169
|
-
|
|
58
|
+
```bash
|
|
59
|
+
LUCIFER_TELEGRAM_TOKEN=your_bot_token npx lucifer-gate --config ./config/lucifer.json
|
|
60
|
+
```
|
|
170
61
|
|
|
171
|
-
|
|
62
|
+
See [docs/specs/operator-workflows.md](docs/specs/operator-workflows.md) for
|
|
63
|
+
the full pairing, `--init`, `log`, and `stats` contract.
|
|
172
64
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
curl -X POST http://localhost:3001/api/v1/execute \
|
|
179
|
-
-H "Content-Type: application/json" \
|
|
180
|
-
-H "x-api-key: luc_yourkey" \
|
|
181
|
-
-d '{"command":"git status"}'
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Success response:
|
|
185
|
-
```json
|
|
186
|
-
{ "requestId": "uuid", "status": "completed", "exitCode": 0, "stdout": "...", "stderr": "", "durationMs": 42 }
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Status values observable on success/failure paths: `completed`, `failed`,
|
|
190
|
-
`denied`, `timed_out`.
|
|
191
|
-
|
|
192
|
-
### Error responses
|
|
193
|
-
|
|
194
|
-
All errors return:
|
|
195
|
-
```json
|
|
196
|
-
{ "code": "ERROR_CODE", "message": "Human readable", "retryable": true }
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Codes: `MISSING_API_KEY`, `INVALID_API_KEY`, `IP_NOT_ALLOWED`, `RATE_LIMITED`,
|
|
200
|
-
`COMMAND_DENIED`, `COMMAND_TOO_LONG`, `INVALID_CWD`, `DENIED`,
|
|
201
|
-
`DUPLICATE_IN_FLIGHT` (an identical command from this API key is already
|
|
202
|
-
awaiting approval — retry after it settles), `APPROVAL_TIMEOUT`,
|
|
203
|
-
`APPROVAL_ERROR`.
|
|
65
|
+
When a `manual_approve` command arrives, an inline keyboard appears in
|
|
66
|
+
Telegram with buttons for exact / prefix approval and lifetimes of 2h, 8h,
|
|
67
|
+
or permanent, plus a Deny button. Approval shapes and storage semantics live
|
|
68
|
+
in [docs/specs/approval-channels.md](docs/specs/approval-channels.md).
|
|
204
69
|
|
|
205
70
|
## CLI
|
|
206
71
|
|
|
@@ -213,83 +78,33 @@ lucifer-gate log [--limit N] Query audit log
|
|
|
213
78
|
lucifer-gate stats Show approval statistics
|
|
214
79
|
```
|
|
215
80
|
|
|
216
|
-
## Docker
|
|
81
|
+
## Configuration, logging, Docker, env vars
|
|
217
82
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
-e LUCIFER_TELEGRAM_TOKEN=your_token \
|
|
222
|
-
-e LUCIFER_TELEGRAM_CHAT_ID=your_chat_id \
|
|
223
|
-
-v ./config:/app/config \
|
|
224
|
-
-v ./data:/app/data \
|
|
225
|
-
lucifer-gate
|
|
226
|
-
```
|
|
83
|
+
All operator-side configuration — file locations, environment variables,
|
|
84
|
+
logging setup, and the Docker recipe — lives in
|
|
85
|
+
[docs/CONFIGURATION.md](docs/CONFIGURATION.md).
|
|
227
86
|
|
|
228
|
-
|
|
87
|
+
Per-feature contracts live under [docs/specs/](docs/specs/):
|
|
229
88
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
"logFile": "lucifer.log",
|
|
241
|
-
...
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
The `logFile` path is relative to `dataDir` (default: `data/lucifer.log`). Remove the key to disable file logging. The `--init` command generates the config with file logging enabled.
|
|
246
|
-
|
|
247
|
-
**Log levels** (controlled via `LOG_LEVEL` env var):
|
|
248
|
-
- `debug` — verbose output (default in development)
|
|
249
|
-
- `info` — normal operations (default in production, when `NODE_ENV=production`)
|
|
250
|
-
- `warn` — potential issues
|
|
251
|
-
- `error` — failures only
|
|
252
|
-
|
|
253
|
-
Example:
|
|
254
|
-
```bash
|
|
255
|
-
LOG_LEVEL=info LUCIFER_TELEGRAM_TOKEN=your_token npx lucifer-gate --config ./config/lucifer.json
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Environment variables
|
|
259
|
-
|
|
260
|
-
| Variable | Required | Description |
|
|
261
|
-
|---|---|---|
|
|
262
|
-
| `LUCIFER_TELEGRAM_TOKEN` | Yes (prod) | Telegram bot token from @BotFather |
|
|
263
|
-
| `LUCIFER_TELEGRAM_CHAT_ID` | No | Telegram chat ID (use `pair` command instead, or set manually) |
|
|
264
|
-
| `LUCIFER_ADMIN_SECRET` | No | Bearer token for admin endpoints |
|
|
265
|
-
| `PORT` | No | Server port (default: 3001) |
|
|
266
|
-
| `LOG_LEVEL` | No | Log level: `debug`, `info`, `warn`, `error` (default: `debug` / `info` in production) |
|
|
267
|
-
| `NODE_ENV` | No | Set to `production` for production defaults (info log level, no pretty-printing) |
|
|
268
|
-
|
|
269
|
-
## Architecture
|
|
270
|
-
|
|
271
|
-
```
|
|
272
|
-
server/src/domains/
|
|
273
|
-
command-gateway/ Core domain
|
|
274
|
-
types/ CommandRequest, Approval, types
|
|
275
|
-
config/ Gateway configuration
|
|
276
|
-
repository/ SQLite stores, JSON config readers
|
|
277
|
-
service/ Auth, rules, risk analysis, execution, approvals
|
|
278
|
-
api/ Execute routes + web approval UI routes
|
|
279
|
-
platform-api/ Health endpoint + server runtime wiring
|
|
280
|
-
request-proxy/ Optional transparent HTTP proxy listeners
|
|
281
|
-
types/ ProxyMapping, ProxyConfig
|
|
282
|
-
config/ proxy-config.json loader + port-collision validation
|
|
283
|
-
service/ http-proxy-middleware-backed listeners
|
|
284
|
-
```
|
|
89
|
+
| Topic | Source of truth |
|
|
90
|
+
|---|---|
|
|
91
|
+
| Execute API & error codes | [specs/command-execution.md](docs/specs/command-execution.md) |
|
|
92
|
+
| Command aliases | [specs/command-execution.md#aliases](docs/specs/command-execution.md#aliases) |
|
|
93
|
+
| Approval channels (Telegram, web admin, auto-approve) | [specs/approval-channels.md](docs/specs/approval-channels.md) |
|
|
94
|
+
| Operator workflows (init, pair, start, audit) | [specs/operator-workflows.md](docs/specs/operator-workflows.md) |
|
|
95
|
+
| Transparent HTTP proxy | [specs/transparent-proxy.md](docs/specs/transparent-proxy.md) |
|
|
96
|
+
| Platform health | [specs/platform-health.md](docs/specs/platform-health.md) |
|
|
97
|
+
| User journeys | [specs/USER-JOURNEYS.md](docs/specs/USER-JOURNEYS.md) |
|
|
98
|
+
| Architecture map | [architecture/ARCHITECTURE.md](docs/architecture/ARCHITECTURE.md) |
|
|
285
99
|
|
|
286
|
-
|
|
100
|
+
For agent-specific instructions (task lifecycle, review checklist, skills),
|
|
101
|
+
start at [AGENTS.md](AGENTS.md).
|
|
287
102
|
|
|
288
103
|
## Stack
|
|
289
104
|
|
|
290
105
|
- Express 5 + TypeScript
|
|
291
|
-
- SQLite (better-sqlite3) for approvals + audit
|
|
106
|
+
- SQLite (better-sqlite3) for approvals + audit log
|
|
292
107
|
- Telegraf for Telegram bot
|
|
293
108
|
- Optional server-delivered web approval UI with SSE updates
|
|
294
|
-
- Pino for structured logging (pino-pretty for human-readable console
|
|
109
|
+
- Pino for structured logging (pino-pretty for human-readable dev console)
|
|
295
110
|
- Vitest for testing
|
|
@@ -48,6 +48,82 @@ function initApprovalChannel(deps, autoApprove, telegramApiRoot) {
|
|
|
48
48
|
}
|
|
49
49
|
return channels.length === 1 ? channels[0] : createMultiApprovalChannel(channels);
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Adapter from the command-gateway ApiKeyStore to the proxy-domain
|
|
53
|
+
* ProxyTokenValidator contract. Kept in this composition file so
|
|
54
|
+
* `request-proxy` never imports from `command-gateway`.
|
|
55
|
+
*/
|
|
56
|
+
function createProxyTokenValidator(apiKeyStore) {
|
|
57
|
+
return {
|
|
58
|
+
validate(rawToken) {
|
|
59
|
+
const entry = apiKeyStore.findByKey(rawToken);
|
|
60
|
+
if (!entry)
|
|
61
|
+
return undefined;
|
|
62
|
+
return { keyId: entry.id, keyName: entry.name };
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Adapter from the command-gateway ApprovalChannel to the proxy-domain
|
|
68
|
+
* ProxyApprovalRequester contract. Synthesises a human-readable descriptor
|
|
69
|
+
* as the "command" string and races against an approval timeout so the
|
|
70
|
+
* proxy request cannot block indefinitely waiting for a human.
|
|
71
|
+
*/
|
|
72
|
+
function createProxyApprovalRequester(approvalChannel, approvalTimeoutSeconds) {
|
|
73
|
+
return {
|
|
74
|
+
async request(ctx) {
|
|
75
|
+
const descriptor = `HTTP proxy ${ctx.method} ${ctx.path} (port ${ctx.port}) by ${ctx.keyName}`;
|
|
76
|
+
let timer;
|
|
77
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
78
|
+
timer = setTimeout(() => resolve('timeout'), approvalTimeoutSeconds * 1000);
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
const approvalPromise = approvalChannel
|
|
82
|
+
.requestApproval(descriptor, ctx.keyName, ctx.ip, ctx.requestId, { level: 'safe', warnings: [] })
|
|
83
|
+
.then((r) => (r.decision === 'approved' ? 'approved' : 'denied'));
|
|
84
|
+
const outcome = await Promise.race([approvalPromise, timeoutPromise]);
|
|
85
|
+
if (outcome === 'timeout') {
|
|
86
|
+
approvalChannel.cancel?.(ctx.requestId);
|
|
87
|
+
}
|
|
88
|
+
return outcome;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
log.warn({ err, requestId: ctx.requestId }, 'Proxy approval channel threw');
|
|
92
|
+
return 'error';
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (timer)
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Adapter from proxy audit events to the command-gateway audit log, so
|
|
103
|
+
* proxy auth/approval activity shows up in `lucifer-gate log` alongside
|
|
104
|
+
* command activity.
|
|
105
|
+
*/
|
|
106
|
+
function createProxyAuditSink(auditLog) {
|
|
107
|
+
return {
|
|
108
|
+
record(event) {
|
|
109
|
+
try {
|
|
110
|
+
auditLog.append({
|
|
111
|
+
ts: event.ts,
|
|
112
|
+
type: event.type,
|
|
113
|
+
requestId: event.requestId,
|
|
114
|
+
command: `HTTP proxy ${event.method} ${event.path} (port ${event.port})`,
|
|
115
|
+
apiKeyName: event.keyName,
|
|
116
|
+
ip: event.ip,
|
|
117
|
+
approvedBy: event.source,
|
|
118
|
+
error: event.reason,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
log.warn({ err }, 'Failed to write proxy audit event');
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
51
127
|
export function createApp(options = {}) {
|
|
52
128
|
const serverConfig = getServerConfig();
|
|
53
129
|
const metadataRepository = createRuntimeMetadataRepository();
|
|
@@ -73,6 +149,7 @@ export function createApp(options = {}) {
|
|
|
73
149
|
}
|
|
74
150
|
let approvalChannel;
|
|
75
151
|
let cleanupInterval;
|
|
152
|
+
const proxyDeps = {};
|
|
76
153
|
// Only initialize gateway if config files exist
|
|
77
154
|
if (fs.existsSync(apiKeysPath) && fs.existsSync(commandRulesPath)) {
|
|
78
155
|
const db = getDatabase(gatewayConfig.dataDir);
|
|
@@ -91,6 +168,11 @@ export function createApp(options = {}) {
|
|
|
91
168
|
approvalStore.removeExpired();
|
|
92
169
|
pendingStore.cleanup(gatewayConfig.approvalTimeoutSeconds * 1000);
|
|
93
170
|
}, 60_000);
|
|
171
|
+
// Wire the proxy bridges over gateway stores so request-proxy stays
|
|
172
|
+
// isolated from command-gateway code (Dependency Rules).
|
|
173
|
+
proxyDeps.tokenValidator = createProxyTokenValidator(apiKeyStore);
|
|
174
|
+
proxyDeps.approvalRequester = createProxyApprovalRequester(approvalChannel, gatewayConfig.approvalTimeoutSeconds);
|
|
175
|
+
proxyDeps.auditSink = createProxyAuditSink(auditLog);
|
|
94
176
|
log.info('Command gateway initialized');
|
|
95
177
|
}
|
|
96
178
|
else {
|
|
@@ -105,7 +187,7 @@ export function createApp(options = {}) {
|
|
|
105
187
|
const proxyConfig = loadProxyConfig(proxyConfigPath);
|
|
106
188
|
if (proxyConfig && proxyConfig.proxies.length > 0) {
|
|
107
189
|
validateProxyPorts(proxyConfig.proxies, gatewayConfig.port);
|
|
108
|
-
proxyServers = createProxyServers(proxyConfig.proxies);
|
|
190
|
+
proxyServers = createProxyServers(proxyConfig.proxies, proxyDeps);
|
|
109
191
|
log.info({ count: proxyConfig.proxies.length }, 'Transparent proxy mappings configured');
|
|
110
192
|
}
|
|
111
193
|
async function start() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create_app.js","sourceRoot":"","sources":["../../server/src/create_app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,OAAO,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAA;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAA;AAC3F,OAAO,EAAE,+BAA+B,EAAE,MAAM,kEAAkE,CAAA;AAClH,OAAO,EAAE,yBAAyB,EAAE,MAAM,wDAAwD,CAAA;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kDAAkD,CAAA;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,wDAAwD,CAAA;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,mDAAmD,CAAA;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAA;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,6DAA6D,CAAA;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,+DAA+D,CAAA;AACzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,0DAA0D,CAAA;AAChG,OAAO,EAAE,6BAA6B,EAAE,MAAM,gEAAgE,CAAA;AAC9G,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAA;AACpG,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAA;AACpG,OAAO,EAAE,0BAA0B,EAAE,MAAM,6DAA6D,CAAA;AACxG,OAAO,EAAE,sBAAsB,EAAE,MAAM,2DAA2D,CAAA;AAElG,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAA;AACpG,OAAO,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"create_app.js","sourceRoot":"","sources":["../../server/src/create_app.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,9 @@ function isValidHeaders(value) {
|
|
|
20
20
|
return false;
|
|
21
21
|
return Object.values(value).every((v) => typeof v === 'string');
|
|
22
22
|
}
|
|
23
|
+
function isValidAuthMode(value) {
|
|
24
|
+
return value === 'none' || value === 'api-key' || value === 'api-key-telegram';
|
|
25
|
+
}
|
|
23
26
|
function isProxyMapping(value) {
|
|
24
27
|
if (typeof value !== 'object' || value === null)
|
|
25
28
|
return false;
|
|
@@ -32,6 +35,24 @@ function isProxyMapping(value) {
|
|
|
32
35
|
return false;
|
|
33
36
|
if (m.host !== undefined && (typeof m.host !== 'string' || m.host.length === 0))
|
|
34
37
|
return false;
|
|
38
|
+
if (m.authMode !== undefined && !isValidAuthMode(m.authMode))
|
|
39
|
+
return false;
|
|
40
|
+
if (m.apiKeyHeader !== undefined && (typeof m.apiKeyHeader !== 'string' || m.apiKeyHeader.length === 0))
|
|
41
|
+
return false;
|
|
42
|
+
if (m.apiKeyPrefix !== undefined && typeof m.apiKeyPrefix !== 'string')
|
|
43
|
+
return false;
|
|
44
|
+
if (m.telegramApprovalTtlSeconds !== undefined &&
|
|
45
|
+
(typeof m.telegramApprovalTtlSeconds !== 'number'
|
|
46
|
+
|| !Number.isInteger(m.telegramApprovalTtlSeconds)
|
|
47
|
+
|| m.telegramApprovalTtlSeconds < 0)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// When auth is required, the caller header name is mandatory. Fail fast at
|
|
51
|
+
// load time rather than at the first request — the operator wants a
|
|
52
|
+
// descriptive startup error, not a 500 later.
|
|
53
|
+
if (m.authMode !== undefined && m.authMode !== 'none' && m.apiKeyHeader === undefined) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
35
56
|
return true;
|
|
36
57
|
}
|
|
37
58
|
function isProxyConfig(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy_config.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/config/proxy_config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC;AAC9F,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtF,OAAO,MAAM,CAAC,MAAM,CAAC,KAAgC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACxE,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy_config.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/config/proxy_config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory TTL cache for "approved" proxy decisions, keyed by the tuple
|
|
3
|
+
* `(api-key-id, port)`. A cache hit means the token-holder has an
|
|
4
|
+
* unexpired Telegram approval for this specific proxy listener, so the
|
|
5
|
+
* next request is forwarded without asking the approver again.
|
|
6
|
+
*
|
|
7
|
+
* Why in-memory (and not SQLite, like command approvals): proxy traffic is
|
|
8
|
+
* expected to be high-volume and the approval is scoped to a running agent
|
|
9
|
+
* session. Survival across restarts is a non-goal for v1 — an operator
|
|
10
|
+
* restart should re-prompt, which is the conservative default. Persisting
|
|
11
|
+
* proxy approvals can be added later without changing the cache API.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Construct a cache. `now` is injectable so tests can advance time
|
|
15
|
+
* without using real timers.
|
|
16
|
+
*/
|
|
17
|
+
export function createProxyApprovalCache(now = Date.now) {
|
|
18
|
+
const entries = new Map();
|
|
19
|
+
function cacheKey(key) {
|
|
20
|
+
return `${key.keyId}::${key.port}`;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
has(key) {
|
|
24
|
+
const expiresAt = entries.get(cacheKey(key));
|
|
25
|
+
if (expiresAt === undefined)
|
|
26
|
+
return false;
|
|
27
|
+
if (expiresAt <= now()) {
|
|
28
|
+
entries.delete(cacheKey(key));
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
set(key, ttlSeconds) {
|
|
34
|
+
if (ttlSeconds <= 0) {
|
|
35
|
+
// A zero/negative TTL means "do not cache". Dropping the write is
|
|
36
|
+
// simpler than surfacing an error — callers disable caching by
|
|
37
|
+
// setting the config value to 0.
|
|
38
|
+
entries.delete(cacheKey(key));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
entries.set(cacheKey(key), now() + ttlSeconds * 1000);
|
|
42
|
+
},
|
|
43
|
+
clear() {
|
|
44
|
+
entries.clear();
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=proxy_approval_cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy_approval_cache.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/service/proxy_approval_cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAgBH;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAoB,IAAI,CAAC,GAAG;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,SAAS,QAAQ,CAAC,GAA0B;QAC1C,OAAO,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IAED,OAAO;QACL,GAAG,CAAC,GAA0B;YAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7C,IAAI,SAAS,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YAC1C,IAAI,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;gBACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC9B,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,GAAG,CAAC,GAA0B,EAAE,UAAkB;YAChD,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;gBACpB,kEAAkE;gBAClE,+DAA+D;gBAC/D,iCAAiC;gBACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,KAAK;YACH,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { DEFAULT_PROXY_APPROVAL_TTL_SECONDS } from '../types/proxy_types.js';
|
|
3
|
+
import { createChildLogger } from '../../../lib/logger.js';
|
|
4
|
+
const log = createChildLogger('proxy-auth');
|
|
5
|
+
/**
|
|
6
|
+
* Authenticate and (optionally) request approval for a single proxy request.
|
|
7
|
+
*
|
|
8
|
+
* Pure logic — takes the incoming request and the per-mapping dependencies,
|
|
9
|
+
* returns a decision. The proxy server is responsible for writing the 401/403
|
|
10
|
+
* response and for deleting the caller's auth header from the forwarded
|
|
11
|
+
* request when the decision is `pass`.
|
|
12
|
+
*/
|
|
13
|
+
export async function authorizeProxyRequest(req, deps) {
|
|
14
|
+
const { mapping, validator, approvalRequester, cache, audit, generateRequestId } = deps;
|
|
15
|
+
const requestId = generateRequestId ? generateRequestId() : randomUUID();
|
|
16
|
+
const authMode = mapping.authMode ?? 'none';
|
|
17
|
+
// Extract non-sensitive primitives once. Downstream logging/audit reads
|
|
18
|
+
// these locals, never the full mapping object — this keeps the taint-flow
|
|
19
|
+
// analyzer from painting `port`/`method`/`path` as derived from the
|
|
20
|
+
// header-configuration fields (`apiKeyHeader`, `apiKeyPrefix`), which are
|
|
21
|
+
// only header *names*, not credentials.
|
|
22
|
+
const port = mapping.port;
|
|
23
|
+
const method = req.method ?? 'GET';
|
|
24
|
+
const path = req.url ?? '/';
|
|
25
|
+
const ip = callerIp(req);
|
|
26
|
+
if (authMode === 'none') {
|
|
27
|
+
return { kind: 'pass', requestId };
|
|
28
|
+
}
|
|
29
|
+
const headerName = mapping.apiKeyHeader;
|
|
30
|
+
if (headerName === undefined) {
|
|
31
|
+
// Config loader should reject this, but fail closed if we ever see it.
|
|
32
|
+
recordAudit(audit, {
|
|
33
|
+
type: 'proxy_auth_denied', requestId, port, method, path, ip,
|
|
34
|
+
reason: 'apiKeyHeader not configured',
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
kind: 'reject', requestId,
|
|
38
|
+
status: 500, code: 'misconfigured',
|
|
39
|
+
message: 'Proxy mapping is misconfigured (apiKeyHeader missing).',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!validator) {
|
|
43
|
+
recordAudit(audit, {
|
|
44
|
+
type: 'proxy_auth_denied', requestId, port, method, path, ip,
|
|
45
|
+
reason: 'no validator wired',
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
kind: 'reject', requestId,
|
|
49
|
+
status: 500, code: 'misconfigured',
|
|
50
|
+
message: 'Proxy auth is configured but no token validator is available.',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const rawToken = extractToken(req, headerName, mapping.apiKeyPrefix);
|
|
54
|
+
if (rawToken === undefined) {
|
|
55
|
+
recordAudit(audit, {
|
|
56
|
+
type: 'proxy_auth_denied', requestId, port, method, path, ip,
|
|
57
|
+
reason: 'missing or malformed header',
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
kind: 'reject', requestId,
|
|
61
|
+
status: 401, code: 'unauthorized',
|
|
62
|
+
message: 'Missing or malformed authentication header.',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const identity = validator.validate(rawToken);
|
|
66
|
+
if (!identity) {
|
|
67
|
+
recordAudit(audit, {
|
|
68
|
+
type: 'proxy_auth_denied', requestId, port, method, path, ip,
|
|
69
|
+
reason: 'invalid token',
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
kind: 'reject', requestId,
|
|
73
|
+
status: 401, code: 'unauthorized',
|
|
74
|
+
message: 'Invalid authentication token.',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (authMode === 'api-key') {
|
|
78
|
+
recordAudit(audit, {
|
|
79
|
+
type: 'proxy_auth_ok', requestId, port, method, path, ip, identity,
|
|
80
|
+
});
|
|
81
|
+
return { kind: 'pass', requestId, keyId: identity.keyId, keyName: identity.keyName };
|
|
82
|
+
}
|
|
83
|
+
// authMode === 'api-key-telegram'
|
|
84
|
+
if (!approvalRequester) {
|
|
85
|
+
recordAudit(audit, {
|
|
86
|
+
type: 'proxy_approval_error', requestId, port, method, path, ip, identity,
|
|
87
|
+
reason: 'no approval requester wired',
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
kind: 'reject', requestId,
|
|
91
|
+
status: 500, code: 'misconfigured',
|
|
92
|
+
message: 'Telegram approval is configured but no approval channel is available.',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const cacheKey = { keyId: identity.keyId, port };
|
|
96
|
+
if (cache?.has(cacheKey)) {
|
|
97
|
+
recordAudit(audit, {
|
|
98
|
+
type: 'proxy_approval_approved', requestId, port, method, path, ip, identity, source: 'cache',
|
|
99
|
+
});
|
|
100
|
+
return { kind: 'pass', requestId, keyId: identity.keyId, keyName: identity.keyName };
|
|
101
|
+
}
|
|
102
|
+
const approvalCtx = {
|
|
103
|
+
port,
|
|
104
|
+
baseUrl: mapping.baseUrl,
|
|
105
|
+
method,
|
|
106
|
+
path,
|
|
107
|
+
keyId: identity.keyId,
|
|
108
|
+
keyName: identity.keyName,
|
|
109
|
+
ip,
|
|
110
|
+
requestId,
|
|
111
|
+
};
|
|
112
|
+
recordAudit(audit, {
|
|
113
|
+
type: 'proxy_approval_requested', requestId, port, method, path, ip, identity,
|
|
114
|
+
});
|
|
115
|
+
let outcome;
|
|
116
|
+
try {
|
|
117
|
+
outcome = await approvalRequester.request(approvalCtx);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
log.error({ err, requestId, port }, 'Approval requester threw');
|
|
121
|
+
recordAudit(audit, {
|
|
122
|
+
type: 'proxy_approval_error', requestId, port, method, path, ip, identity,
|
|
123
|
+
reason: err instanceof Error ? err.message : 'approval error',
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
kind: 'reject', requestId,
|
|
127
|
+
status: 503, code: 'approval_error',
|
|
128
|
+
message: 'Approval channel failed.',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (outcome === 'approved') {
|
|
132
|
+
const ttl = mapping.telegramApprovalTtlSeconds ?? DEFAULT_PROXY_APPROVAL_TTL_SECONDS;
|
|
133
|
+
cache?.set(cacheKey, ttl);
|
|
134
|
+
recordAudit(audit, {
|
|
135
|
+
type: 'proxy_approval_approved', requestId, port, method, path, ip, identity, source: 'telegram',
|
|
136
|
+
});
|
|
137
|
+
return { kind: 'pass', requestId, keyId: identity.keyId, keyName: identity.keyName };
|
|
138
|
+
}
|
|
139
|
+
if (outcome === 'timeout') {
|
|
140
|
+
recordAudit(audit, {
|
|
141
|
+
type: 'proxy_approval_timeout', requestId, port, method, path, ip, identity,
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
kind: 'reject', requestId,
|
|
145
|
+
status: 408, code: 'approval_timeout',
|
|
146
|
+
message: 'Approval timed out.',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (outcome === 'error') {
|
|
150
|
+
recordAudit(audit, {
|
|
151
|
+
type: 'proxy_approval_error', requestId, port, method, path, ip, identity,
|
|
152
|
+
reason: 'approval channel error',
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
kind: 'reject', requestId,
|
|
156
|
+
status: 503, code: 'approval_error',
|
|
157
|
+
message: 'Approval channel failed.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// outcome === 'denied'
|
|
161
|
+
recordAudit(audit, {
|
|
162
|
+
type: 'proxy_approval_denied', requestId, port, method, path, ip, identity,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
kind: 'reject', requestId,
|
|
166
|
+
status: 403, code: 'forbidden',
|
|
167
|
+
message: 'Approval denied.',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Extract the token value from the configured header (case-insensitive).
|
|
172
|
+
* Returns `undefined` when missing, empty, or when a required prefix is
|
|
173
|
+
* not present.
|
|
174
|
+
*/
|
|
175
|
+
function extractToken(req, headerName, prefix) {
|
|
176
|
+
const lower = headerName.toLowerCase();
|
|
177
|
+
const raw = req.headers[lower];
|
|
178
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
179
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
180
|
+
return undefined;
|
|
181
|
+
if (prefix !== undefined && prefix.length > 0) {
|
|
182
|
+
if (!value.startsWith(prefix))
|
|
183
|
+
return undefined;
|
|
184
|
+
const stripped = value.slice(prefix.length);
|
|
185
|
+
return stripped.length > 0 ? stripped : undefined;
|
|
186
|
+
}
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Best-effort remote address extraction. The proxy listeners bind to loopback
|
|
191
|
+
* by default, so in production this is primarily used for audit context
|
|
192
|
+
* rather than authorization.
|
|
193
|
+
*/
|
|
194
|
+
function callerIp(req) {
|
|
195
|
+
return req.socket?.remoteAddress ?? 'unknown';
|
|
196
|
+
}
|
|
197
|
+
function recordAudit(sink, args) {
|
|
198
|
+
if (!sink)
|
|
199
|
+
return;
|
|
200
|
+
const event = {
|
|
201
|
+
type: args.type,
|
|
202
|
+
ts: new Date().toISOString(),
|
|
203
|
+
requestId: args.requestId,
|
|
204
|
+
port: args.port,
|
|
205
|
+
method: args.method,
|
|
206
|
+
path: args.path,
|
|
207
|
+
keyId: args.identity?.keyId,
|
|
208
|
+
keyName: args.identity?.keyName,
|
|
209
|
+
ip: args.ip,
|
|
210
|
+
reason: args.reason,
|
|
211
|
+
source: args.source,
|
|
212
|
+
};
|
|
213
|
+
try {
|
|
214
|
+
sink.record(event);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
log.warn({ err, requestId: args.requestId }, 'Proxy audit sink threw; continuing');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=proxy_auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy_auth.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/service/proxy_auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,14 +2,29 @@ import http from 'node:http';
|
|
|
2
2
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
3
3
|
import { DEFAULT_PROXY_HOST } from '../types/proxy_types.js';
|
|
4
4
|
import { createChildLogger } from '../../../lib/logger.js';
|
|
5
|
+
import { authorizeProxyRequest } from './proxy_auth.js';
|
|
6
|
+
import { createProxyApprovalCache } from './proxy_approval_cache.js';
|
|
5
7
|
const log = createChildLogger('proxy');
|
|
6
|
-
function
|
|
8
|
+
function writeJsonError(res, status, code, message) {
|
|
9
|
+
if (res.headersSent)
|
|
10
|
+
return;
|
|
11
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
12
|
+
res.end(JSON.stringify({ error: code, message }));
|
|
13
|
+
}
|
|
14
|
+
function buildProxyServer(mapping, deps, cache) {
|
|
7
15
|
const middleware = createProxyMiddleware({
|
|
8
16
|
target: mapping.baseUrl,
|
|
9
17
|
changeOrigin: true,
|
|
10
18
|
logger: log,
|
|
11
19
|
on: {
|
|
12
20
|
proxyReq: (proxyReq) => {
|
|
21
|
+
// Defense-in-depth: also strip the caller's auth header from the
|
|
22
|
+
// outgoing request. The incoming req.headers has already been
|
|
23
|
+
// mutated (see handler below), but proxyReq.removeHeader is the
|
|
24
|
+
// authoritative write onto the outgoing socket.
|
|
25
|
+
if (mapping.authMode && mapping.authMode !== 'none' && mapping.apiKeyHeader) {
|
|
26
|
+
proxyReq.removeHeader(mapping.apiKeyHeader);
|
|
27
|
+
}
|
|
13
28
|
if (!mapping.headers)
|
|
14
29
|
return;
|
|
15
30
|
for (const [name, value] of Object.entries(mapping.headers)) {
|
|
@@ -28,12 +43,34 @@ function buildProxyServer(mapping) {
|
|
|
28
43
|
},
|
|
29
44
|
});
|
|
30
45
|
return http.createServer((req, res) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
authorizeProxyRequest(req, {
|
|
47
|
+
mapping,
|
|
48
|
+
validator: deps.tokenValidator,
|
|
49
|
+
approvalRequester: deps.approvalRequester,
|
|
50
|
+
cache,
|
|
51
|
+
audit: deps.auditSink,
|
|
52
|
+
})
|
|
53
|
+
.then((decision) => {
|
|
54
|
+
if (decision.kind === 'reject') {
|
|
55
|
+
writeJsonError(res, decision.status, decision.code, decision.message);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Strip the caller's auth header from req.headers before the proxy
|
|
59
|
+
// middleware forwards it, so the lucifer-gate token does not leak
|
|
60
|
+
// upstream. The proxyReq handler also removes it on the outgoing
|
|
61
|
+
// socket (belt and braces).
|
|
62
|
+
if (mapping.authMode && mapping.authMode !== 'none' && mapping.apiKeyHeader) {
|
|
63
|
+
delete req.headers[mapping.apiKeyHeader.toLowerCase()];
|
|
36
64
|
}
|
|
65
|
+
middleware(req, res).catch((err) => {
|
|
66
|
+
log.error({ err, port: mapping.port }, 'Proxy middleware threw');
|
|
67
|
+
writeJsonError(res, 502, 'bad_gateway', 'Proxy handler failed');
|
|
68
|
+
});
|
|
69
|
+
})
|
|
70
|
+
.catch((err) => {
|
|
71
|
+
// authorizeProxyRequest never rejects, but guard just in case.
|
|
72
|
+
log.error({ err, port: mapping.port }, 'Proxy auth threw unexpectedly');
|
|
73
|
+
writeJsonError(res, 500, 'internal_error', 'Proxy authorization failed');
|
|
37
74
|
});
|
|
38
75
|
});
|
|
39
76
|
}
|
|
@@ -69,6 +106,26 @@ async function bestEffortClose(server) {
|
|
|
69
106
|
log.warn({ err }, 'Error while closing proxy listener');
|
|
70
107
|
}
|
|
71
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Validate that every mapping with a non-`none` authMode has the runtime
|
|
111
|
+
* deps it needs. Called at startup so operators get a descriptive error
|
|
112
|
+
* rather than a confusing 500 on the first request.
|
|
113
|
+
*/
|
|
114
|
+
function validateMappingDeps(mappings, deps) {
|
|
115
|
+
for (const [index, m] of mappings.entries()) {
|
|
116
|
+
const mode = m.authMode ?? 'none';
|
|
117
|
+
if (mode === 'none')
|
|
118
|
+
continue;
|
|
119
|
+
if (!deps.tokenValidator) {
|
|
120
|
+
throw new Error(`Proxy mapping proxies[${index}] (port ${m.port}) has authMode='${mode}' but no token validator is wired. ` +
|
|
121
|
+
`Ensure api-keys.json is present so the gateway is initialized before the proxy.`);
|
|
122
|
+
}
|
|
123
|
+
if (mode === 'api-key-telegram' && !deps.approvalRequester) {
|
|
124
|
+
throw new Error(`Proxy mapping proxies[${index}] (port ${m.port}) has authMode='api-key-telegram' but no approval channel is wired. ` +
|
|
125
|
+
`Configure Telegram (LUCIFER_TELEGRAM_TOKEN + chat id) or the web approval UI.`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
72
129
|
/**
|
|
73
130
|
* Build proxy servers for a set of mappings. Listeners are NOT bound until
|
|
74
131
|
* `start()` is called so that `createApp()` stays synchronous and config
|
|
@@ -78,11 +135,17 @@ async function bestEffortClose(server) {
|
|
|
78
135
|
* started listeners are closed before the error is rethrown, so the caller
|
|
79
136
|
* never observes a partially-started set.
|
|
80
137
|
*/
|
|
81
|
-
export function createProxyServers(mappings) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
138
|
+
export function createProxyServers(mappings, deps = {}) {
|
|
139
|
+
validateMappingDeps(mappings, deps);
|
|
140
|
+
const cacheFactory = deps.approvalCacheFactory ?? (() => createProxyApprovalCache());
|
|
141
|
+
const running = mappings.map((mapping) => {
|
|
142
|
+
const cache = cacheFactory();
|
|
143
|
+
return {
|
|
144
|
+
mapping,
|
|
145
|
+
cache,
|
|
146
|
+
server: buildProxyServer(mapping, deps, cache),
|
|
147
|
+
};
|
|
148
|
+
});
|
|
86
149
|
async function start() {
|
|
87
150
|
const started = [];
|
|
88
151
|
try {
|
|
@@ -90,7 +153,7 @@ export function createProxyServers(mappings) {
|
|
|
90
153
|
const host = entry.mapping.host ?? DEFAULT_PROXY_HOST;
|
|
91
154
|
await listenAsync(entry.server, entry.mapping.port, host);
|
|
92
155
|
started.push(entry);
|
|
93
|
-
log.info({ port: entry.mapping.port, host, baseUrl: entry.mapping.baseUrl }, 'Proxy listening');
|
|
156
|
+
log.info({ port: entry.mapping.port, host, baseUrl: entry.mapping.baseUrl, authMode: entry.mapping.authMode ?? 'none' }, 'Proxy listening');
|
|
94
157
|
}
|
|
95
158
|
}
|
|
96
159
|
catch (err) {
|
|
@@ -103,7 +166,10 @@ export function createProxyServers(mappings) {
|
|
|
103
166
|
async function stop() {
|
|
104
167
|
// Best-effort close: a listener that never bound (e.g. because start()
|
|
105
168
|
// failed partway) must not prevent cleanup of the rest.
|
|
106
|
-
await Promise.all(running.map(({ server }) =>
|
|
169
|
+
await Promise.all(running.map(({ server, cache }) => {
|
|
170
|
+
cache.clear();
|
|
171
|
+
return bestEffortClose(server);
|
|
172
|
+
}));
|
|
107
173
|
}
|
|
108
174
|
return { start, stop };
|
|
109
175
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy_server.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/service/proxy_server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy_server.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/service/proxy_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy_types.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/types/proxy_types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy_types.js","sourceRoot":"","sources":["../../../../../server/src/domains/request-proxy/types/proxy_types.ts"],"names":[],"mappings":"AA0BA,MAAM,CAAC,MAAM,uBAAuB,GAAkB,MAAM,CAAC;AAC7D,MAAM,CAAC,MAAM,kCAAkC,GAAG,IAAI,CAAC;AAavD,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC"}
|
|
@@ -17,7 +17,9 @@ export function createTestAppContext(label, options) {
|
|
|
17
17
|
approvalTimeoutSeconds: 5,
|
|
18
18
|
executionTimeoutSeconds: 10,
|
|
19
19
|
maxConcurrentExecutions: 3,
|
|
20
|
-
|
|
20
|
+
// Integration tests run real commands against the repo (e.g. `git status`),
|
|
21
|
+
// so this needs to accommodate a growing working tree. 1 KiB was brittle.
|
|
22
|
+
maxOutputBytes: 65536,
|
|
21
23
|
rateLimitPerMinute: 100,
|
|
22
24
|
onApprovalTimeout: 'deny',
|
|
23
25
|
dataDir: '../data',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-setup.js","sourceRoot":"","sources":["../../../server/src/test/integration-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,wDAAwD,CAAC;AAUpF,MAAM,UAAU,oBAAoB,CAClC,KAAa,EACb,OAGC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEtC,MAAM,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC;IACrC,MAAM,QAAQ,GAAG,GAAG,KAAK,eAAe,CAAC;IACzC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE/C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAEnD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC;QACvC,IAAI,EAAE,CAAC;QACP,sBAAsB,EAAE,CAAC;QACzB,uBAAuB,EAAE,EAAE;QAC3B,uBAAuB,EAAE,CAAC;QAC1B,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"integration-setup.js","sourceRoot":"","sources":["../../../server/src/test/integration-setup.ts"],"names":[],"mappings":"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"}
|