@cytario/web 2.2.7 → 2.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/app/.server/db/redis.ts +10 -7
- package/app/.server/db/redisOptions.ts +21 -6
- package/app/config.ts +1 -1
- package/bin/cytario-web.mjs +1 -1
- package/package.json +1 -1
package/app/.server/db/redis.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { buildRedisOptions } from "./redisOptions";
|
|
|
14
14
|
* - REDIS_PORT: Server port (default: 6379)
|
|
15
15
|
* - REDIS_USERNAME: Optional username for authenticated connections (Redis 6+ / Valkey)
|
|
16
16
|
* - REDIS_PASSWORD: Optional password for authenticated connections
|
|
17
|
+
* - REDIS_KEY_PREFIX: Optional prefix prepended to every key (e.g. "cytario-web:")
|
|
17
18
|
* - REDIS_TLS: Set to "true" to wrap the connection in TLS (required in production)
|
|
18
19
|
* - REDIS_CA_CERT: Optional PEM-encoded CA certificate (string) for self-signed deployments
|
|
19
20
|
* - REDIS_TLS_SERVER_NAME: Optional SNI / certificate hostname override
|
|
@@ -35,6 +36,14 @@ import { buildRedisOptions } from "./redisOptions";
|
|
|
35
36
|
|
|
36
37
|
const options = buildRedisOptions(process.env);
|
|
37
38
|
|
|
39
|
+
const auth = options.password ? "on" : "off";
|
|
40
|
+
const tls = options.tls ? "on" : "off";
|
|
41
|
+
const who = options.username ? ` as ${options.username}` : "";
|
|
42
|
+
const prefix = options.keyPrefix ? `, prefix "${options.keyPrefix}"` : "";
|
|
43
|
+
console.log(
|
|
44
|
+
`Redis/Valkey config: ${options.host}:${options.port}${who} (AUTH ${auth}, TLS ${tls}${prefix})`,
|
|
45
|
+
);
|
|
46
|
+
|
|
38
47
|
export const redis = new Redis(options);
|
|
39
48
|
|
|
40
49
|
redis.on("error", (err) => {
|
|
@@ -42,11 +51,5 @@ redis.on("error", (err) => {
|
|
|
42
51
|
});
|
|
43
52
|
|
|
44
53
|
redis.on("connect", () => {
|
|
45
|
-
|
|
46
|
-
? ` (authenticated as ${options.username})`
|
|
47
|
-
: options.password
|
|
48
|
-
? " (authenticated)"
|
|
49
|
-
: "";
|
|
50
|
-
const tlsInfo = options.tls ? " over TLS" : "";
|
|
51
|
-
console.log(`Connected to Redis/Valkey at ${options.host}:${options.port}${authInfo}${tlsInfo}`);
|
|
54
|
+
console.log(`Connected to Redis/Valkey at ${options.host}:${options.port}`);
|
|
52
55
|
});
|
|
@@ -7,7 +7,10 @@ import type { RedisOptions } from "ioredis";
|
|
|
7
7
|
* - `REDIS_HOST` (default: `localhost`)
|
|
8
8
|
* - `REDIS_PORT` (default: `6379`)
|
|
9
9
|
* - `REDIS_USERNAME` — optional ACL username
|
|
10
|
-
* - `REDIS_PASSWORD` —
|
|
10
|
+
* - `REDIS_PASSWORD` — password; required outside development
|
|
11
|
+
* - `REDIS_KEY_PREFIX` — optional prefix prepended to every key issued by
|
|
12
|
+
* this client (e.g. `"cytario-web:"`). Lets operators scope the app to a
|
|
13
|
+
* single ACL keyspace pattern without code changes.
|
|
11
14
|
* - `REDIS_TLS` — `"true"` to wrap the connection in TLS
|
|
12
15
|
* - `REDIS_CA_CERT` — PEM-encoded CA certificate (string) used to verify
|
|
13
16
|
* the server when the cert chain is not in the system trust store
|
|
@@ -16,9 +19,13 @@ import type { RedisOptions } from "ioredis";
|
|
|
16
19
|
* production TLS requirement (escape hatch for trusted in-cluster
|
|
17
20
|
* networks; logs a warning)
|
|
18
21
|
*
|
|
19
|
-
* Fails fast outside development if
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* Fails fast outside development if:
|
|
23
|
+
* - TLS is off and the opt-out flag is not set, or
|
|
24
|
+
* - `REDIS_PASSWORD` is empty.
|
|
25
|
+
*
|
|
26
|
+
* Session blobs contain OAuth tokens and STS credentials and must never
|
|
27
|
+
* traverse plaintext links or be readable by anonymous clients in
|
|
28
|
+
* production.
|
|
22
29
|
*/
|
|
23
30
|
export function buildRedisOptions(env: Record<string, string | undefined>): RedisOptions {
|
|
24
31
|
const host = env.REDIS_HOST || "localhost";
|
|
@@ -29,15 +36,22 @@ export function buildRedisOptions(env: Record<string, string | undefined>): Redi
|
|
|
29
36
|
const caCert = env.REDIS_CA_CERT;
|
|
30
37
|
const tlsServerName = env.REDIS_TLS_SERVER_NAME;
|
|
31
38
|
const allowPlaintext = env.REDIS_INSECURE_ALLOW_PLAINTEXT === "true";
|
|
39
|
+
const keyPrefix = env.REDIS_KEY_PREFIX;
|
|
32
40
|
const nodeEnv = env.NODE_ENV;
|
|
33
41
|
|
|
34
42
|
const isLocalEnv = nodeEnv === "development" || nodeEnv === "test";
|
|
35
43
|
|
|
44
|
+
if (!isLocalEnv && !password) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Refusing to start: Redis/Valkey AUTH is disabled. Set REDIS_PASSWORD " +
|
|
47
|
+
"to a non-empty value so the session store rejects anonymous clients.",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
36
51
|
if (!tlsEnabled && !isLocalEnv && !allowPlaintext) {
|
|
37
52
|
throw new Error(
|
|
38
53
|
"Refusing to start: Redis/Valkey TLS is disabled. Set REDIS_TLS=true " +
|
|
39
|
-
"(recommended) or REDIS_INSECURE_ALLOW_PLAINTEXT=true to opt out.
|
|
40
|
-
"See C-204 / OWASP A02:2021.",
|
|
54
|
+
"(recommended) or REDIS_INSECURE_ALLOW_PLAINTEXT=true to opt out.",
|
|
41
55
|
);
|
|
42
56
|
}
|
|
43
57
|
|
|
@@ -53,6 +67,7 @@ export function buildRedisOptions(env: Record<string, string | undefined>): Redi
|
|
|
53
67
|
port,
|
|
54
68
|
...(username && { username }),
|
|
55
69
|
...(password && { password }),
|
|
70
|
+
...(keyPrefix && { keyPrefix }),
|
|
56
71
|
maxRetriesPerRequest: 3,
|
|
57
72
|
retryStrategy(times) {
|
|
58
73
|
const delay = Math.min(times * 50, 2000);
|
package/app/config.ts
CHANGED
|
@@ -22,7 +22,7 @@ interface CytarioConfig {
|
|
|
22
22
|
host: string;
|
|
23
23
|
/** Optional username for authenticated connections (Redis 6+ ACL / Valkey) */
|
|
24
24
|
username?: string;
|
|
25
|
-
/**
|
|
25
|
+
/** Password for authenticated connections. Optional in development only */
|
|
26
26
|
password?: string;
|
|
27
27
|
};
|
|
28
28
|
cookie: CookieOptions;
|
package/bin/cytario-web.mjs
CHANGED
|
@@ -85,7 +85,7 @@ function runStart(forwardedArgs) {
|
|
|
85
85
|
const child = spawn(process.execPath, [serverEntry, ...forwardedArgs], {
|
|
86
86
|
cwd: PACKAGE_ROOT,
|
|
87
87
|
stdio: "inherit",
|
|
88
|
-
env: { ...process.env, NODE_ENV: "production" },
|
|
88
|
+
env: { ...process.env, NODE_ENV: process.env.NODE_ENV ?? "production" },
|
|
89
89
|
});
|
|
90
90
|
child.on("exit", (code, signal) => {
|
|
91
91
|
if (signal) process.kill(process.pid, signal);
|
package/package.json
CHANGED