@praesidias/api-gateway 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +16 -2
- package/dist/cli.js +1005 -0
- package/dist/index.cjs +962 -0
- package/dist/index.d.cts +248 -0
- package/dist/index.d.ts +248 -0
- package/dist/index.js +927 -0
- package/package.json +67 -3
- package/praesidias.gateway.example.json +35 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Condition, MappingFile, Praesidias } from '@praesidias/sdk';
|
|
2
|
+
import { Server, IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
import { Server as Server$1 } from 'node:https';
|
|
4
|
+
|
|
5
|
+
/** How the upstream's credential is put on the forwarded request. */
|
|
6
|
+
type CredentialPlacement = {
|
|
7
|
+
/** The header to set. Default "authorization". */
|
|
8
|
+
header?: string;
|
|
9
|
+
/** Put before the value, with a space: "Bearer", "Basic", or nothing. */
|
|
10
|
+
scheme?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Where the upstream's credential comes from. The secret itself is never written in the config
|
|
14
|
+
* file: only where to find it.
|
|
15
|
+
*
|
|
16
|
+
* - `env`: an environment variable of the gateway's process.
|
|
17
|
+
* - `file`: a file, read again on every request. Vault Agent, the Kubernetes Secrets Store CSI
|
|
18
|
+
* driver (AWS, Azure, Google Cloud, Vault) and Docker secrets all deliver secrets this way.
|
|
19
|
+
* - `vault`: HashiCorp Vault's key/value store, read over https (see VaultCredentialSource).
|
|
20
|
+
*/
|
|
21
|
+
type CredentialRule = CredentialPlacement & ({
|
|
22
|
+
from: 'env'; /** The environment variable. */
|
|
23
|
+
name: string;
|
|
24
|
+
} | {
|
|
25
|
+
from: 'file'; /** An absolute path. */
|
|
26
|
+
path: string;
|
|
27
|
+
} | {
|
|
28
|
+
from: 'vault';
|
|
29
|
+
/** The secret's path as Vault's API names it, for example "secret/data/payments/bank". */
|
|
30
|
+
path: string;
|
|
31
|
+
/** The field within the secret, for example "api_key". */
|
|
32
|
+
field: string;
|
|
33
|
+
});
|
|
34
|
+
/** A credential rule as checked: which source, and what to ask it for. */
|
|
35
|
+
type CompiledCredential = CredentialPlacement & {
|
|
36
|
+
from: 'env' | 'file' | 'vault';
|
|
37
|
+
/** env: the variable. file: the path. vault: "<path>#<field>". */
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
type RouteRule = {
|
|
41
|
+
method: string;
|
|
42
|
+
/** A path under the upstream, with `:name` for a segment that varies. */
|
|
43
|
+
path: string;
|
|
44
|
+
action?: string;
|
|
45
|
+
resource?: {
|
|
46
|
+
type?: string;
|
|
47
|
+
idFrom?: string | string[];
|
|
48
|
+
};
|
|
49
|
+
/** `arg` is `params.x`, `query.x` or `body.x`. The first rule that matches replaces the action. */
|
|
50
|
+
when?: Array<{
|
|
51
|
+
if: Condition | Condition[];
|
|
52
|
+
action: string;
|
|
53
|
+
resource?: RouteRule['resource'];
|
|
54
|
+
}>;
|
|
55
|
+
/** Values recorded with the decision, by the same paths. Nothing else from the request is. */
|
|
56
|
+
context?: string[];
|
|
57
|
+
system?: string;
|
|
58
|
+
/** Forwarded without asking Praesidias. For reads you have decided need no record. */
|
|
59
|
+
ungoverned?: boolean;
|
|
60
|
+
};
|
|
61
|
+
type UpstreamRule = {
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
credential?: CredentialRule;
|
|
64
|
+
routes: RouteRule[];
|
|
65
|
+
/**
|
|
66
|
+
* Drop-in: the API's own hostnames. When the platform points one at this gateway (DNS, or the
|
|
67
|
+
* address the agent is given), the agent keeps calling the URL and paths it always did, with
|
|
68
|
+
* no "/<upstream>" in front. A request is matched to its upstream by its Host header.
|
|
69
|
+
*/
|
|
70
|
+
hosts?: string[];
|
|
71
|
+
/**
|
|
72
|
+
* Drop-in: the agent's Praesidias key arrives in the header the agent already sends to this
|
|
73
|
+
* API, in place of the API's own credential. The gateway takes it out, asks Praesidias, and
|
|
74
|
+
* puts the real credential on instead. Only a value shaped like a Praesidias key is accepted.
|
|
75
|
+
*/
|
|
76
|
+
agentKey?: {
|
|
77
|
+
header: string;
|
|
78
|
+
scheme?: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
type GatewayConfig = {
|
|
82
|
+
/** The system these APIs are, as your areas name it. */
|
|
83
|
+
system: string;
|
|
84
|
+
listen?: {
|
|
85
|
+
host?: string;
|
|
86
|
+
port?: number;
|
|
87
|
+
/** Required to listen anywhere but this machine. */ allowRemote?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Answer over https with this certificate and key (files, PEM). For drop-in, the certificate
|
|
90
|
+
* is for the API's own hostnames, issued by the organization's certificate authority.
|
|
91
|
+
*/
|
|
92
|
+
tls?: {
|
|
93
|
+
certFile: string;
|
|
94
|
+
keyFile: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/** By the name that is the first segment of the address the agent calls. */
|
|
98
|
+
upstreams?: Record<string, UpstreamRule>;
|
|
99
|
+
/**
|
|
100
|
+
* Also answer `POST /ask`: one request that asks Praesidias about an action and returns a
|
|
101
|
+
* plain yes or no. For no-code automations (n8n, Zapier, Make, Power Automate), where one
|
|
102
|
+
* HTTPS step is all there is. Off unless set.
|
|
103
|
+
*/
|
|
104
|
+
ask?: boolean;
|
|
105
|
+
};
|
|
106
|
+
declare class ConfigError extends Error {
|
|
107
|
+
}
|
|
108
|
+
type CompiledRoute = RouteRule & {
|
|
109
|
+
method: string;
|
|
110
|
+
match: RegExp;
|
|
111
|
+
params: string[];
|
|
112
|
+
id: string;
|
|
113
|
+
};
|
|
114
|
+
type CompiledUpstream = {
|
|
115
|
+
name: string;
|
|
116
|
+
base: URL;
|
|
117
|
+
credential: CompiledCredential | null;
|
|
118
|
+
routes: CompiledRoute[];
|
|
119
|
+
mapping: MappingFile;
|
|
120
|
+
hosts: string[];
|
|
121
|
+
agentKey: {
|
|
122
|
+
header: string;
|
|
123
|
+
scheme?: string;
|
|
124
|
+
} | null;
|
|
125
|
+
};
|
|
126
|
+
type Compiled = {
|
|
127
|
+
system: string;
|
|
128
|
+
host: string;
|
|
129
|
+
port: number;
|
|
130
|
+
ask: boolean;
|
|
131
|
+
upstreams: Map<string, CompiledUpstream>;
|
|
132
|
+
/** Drop-in: an upstream by one of its own hostnames. */
|
|
133
|
+
hosts: Map<string, CompiledUpstream>;
|
|
134
|
+
tls: {
|
|
135
|
+
certFile: string;
|
|
136
|
+
keyFile: string;
|
|
137
|
+
} | null;
|
|
138
|
+
};
|
|
139
|
+
/** Checks the whole file at start, so a mistake stops the gateway instead of surfacing mid-request. */
|
|
140
|
+
declare function compileConfig(raw: unknown): Compiled;
|
|
141
|
+
declare function loadConfig(path: string): Compiled;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Where the gateway gets an upstream API's credential at the moment it forwards a request.
|
|
145
|
+
* The agent never has it: the agent only ever holds its Praesidias key.
|
|
146
|
+
*
|
|
147
|
+
* `get` is asked each time, so a source may rotate what it returns. Return undefined when
|
|
148
|
+
* there is nothing under that name; the request is then refused, never sent without one.
|
|
149
|
+
*/
|
|
150
|
+
type CredentialSource = {
|
|
151
|
+
get(name: string): Promise<string | undefined> | string | undefined;
|
|
152
|
+
};
|
|
153
|
+
/** Reads credentials from this process's environment. */
|
|
154
|
+
declare class EnvCredentialSource implements CredentialSource {
|
|
155
|
+
private readonly env;
|
|
156
|
+
constructor(env?: NodeJS.ProcessEnv);
|
|
157
|
+
get(name: string): string | undefined;
|
|
158
|
+
}
|
|
159
|
+
/** Why a credential could not be had. Names only: never a secret, a token or a file's contents. */
|
|
160
|
+
type Note = (line: string) => void;
|
|
161
|
+
/**
|
|
162
|
+
* Reads a credential from a file, read again on every request so a rotated secret is picked up
|
|
163
|
+
* without a restart. This is how most secret stores hand a secret to a process: Vault Agent,
|
|
164
|
+
* the Kubernetes Secrets Store CSI driver (for AWS, Azure, Google Cloud or Vault), and Docker
|
|
165
|
+
* secrets all write it to a file. The name asked for is the file's path.
|
|
166
|
+
*/
|
|
167
|
+
declare class FileCredentialSource implements CredentialSource {
|
|
168
|
+
private readonly note;
|
|
169
|
+
constructor(note?: Note);
|
|
170
|
+
get(path: string): Promise<string | undefined>;
|
|
171
|
+
}
|
|
172
|
+
type VaultOptions = {
|
|
173
|
+
env?: NodeJS.ProcessEnv;
|
|
174
|
+
/** How long a secret read from Vault is reused, in milliseconds. Default 60 s; 0 reads every time. */
|
|
175
|
+
cacheMs?: number;
|
|
176
|
+
/** How long Vault may take, in milliseconds. Default 5 s. */
|
|
177
|
+
timeoutMs?: number;
|
|
178
|
+
note?: Note;
|
|
179
|
+
/** For tests. */
|
|
180
|
+
fetch?: typeof fetch;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Reads a credential from HashiCorp Vault's key/value store (version 2, or version 1), over
|
|
184
|
+
* https. The name asked for is `<path>#<field>`, for example `secret/data/payments/bank#api_key`.
|
|
185
|
+
*
|
|
186
|
+
* Vault is found from the standard variables: VAULT_ADDR, VAULT_TOKEN or VAULT_TOKEN_FILE (the
|
|
187
|
+
* file Vault Agent keeps a fresh token in, read again each time), and VAULT_NAMESPACE. A private
|
|
188
|
+
* certificate authority for Vault is trusted with NODE_EXTRA_CA_CERTS, as for any Node process.
|
|
189
|
+
*
|
|
190
|
+
* A secret is reused for a short while so Vault is not asked on every request. When it is time
|
|
191
|
+
* to ask again and Vault cannot answer, nothing is returned and the request is refused: an
|
|
192
|
+
* expired secret is never used.
|
|
193
|
+
*/
|
|
194
|
+
declare class VaultCredentialSource implements CredentialSource {
|
|
195
|
+
private readonly env;
|
|
196
|
+
private readonly cacheMs;
|
|
197
|
+
private readonly timeoutMs;
|
|
198
|
+
private readonly note;
|
|
199
|
+
private readonly send;
|
|
200
|
+
private readonly cache;
|
|
201
|
+
private readonly reading;
|
|
202
|
+
constructor(options?: VaultOptions);
|
|
203
|
+
get(name: string): Promise<string | undefined>;
|
|
204
|
+
private token;
|
|
205
|
+
private read;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
type ApiGatewayOptions = {
|
|
209
|
+
config: Compiled;
|
|
210
|
+
/** The Praesidias address. Must be https, except on this machine. */
|
|
211
|
+
praesidiasUrl: string;
|
|
212
|
+
/** Where "env" credentials come from. Default: this process's environment. */
|
|
213
|
+
credentials?: CredentialSource;
|
|
214
|
+
/** Settings for "vault" credentials (Vault itself is found from VAULT_ADDR and friends). */
|
|
215
|
+
vault?: Omit<VaultOptions, 'note'>;
|
|
216
|
+
/** One line per request: method, route, action, outcome. Never a key, a credential or a body. */
|
|
217
|
+
log?: (line: string) => void;
|
|
218
|
+
/** The header the agent puts its Praesidias key in. Default "x-praesidias-key". */
|
|
219
|
+
keyHeader?: string;
|
|
220
|
+
/** Largest request body accepted, in bytes. Default 1 MB. */
|
|
221
|
+
maxBodyBytes?: number;
|
|
222
|
+
/** How long an upstream may take, in milliseconds. Default 30 s. */
|
|
223
|
+
upstreamTimeoutMs?: number;
|
|
224
|
+
/** For tests. */
|
|
225
|
+
fetch?: typeof fetch;
|
|
226
|
+
sdkOptions?: ConstructorParameters<typeof Praesidias>[0];
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* A gateway in front of the outside APIs an agent calls.
|
|
230
|
+
*
|
|
231
|
+
* The agent calls `http://<gateway>/<upstream>/<path>` with its Praesidias key. The gateway
|
|
232
|
+
* finds the configured route, asks Praesidias whether this agent may do that action on that
|
|
233
|
+
* resource, and only then forwards the request, adding the API's own credential. The agent
|
|
234
|
+
* never holds that credential, so it cannot reach the API any other way.
|
|
235
|
+
*
|
|
236
|
+
* It fails closed at every step: no route, no key, no answer from Praesidias, or no credential
|
|
237
|
+
* all mean the request is not sent.
|
|
238
|
+
*/
|
|
239
|
+
declare function createApiGateway(options: ApiGatewayOptions): Server;
|
|
240
|
+
/**
|
|
241
|
+
* The gateway as a server: over https when the config has "listen.tls" (the certificate and key
|
|
242
|
+
* are read here, once), otherwise plain http as `createApiGateway`.
|
|
243
|
+
*/
|
|
244
|
+
declare function createApiGatewayServer(options: ApiGatewayOptions): Server | Server$1;
|
|
245
|
+
/** The request handler, for any http or https server. Everything the gateway does is here. */
|
|
246
|
+
declare function createApiGatewayHandler(options: ApiGatewayOptions): (req: IncomingMessage, res: ServerResponse) => void;
|
|
247
|
+
|
|
248
|
+
export { type ApiGatewayOptions, type Compiled, type CompiledCredential, ConfigError, type CredentialRule, type CredentialSource, EnvCredentialSource, FileCredentialSource, type GatewayConfig, type RouteRule, type UpstreamRule, VaultCredentialSource, type VaultOptions, compileConfig, createApiGateway, createApiGatewayHandler, createApiGatewayServer, loadConfig };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Condition, MappingFile, Praesidias } from '@praesidias/sdk';
|
|
2
|
+
import { Server, IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
import { Server as Server$1 } from 'node:https';
|
|
4
|
+
|
|
5
|
+
/** How the upstream's credential is put on the forwarded request. */
|
|
6
|
+
type CredentialPlacement = {
|
|
7
|
+
/** The header to set. Default "authorization". */
|
|
8
|
+
header?: string;
|
|
9
|
+
/** Put before the value, with a space: "Bearer", "Basic", or nothing. */
|
|
10
|
+
scheme?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Where the upstream's credential comes from. The secret itself is never written in the config
|
|
14
|
+
* file: only where to find it.
|
|
15
|
+
*
|
|
16
|
+
* - `env`: an environment variable of the gateway's process.
|
|
17
|
+
* - `file`: a file, read again on every request. Vault Agent, the Kubernetes Secrets Store CSI
|
|
18
|
+
* driver (AWS, Azure, Google Cloud, Vault) and Docker secrets all deliver secrets this way.
|
|
19
|
+
* - `vault`: HashiCorp Vault's key/value store, read over https (see VaultCredentialSource).
|
|
20
|
+
*/
|
|
21
|
+
type CredentialRule = CredentialPlacement & ({
|
|
22
|
+
from: 'env'; /** The environment variable. */
|
|
23
|
+
name: string;
|
|
24
|
+
} | {
|
|
25
|
+
from: 'file'; /** An absolute path. */
|
|
26
|
+
path: string;
|
|
27
|
+
} | {
|
|
28
|
+
from: 'vault';
|
|
29
|
+
/** The secret's path as Vault's API names it, for example "secret/data/payments/bank". */
|
|
30
|
+
path: string;
|
|
31
|
+
/** The field within the secret, for example "api_key". */
|
|
32
|
+
field: string;
|
|
33
|
+
});
|
|
34
|
+
/** A credential rule as checked: which source, and what to ask it for. */
|
|
35
|
+
type CompiledCredential = CredentialPlacement & {
|
|
36
|
+
from: 'env' | 'file' | 'vault';
|
|
37
|
+
/** env: the variable. file: the path. vault: "<path>#<field>". */
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
type RouteRule = {
|
|
41
|
+
method: string;
|
|
42
|
+
/** A path under the upstream, with `:name` for a segment that varies. */
|
|
43
|
+
path: string;
|
|
44
|
+
action?: string;
|
|
45
|
+
resource?: {
|
|
46
|
+
type?: string;
|
|
47
|
+
idFrom?: string | string[];
|
|
48
|
+
};
|
|
49
|
+
/** `arg` is `params.x`, `query.x` or `body.x`. The first rule that matches replaces the action. */
|
|
50
|
+
when?: Array<{
|
|
51
|
+
if: Condition | Condition[];
|
|
52
|
+
action: string;
|
|
53
|
+
resource?: RouteRule['resource'];
|
|
54
|
+
}>;
|
|
55
|
+
/** Values recorded with the decision, by the same paths. Nothing else from the request is. */
|
|
56
|
+
context?: string[];
|
|
57
|
+
system?: string;
|
|
58
|
+
/** Forwarded without asking Praesidias. For reads you have decided need no record. */
|
|
59
|
+
ungoverned?: boolean;
|
|
60
|
+
};
|
|
61
|
+
type UpstreamRule = {
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
credential?: CredentialRule;
|
|
64
|
+
routes: RouteRule[];
|
|
65
|
+
/**
|
|
66
|
+
* Drop-in: the API's own hostnames. When the platform points one at this gateway (DNS, or the
|
|
67
|
+
* address the agent is given), the agent keeps calling the URL and paths it always did, with
|
|
68
|
+
* no "/<upstream>" in front. A request is matched to its upstream by its Host header.
|
|
69
|
+
*/
|
|
70
|
+
hosts?: string[];
|
|
71
|
+
/**
|
|
72
|
+
* Drop-in: the agent's Praesidias key arrives in the header the agent already sends to this
|
|
73
|
+
* API, in place of the API's own credential. The gateway takes it out, asks Praesidias, and
|
|
74
|
+
* puts the real credential on instead. Only a value shaped like a Praesidias key is accepted.
|
|
75
|
+
*/
|
|
76
|
+
agentKey?: {
|
|
77
|
+
header: string;
|
|
78
|
+
scheme?: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
type GatewayConfig = {
|
|
82
|
+
/** The system these APIs are, as your areas name it. */
|
|
83
|
+
system: string;
|
|
84
|
+
listen?: {
|
|
85
|
+
host?: string;
|
|
86
|
+
port?: number;
|
|
87
|
+
/** Required to listen anywhere but this machine. */ allowRemote?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Answer over https with this certificate and key (files, PEM). For drop-in, the certificate
|
|
90
|
+
* is for the API's own hostnames, issued by the organization's certificate authority.
|
|
91
|
+
*/
|
|
92
|
+
tls?: {
|
|
93
|
+
certFile: string;
|
|
94
|
+
keyFile: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/** By the name that is the first segment of the address the agent calls. */
|
|
98
|
+
upstreams?: Record<string, UpstreamRule>;
|
|
99
|
+
/**
|
|
100
|
+
* Also answer `POST /ask`: one request that asks Praesidias about an action and returns a
|
|
101
|
+
* plain yes or no. For no-code automations (n8n, Zapier, Make, Power Automate), where one
|
|
102
|
+
* HTTPS step is all there is. Off unless set.
|
|
103
|
+
*/
|
|
104
|
+
ask?: boolean;
|
|
105
|
+
};
|
|
106
|
+
declare class ConfigError extends Error {
|
|
107
|
+
}
|
|
108
|
+
type CompiledRoute = RouteRule & {
|
|
109
|
+
method: string;
|
|
110
|
+
match: RegExp;
|
|
111
|
+
params: string[];
|
|
112
|
+
id: string;
|
|
113
|
+
};
|
|
114
|
+
type CompiledUpstream = {
|
|
115
|
+
name: string;
|
|
116
|
+
base: URL;
|
|
117
|
+
credential: CompiledCredential | null;
|
|
118
|
+
routes: CompiledRoute[];
|
|
119
|
+
mapping: MappingFile;
|
|
120
|
+
hosts: string[];
|
|
121
|
+
agentKey: {
|
|
122
|
+
header: string;
|
|
123
|
+
scheme?: string;
|
|
124
|
+
} | null;
|
|
125
|
+
};
|
|
126
|
+
type Compiled = {
|
|
127
|
+
system: string;
|
|
128
|
+
host: string;
|
|
129
|
+
port: number;
|
|
130
|
+
ask: boolean;
|
|
131
|
+
upstreams: Map<string, CompiledUpstream>;
|
|
132
|
+
/** Drop-in: an upstream by one of its own hostnames. */
|
|
133
|
+
hosts: Map<string, CompiledUpstream>;
|
|
134
|
+
tls: {
|
|
135
|
+
certFile: string;
|
|
136
|
+
keyFile: string;
|
|
137
|
+
} | null;
|
|
138
|
+
};
|
|
139
|
+
/** Checks the whole file at start, so a mistake stops the gateway instead of surfacing mid-request. */
|
|
140
|
+
declare function compileConfig(raw: unknown): Compiled;
|
|
141
|
+
declare function loadConfig(path: string): Compiled;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Where the gateway gets an upstream API's credential at the moment it forwards a request.
|
|
145
|
+
* The agent never has it: the agent only ever holds its Praesidias key.
|
|
146
|
+
*
|
|
147
|
+
* `get` is asked each time, so a source may rotate what it returns. Return undefined when
|
|
148
|
+
* there is nothing under that name; the request is then refused, never sent without one.
|
|
149
|
+
*/
|
|
150
|
+
type CredentialSource = {
|
|
151
|
+
get(name: string): Promise<string | undefined> | string | undefined;
|
|
152
|
+
};
|
|
153
|
+
/** Reads credentials from this process's environment. */
|
|
154
|
+
declare class EnvCredentialSource implements CredentialSource {
|
|
155
|
+
private readonly env;
|
|
156
|
+
constructor(env?: NodeJS.ProcessEnv);
|
|
157
|
+
get(name: string): string | undefined;
|
|
158
|
+
}
|
|
159
|
+
/** Why a credential could not be had. Names only: never a secret, a token or a file's contents. */
|
|
160
|
+
type Note = (line: string) => void;
|
|
161
|
+
/**
|
|
162
|
+
* Reads a credential from a file, read again on every request so a rotated secret is picked up
|
|
163
|
+
* without a restart. This is how most secret stores hand a secret to a process: Vault Agent,
|
|
164
|
+
* the Kubernetes Secrets Store CSI driver (for AWS, Azure, Google Cloud or Vault), and Docker
|
|
165
|
+
* secrets all write it to a file. The name asked for is the file's path.
|
|
166
|
+
*/
|
|
167
|
+
declare class FileCredentialSource implements CredentialSource {
|
|
168
|
+
private readonly note;
|
|
169
|
+
constructor(note?: Note);
|
|
170
|
+
get(path: string): Promise<string | undefined>;
|
|
171
|
+
}
|
|
172
|
+
type VaultOptions = {
|
|
173
|
+
env?: NodeJS.ProcessEnv;
|
|
174
|
+
/** How long a secret read from Vault is reused, in milliseconds. Default 60 s; 0 reads every time. */
|
|
175
|
+
cacheMs?: number;
|
|
176
|
+
/** How long Vault may take, in milliseconds. Default 5 s. */
|
|
177
|
+
timeoutMs?: number;
|
|
178
|
+
note?: Note;
|
|
179
|
+
/** For tests. */
|
|
180
|
+
fetch?: typeof fetch;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Reads a credential from HashiCorp Vault's key/value store (version 2, or version 1), over
|
|
184
|
+
* https. The name asked for is `<path>#<field>`, for example `secret/data/payments/bank#api_key`.
|
|
185
|
+
*
|
|
186
|
+
* Vault is found from the standard variables: VAULT_ADDR, VAULT_TOKEN or VAULT_TOKEN_FILE (the
|
|
187
|
+
* file Vault Agent keeps a fresh token in, read again each time), and VAULT_NAMESPACE. A private
|
|
188
|
+
* certificate authority for Vault is trusted with NODE_EXTRA_CA_CERTS, as for any Node process.
|
|
189
|
+
*
|
|
190
|
+
* A secret is reused for a short while so Vault is not asked on every request. When it is time
|
|
191
|
+
* to ask again and Vault cannot answer, nothing is returned and the request is refused: an
|
|
192
|
+
* expired secret is never used.
|
|
193
|
+
*/
|
|
194
|
+
declare class VaultCredentialSource implements CredentialSource {
|
|
195
|
+
private readonly env;
|
|
196
|
+
private readonly cacheMs;
|
|
197
|
+
private readonly timeoutMs;
|
|
198
|
+
private readonly note;
|
|
199
|
+
private readonly send;
|
|
200
|
+
private readonly cache;
|
|
201
|
+
private readonly reading;
|
|
202
|
+
constructor(options?: VaultOptions);
|
|
203
|
+
get(name: string): Promise<string | undefined>;
|
|
204
|
+
private token;
|
|
205
|
+
private read;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
type ApiGatewayOptions = {
|
|
209
|
+
config: Compiled;
|
|
210
|
+
/** The Praesidias address. Must be https, except on this machine. */
|
|
211
|
+
praesidiasUrl: string;
|
|
212
|
+
/** Where "env" credentials come from. Default: this process's environment. */
|
|
213
|
+
credentials?: CredentialSource;
|
|
214
|
+
/** Settings for "vault" credentials (Vault itself is found from VAULT_ADDR and friends). */
|
|
215
|
+
vault?: Omit<VaultOptions, 'note'>;
|
|
216
|
+
/** One line per request: method, route, action, outcome. Never a key, a credential or a body. */
|
|
217
|
+
log?: (line: string) => void;
|
|
218
|
+
/** The header the agent puts its Praesidias key in. Default "x-praesidias-key". */
|
|
219
|
+
keyHeader?: string;
|
|
220
|
+
/** Largest request body accepted, in bytes. Default 1 MB. */
|
|
221
|
+
maxBodyBytes?: number;
|
|
222
|
+
/** How long an upstream may take, in milliseconds. Default 30 s. */
|
|
223
|
+
upstreamTimeoutMs?: number;
|
|
224
|
+
/** For tests. */
|
|
225
|
+
fetch?: typeof fetch;
|
|
226
|
+
sdkOptions?: ConstructorParameters<typeof Praesidias>[0];
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* A gateway in front of the outside APIs an agent calls.
|
|
230
|
+
*
|
|
231
|
+
* The agent calls `http://<gateway>/<upstream>/<path>` with its Praesidias key. The gateway
|
|
232
|
+
* finds the configured route, asks Praesidias whether this agent may do that action on that
|
|
233
|
+
* resource, and only then forwards the request, adding the API's own credential. The agent
|
|
234
|
+
* never holds that credential, so it cannot reach the API any other way.
|
|
235
|
+
*
|
|
236
|
+
* It fails closed at every step: no route, no key, no answer from Praesidias, or no credential
|
|
237
|
+
* all mean the request is not sent.
|
|
238
|
+
*/
|
|
239
|
+
declare function createApiGateway(options: ApiGatewayOptions): Server;
|
|
240
|
+
/**
|
|
241
|
+
* The gateway as a server: over https when the config has "listen.tls" (the certificate and key
|
|
242
|
+
* are read here, once), otherwise plain http as `createApiGateway`.
|
|
243
|
+
*/
|
|
244
|
+
declare function createApiGatewayServer(options: ApiGatewayOptions): Server | Server$1;
|
|
245
|
+
/** The request handler, for any http or https server. Everything the gateway does is here. */
|
|
246
|
+
declare function createApiGatewayHandler(options: ApiGatewayOptions): (req: IncomingMessage, res: ServerResponse) => void;
|
|
247
|
+
|
|
248
|
+
export { type ApiGatewayOptions, type Compiled, type CompiledCredential, ConfigError, type CredentialRule, type CredentialSource, EnvCredentialSource, FileCredentialSource, type GatewayConfig, type RouteRule, type UpstreamRule, VaultCredentialSource, type VaultOptions, compileConfig, createApiGateway, createApiGatewayHandler, createApiGatewayServer, loadConfig };
|