@catladder/cli 0.0.0-main-2355fd7c → 0.0.0-main-018e2dee
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/dist/apps/cli/src/apps/catenv/catenv.js +3 -0
- package/dist/apps/cli/src/apps/catenv/catenv.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.d.ts +4 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js +12 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.d.ts +2 -2
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js +3 -1
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.d.ts +10 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.js +98 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkLocalAccess.js.map +1 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupContext.js +2 -0
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupContext.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.d.ts +9 -0
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.js +31 -0
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupLocalAccess.js.map +1 -0
- package/dist/apps/cli/src/config/getProjectConfig.js +9 -1
- package/dist/apps/cli/src/config/getProjectConfig.js.map +1 -1
- package/dist/apps/cli/src/gcloud/localAccess.d.ts +53 -0
- package/dist/apps/cli/src/gcloud/localAccess.js +176 -0
- package/dist/apps/cli/src/gcloud/localAccess.js.map +1 -0
- package/dist/apps/cli/src/gcloud/serviceAccounts.js +9 -2
- package/dist/apps/cli/src/gcloud/serviceAccounts.js.map +1 -1
- package/dist/apps/cli/src/localAccess/declared.d.ts +14 -0
- package/dist/apps/cli/src/localAccess/declared.js +34 -0
- package/dist/apps/cli/src/localAccess/declared.js.map +1 -0
- package/dist/apps/cli/src/localAccess/keyFiles.d.ts +28 -0
- package/dist/apps/cli/src/localAccess/keyFiles.js +91 -0
- package/dist/apps/cli/src/localAccess/keyFiles.js.map +1 -0
- package/dist/apps/cli/src/utils/writePrivateFile.d.ts +7 -0
- package/dist/apps/cli/src/utils/writePrivateFile.js +25 -0
- package/dist/apps/cli/src/utils/writePrivateFile.js.map +1 -0
- package/dist/apps/cli/src/vault/BitwardenVault.js +13 -22
- package/dist/apps/cli/src/vault/BitwardenVault.js.map +1 -1
- package/dist/apps/cli/src/vault/VaultManager.d.ts +6 -0
- package/dist/apps/cli/src/vault/VaultManager.js +18 -0
- package/dist/apps/cli/src/vault/VaultManager.js.map +1 -1
- package/dist/apps/cli/src/vault/bw.d.ts +6 -2
- package/dist/apps/cli/src/vault/bw.js +32 -6
- package/dist/apps/cli/src/vault/bw.js.map +1 -1
- package/dist/apps/cli/src/vault/cache.js +5 -2
- package/dist/apps/cli/src/vault/cache.js.map +1 -1
- package/dist/bundles/catenv/index.js +436 -31
- package/dist/bundles/cli/index.js +786 -35
- package/dist/bundles/skills/catladder-secrets/SKILL.md +35 -3
- package/dist/packages/pipeline/src/index.d.ts +1 -0
- package/dist/packages/pipeline/src/index.js +1 -0
- package/dist/packages/pipeline/src/index.js.map +1 -1
- package/dist/packages/pipeline/src/localAccess/index.d.ts +53 -0
- package/dist/packages/pipeline/src/localAccess/index.js +177 -0
- package/dist/packages/pipeline/src/localAccess/index.js.map +1 -0
- package/dist/packages/pipeline/src/types/config.d.ts +75 -0
- package/dist/packages/pipeline/src/types/config.js.map +1 -1
- package/dist/skills/catladder-secrets/SKILL.md +35 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/catenv/catenv.ts +4 -0
- package/src/apps/cli/commands/project/doctor/GcloudProjectInspector.ts +25 -1
- package/src/apps/cli/commands/project/doctor/__tests__/checkLocalAccess.test.ts +127 -0
- package/src/apps/cli/commands/project/doctor/checkCloudRun.ts +5 -1
- package/src/apps/cli/commands/project/doctor/checkLocalAccess.ts +146 -0
- package/src/apps/cli/commands/project/doctor/index.ts +1 -1
- package/src/apps/cli/commands/project/setup/setupContext.ts +2 -0
- package/src/apps/cli/commands/project/setup/setupLocalAccess.ts +44 -0
- package/src/config/__tests__/getEnvVarsResolved.test.ts +45 -0
- package/src/config/getProjectConfig.ts +11 -1
- package/src/gcloud/__tests__/localAccess.test.ts +82 -0
- package/src/gcloud/__tests__/syncLocalAccess.test.ts +196 -0
- package/src/gcloud/localAccess.ts +257 -0
- package/src/gcloud/serviceAccounts.ts +8 -2
- package/src/localAccess/__tests__/keyFiles.test.ts +166 -0
- package/src/localAccess/declared.ts +38 -0
- package/src/localAccess/keyFiles.ts +126 -0
- package/src/utils/__tests__/writePrivateFile.test.ts +40 -0
- package/src/utils/writePrivateFile.ts +20 -0
- package/src/vault/BitwardenVault.ts +8 -9
- package/src/vault/VaultManager.ts +28 -0
- package/src/vault/__tests__/BitwardenVault.test.ts +4 -4
- package/src/vault/__tests__/cache.test.ts +37 -0
- package/src/vault/bw.ts +34 -9
- package/src/vault/cache.ts +5 -2
|
@@ -6,7 +6,7 @@ description: Managing environment variables and secrets in a catladder project.
|
|
|
6
6
|
# Env vars and secrets with catladder
|
|
7
7
|
|
|
8
8
|
Environment variables are declared per component in `catladder.ts`;
|
|
9
|
-
secret
|
|
9
|
+
secret _values_ are never checked into the repo.
|
|
10
10
|
|
|
11
11
|
## Declaring variables
|
|
12
12
|
|
|
@@ -93,7 +93,7 @@ A few secrets are not typed in by a human but created by
|
|
|
93
93
|
(`GCLOUD_DEPLOY_credentialsKey`) and the kubernetes deploy credentials
|
|
94
94
|
(`KUBE_TOKEN`, `KUBE_CA_PEM`, `KUBE_URL`). They travel the same route as
|
|
95
95
|
every other secret — vault first, then the enabled CI backends — but
|
|
96
|
-
they are
|
|
96
|
+
they are _hidden_: they never show up in `secrets-list`, in the editor
|
|
97
97
|
or in a pulled document, and they must never be set by hand.
|
|
98
98
|
|
|
99
99
|
Re-running `project setup` **rotates** them (a fresh credential is
|
|
@@ -105,7 +105,7 @@ over, so re-run `project setup` to provision them into the new vault.
|
|
|
105
105
|
## GitHub pipelines
|
|
106
106
|
|
|
107
107
|
GitHub Actions cannot read the vault at runtime; secrets are mirrored
|
|
108
|
-
into GitHub
|
|
108
|
+
into GitHub _environment_ secrets (one GitHub environment per catladder
|
|
109
109
|
env). Sync them with:
|
|
110
110
|
|
|
111
111
|
```sh
|
|
@@ -122,6 +122,38 @@ direnv) for the `local` environment. If a var is missing locally,
|
|
|
122
122
|
check its declaration in `catladder.ts` and re-run `yarn catenv` before
|
|
123
123
|
suspecting anything else.
|
|
124
124
|
|
|
125
|
+
### Read-only cloud access for local use
|
|
126
|
+
|
|
127
|
+
For google cloud run components, `localAccess` in `catladder.ts`
|
|
128
|
+
(project default at the top level, per env in `environments.<env>`,
|
|
129
|
+
`false` to disable) declares read-only access for local use, e.g.
|
|
130
|
+
`localAccess: { gcp: { presets: ["logs"] } }`. `catladder project setup`
|
|
131
|
+
creates a service account per component/env with exactly those roles and
|
|
132
|
+
stores its key in the secret store under the `local` env (never mirrored
|
|
133
|
+
to CI); `catladder project doctor` reports drift. `catenv` writes the key
|
|
134
|
+
to `.catladder/local-access/gcp-<env>-<component>.json` (0600, gitignored)
|
|
135
|
+
and exports its path as `CATLADDER_LOCAL_ACCESS_GCP_<ENV>_KEY_FILE` — not
|
|
136
|
+
as `GOOGLE_APPLICATION_CREDENTIALS`, use it with an isolated
|
|
137
|
+
`CLOUDSDK_CONFIG`, never `gcloud auth` on the personal config. The
|
|
138
|
+
`logs` preset is `roles/logging.viewer` only (project-wide); extra `roles`
|
|
139
|
+
must be `roles/{logging,monitoring,errorreporting}.viewer` (never `roles/viewer`/`roles/run.viewer`:
|
|
140
|
+
cloud run revisions carry secrets as plain env vars), unless
|
|
141
|
+
`allowAnyRole: true` (also needed for custom roles); write/admin roles are always rejected. Needs a
|
|
142
|
+
non-CI vault (e.g. bitwarden) — `project setup` refuses with the gitlab
|
|
143
|
+
vault. After a key rotation on another machine, run
|
|
144
|
+
`catenv --vault-mode refresh`.
|
|
145
|
+
|
|
146
|
+
The `vertex` preset grants `roles/aiplatform.user` (to call Vertex AI
|
|
147
|
+
models, e.g. local evals). It is **not** read-only: it can use and bill
|
|
148
|
+
every Vertex AI API of the project and create/change/delete Vertex AI
|
|
149
|
+
resources; it is only accepted as a preset, never in `roles`. Add
|
|
150
|
+
`credentialsEnvVar: "GOOGLE_APPLICATION_CREDENTIALS"` (in one env only)
|
|
151
|
+
to have catenv export the key path under that name as well, so google
|
|
152
|
+
client libraries use it without `gcloud auth application-default login`.
|
|
153
|
+
The variable lands in the component's `.env` (e.g. `apps/web/.env`), not
|
|
154
|
+
in a root `.env`: scripts run from the repo root must load that file
|
|
155
|
+
(`node --env-file-if-exists=apps/web/.env …`).
|
|
156
|
+
|
|
125
157
|
## Related skills
|
|
126
158
|
|
|
127
159
|
- `catladder-config` — general catladder.ts structure and regeneration
|
|
@@ -36,4 +36,5 @@ __exportStar(require("./utils/measureTime"), exports);
|
|
|
36
36
|
__exportStar(require("../../bash/src/index.js"), exports);
|
|
37
37
|
__exportStar(require("./catenv"), exports);
|
|
38
38
|
__exportStar(require("./agentSkills"), exports);
|
|
39
|
+
__exportStar(require("./localAccess"), exports);
|
|
39
40
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/pipeline/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,6CAA2B;AAC3B,+CAA6B;AAC7B,6CAA2B;AAC3B,0CAAwB;AACxB,0CAAwB;AACxB,2CAAyB;AACzB,+DAA6C;AAC7C,+DAA6C;AAC7C,2CAAyB;AACzB,0CAAwB;AACxB,4CAA0B;AAC1B,0CAAwB;AACxB,2CAAyB;AACzB,2CAAyB;AACzB,kDAAgC;AAChC,4CAA0B;AAC1B,qDAAmC;AACnC,sDAAoC;AACpC,kDAAgC;AAChC,2CAAyB;AACzB,gDAA8B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/pipeline/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,6CAA2B;AAC3B,+CAA6B;AAC7B,6CAA2B;AAC3B,0CAAwB;AACxB,0CAAwB;AACxB,2CAAyB;AACzB,+DAA6C;AAC7C,+DAA6C;AAC7C,2CAAyB;AACzB,0CAAwB;AACxB,4CAA0B;AAC1B,0CAAwB;AACxB,2CAAyB;AACzB,2CAAyB;AACzB,kDAAgC;AAChC,4CAA0B;AAC1B,qDAAmC;AACnC,sDAAoC;AACpC,kDAAgC;AAChC,2CAAyB;AACzB,gDAA8B;AAC9B,gDAA8B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Config, LocalAccessConfig, LocalAccessGcpPreset } from "../types";
|
|
2
|
+
export declare const LOCAL_ACCESS_GCP_PRESETS: Record<LocalAccessGcpPreset, string[]>;
|
|
3
|
+
/** name prefix of the service account `project setup` creates (see getGcloudServiceAccountNames) */
|
|
4
|
+
export declare const LOCAL_ACCESS_GCP_SA_NAME = "local-diag";
|
|
5
|
+
/**
|
|
6
|
+
* why a role may not be granted for local access, or undefined if it
|
|
7
|
+
* is acceptable.
|
|
8
|
+
*
|
|
9
|
+
* By default only the roles in {@link SAFE_ROLES} pass; custom roles
|
|
10
|
+
* and everything else need `allowAnyRole`. `allowAnyRole` lifts the
|
|
11
|
+
* allow-list, but the obvious write/admin/impersonation roles and the
|
|
12
|
+
* secret exposing roles stay denied.
|
|
13
|
+
*/
|
|
14
|
+
export declare const getDeniedLocalAccessRoleReason: (role: string, options?: {
|
|
15
|
+
allowAnyRole?: boolean;
|
|
16
|
+
}) => string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* the effective local access declaration of an env: the env's own
|
|
19
|
+
* declaration replaces the project default entirely
|
|
20
|
+
*/
|
|
21
|
+
export declare const getLocalAccessConfig: (config: Pick<Config, "localAccess" | "environments">, env: string) => Exclude<LocalAccessConfig, false> | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* the gcp roles local access grants in `env`: preset roles plus the
|
|
24
|
+
* explicit ones, deduplicated and sorted. Throws on an unknown preset,
|
|
25
|
+
* a malformed role or a denied (write/admin) role.
|
|
26
|
+
*/
|
|
27
|
+
export declare const getLocalAccessGcpRoles: (config: Pick<Config, "localAccess" | "environments">, env: string) => string[];
|
|
28
|
+
/**
|
|
29
|
+
* the envs of a component that declare gcp local access. Empty for
|
|
30
|
+
* components that do not deploy to google cloud run.
|
|
31
|
+
*/
|
|
32
|
+
export declare const getLocalAccessGcpEnvs: (config: Config, componentName: string) => string[];
|
|
33
|
+
/**
|
|
34
|
+
* name of the secret (stored under the `local` env of the component)
|
|
35
|
+
* holding the service account key of `env`'s local access
|
|
36
|
+
*/
|
|
37
|
+
export declare const getLocalAccessGcpSecretName: (env: string) => string;
|
|
38
|
+
/**
|
|
39
|
+
* the bindings to add and remove so that `current` matches `desired`.
|
|
40
|
+
* The local access account is dedicated, so every binding it carries
|
|
41
|
+
* beyond the declaration is extra.
|
|
42
|
+
*/
|
|
43
|
+
export declare const diffRoleBindings: (desired: string[], current: string[]) => {
|
|
44
|
+
missing: string[];
|
|
45
|
+
extra: string[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* the extra env var names (`credentialsEnvVar`) under which the local
|
|
49
|
+
* env of a component exports the key file path, mapped to the env whose
|
|
50
|
+
* key they point at. Throws on a malformed name or on two envs
|
|
51
|
+
* exporting the same name (e.g. a project default declaring it).
|
|
52
|
+
*/
|
|
53
|
+
export declare const getLocalAccessGcpCredentialsEnvVars: (config: Config, componentName: string) => Record<string, string>;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLocalAccessGcpCredentialsEnvVars = exports.diffRoleBindings = exports.getLocalAccessGcpSecretName = exports.getLocalAccessGcpEnvs = exports.getLocalAccessGcpRoles = exports.getLocalAccessConfig = exports.getDeniedLocalAccessRoleReason = exports.LOCAL_ACCESS_GCP_SA_NAME = exports.LOCAL_ACCESS_GCP_PRESETS = void 0;
|
|
4
|
+
const configruedEnvs_1 = require("../config/configruedEnvs");
|
|
5
|
+
const getEnvConfig_1 = require("../context/getEnvConfig");
|
|
6
|
+
const types_1 = require("../deploy/types");
|
|
7
|
+
exports.LOCAL_ACCESS_GCP_PRESETS = {
|
|
8
|
+
// no run.viewer on purpose, see LocalAccessGcpPreset
|
|
9
|
+
logs: ["roles/logging.viewer"],
|
|
10
|
+
// not read-only, see LocalAccessGcpPreset; only reachable as a
|
|
11
|
+
// preset, getDeniedLocalAccessRoleReason rejects it in `roles`
|
|
12
|
+
vertex: ["roles/aiplatform.user"],
|
|
13
|
+
};
|
|
14
|
+
/** name prefix of the service account `project setup` creates (see getGcloudServiceAccountNames) */
|
|
15
|
+
exports.LOCAL_ACCESS_GCP_SA_NAME = "local-diag";
|
|
16
|
+
const ROLE_FORMAT = /^(roles\/[A-Za-z0-9_.]+|projects\/[a-z0-9-]+\/roles\/[A-Za-z0-9_.]+|organizations\/\d+\/roles\/[A-Za-z0-9_.]+)$/;
|
|
17
|
+
/**
|
|
18
|
+
* roles that are read-only on paper but expose secrets: cloud run
|
|
19
|
+
* services/jobs carry their secrets as plain env vars, so anything that
|
|
20
|
+
* may read them reads every deployed secret. Denied even with
|
|
21
|
+
* `allowAnyRole`.
|
|
22
|
+
*/
|
|
23
|
+
const SECRET_EXPOSING_ROLES = {
|
|
24
|
+
"roles/viewer": "roles/viewer can read cloud run env vars (secrets)",
|
|
25
|
+
"roles/run.viewer": "roles/run.viewer can read cloud run env vars (secrets)",
|
|
26
|
+
"roles/secretmanager.secretAccessor": "this role reads secret values",
|
|
27
|
+
"roles/secretmanager.admin": "this role reads secret values",
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* roles granted without `allowAnyRole`. An explicit list instead of a
|
|
31
|
+
* name pattern: a role called `*.viewer` / `*.reader` says nothing about
|
|
32
|
+
* its permissions (custom roles can hold anything, and predefined ones
|
|
33
|
+
* like storage.objectViewer, cloudfunctions.viewer, compute.viewer or
|
|
34
|
+
* logging.privateLogViewer expose data or secrets).
|
|
35
|
+
*/
|
|
36
|
+
const SAFE_ROLES = [
|
|
37
|
+
"roles/logging.viewer",
|
|
38
|
+
"roles/monitoring.viewer",
|
|
39
|
+
"roles/errorreporting.viewer",
|
|
40
|
+
];
|
|
41
|
+
/** `roles/foo.bar.v1` -> `foo.bar` (version suffixes must not defeat the checks) */
|
|
42
|
+
const getRoleName = (role) => { var _a; return ((_a = role.split("/").pop()) !== null && _a !== void 0 ? _a : role).replace(/(\.v\d+)+$/i, ""); };
|
|
43
|
+
/**
|
|
44
|
+
* why a role may not be granted for local access, or undefined if it
|
|
45
|
+
* is acceptable.
|
|
46
|
+
*
|
|
47
|
+
* By default only the roles in {@link SAFE_ROLES} pass; custom roles
|
|
48
|
+
* and everything else need `allowAnyRole`. `allowAnyRole` lifts the
|
|
49
|
+
* allow-list, but the obvious write/admin/impersonation roles and the
|
|
50
|
+
* secret exposing roles stay denied.
|
|
51
|
+
*/
|
|
52
|
+
const getDeniedLocalAccessRoleReason = (role, options = {}) => {
|
|
53
|
+
var _a;
|
|
54
|
+
const secretReason = SECRET_EXPOSING_ROLES[role];
|
|
55
|
+
if (secretReason)
|
|
56
|
+
return secretReason;
|
|
57
|
+
const name = getRoleName(role);
|
|
58
|
+
if (name === "owner" || name === "editor") {
|
|
59
|
+
return "owner/editor are full write roles";
|
|
60
|
+
}
|
|
61
|
+
const last = (_a = name.split(".").pop()) !== null && _a !== void 0 ? _a : name;
|
|
62
|
+
if (/admin$/i.test(last)) {
|
|
63
|
+
return "admin roles can change resources";
|
|
64
|
+
}
|
|
65
|
+
if (/(editor|developer|writer|creator|deployer|invoker|builder|publisher|client|user|objectUser)$/i.test(last) &&
|
|
66
|
+
!/^iam\./.test(name)) {
|
|
67
|
+
return "this role can change resources";
|
|
68
|
+
}
|
|
69
|
+
if (/^iam\./.test(name) &&
|
|
70
|
+
/(serviceAccountUser|serviceAccountTokenCreator|serviceAccountKeyAdmin|workloadIdentityUser)$/.test(name)) {
|
|
71
|
+
return "this role allows impersonating service accounts";
|
|
72
|
+
}
|
|
73
|
+
if (!options.allowAnyRole && !SAFE_ROLES.includes(role)) {
|
|
74
|
+
return `only these roles are allowed: ${SAFE_ROLES.join(", ")} (set allowAnyRole: true to grant others at your own risk)`;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
};
|
|
78
|
+
exports.getDeniedLocalAccessRoleReason = getDeniedLocalAccessRoleReason;
|
|
79
|
+
/**
|
|
80
|
+
* the effective local access declaration of an env: the env's own
|
|
81
|
+
* declaration replaces the project default entirely
|
|
82
|
+
*/
|
|
83
|
+
const getLocalAccessConfig = (config, env) => {
|
|
84
|
+
var _a;
|
|
85
|
+
if (env === "local")
|
|
86
|
+
return undefined;
|
|
87
|
+
const envEntry = (_a = config.environments) === null || _a === void 0 ? void 0 : _a[env];
|
|
88
|
+
const declared = envEntry && envEntry.localAccess !== undefined
|
|
89
|
+
? envEntry.localAccess
|
|
90
|
+
: config.localAccess;
|
|
91
|
+
return declared || undefined;
|
|
92
|
+
};
|
|
93
|
+
exports.getLocalAccessConfig = getLocalAccessConfig;
|
|
94
|
+
/**
|
|
95
|
+
* the gcp roles local access grants in `env`: preset roles plus the
|
|
96
|
+
* explicit ones, deduplicated and sorted. Throws on an unknown preset,
|
|
97
|
+
* a malformed role or a denied (write/admin) role.
|
|
98
|
+
*/
|
|
99
|
+
const getLocalAccessGcpRoles = (config, env) => {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
const gcp = (_a = (0, exports.getLocalAccessConfig)(config, env)) === null || _a === void 0 ? void 0 : _a.gcp;
|
|
102
|
+
if (!gcp)
|
|
103
|
+
return [];
|
|
104
|
+
const roles = new Set();
|
|
105
|
+
for (const preset of (_b = gcp.presets) !== null && _b !== void 0 ? _b : []) {
|
|
106
|
+
const presetRoles = exports.LOCAL_ACCESS_GCP_PRESETS[preset];
|
|
107
|
+
if (!presetRoles) {
|
|
108
|
+
throw new Error(`localAccess (${env}): unknown gcp preset '${preset}', known: ${Object.keys(exports.LOCAL_ACCESS_GCP_PRESETS).join(", ")}`);
|
|
109
|
+
}
|
|
110
|
+
presetRoles.forEach((role) => roles.add(role));
|
|
111
|
+
}
|
|
112
|
+
for (const role of (_c = gcp.roles) !== null && _c !== void 0 ? _c : []) {
|
|
113
|
+
if (!ROLE_FORMAT.test(role)) {
|
|
114
|
+
throw new Error(`localAccess (${env}): '${role}' is not a valid role (expected e.g. roles/logging.viewer)`);
|
|
115
|
+
}
|
|
116
|
+
const reason = (0, exports.getDeniedLocalAccessRoleReason)(role, {
|
|
117
|
+
allowAnyRole: gcp.allowAnyRole,
|
|
118
|
+
});
|
|
119
|
+
if (reason) {
|
|
120
|
+
throw new Error(`localAccess (${env}): role '${role}' is not allowed (${reason})`);
|
|
121
|
+
}
|
|
122
|
+
roles.add(role);
|
|
123
|
+
}
|
|
124
|
+
return [...roles].sort();
|
|
125
|
+
};
|
|
126
|
+
exports.getLocalAccessGcpRoles = getLocalAccessGcpRoles;
|
|
127
|
+
/**
|
|
128
|
+
* the envs of a component that declare gcp local access. Empty for
|
|
129
|
+
* components that do not deploy to google cloud run.
|
|
130
|
+
*/
|
|
131
|
+
const getLocalAccessGcpEnvs = (config, componentName) => (0, configruedEnvs_1.getAllEnvs)(config, componentName).filter((env) =>
|
|
132
|
+
// only google cloud run envs have a project to grant roles in; the
|
|
133
|
+
// env's own `deploy` overrides the component's
|
|
134
|
+
(0, types_1.isOfDeployType)((0, getEnvConfig_1.getEnvConfig)(config, componentName, env).deploy, "google-cloudrun") && (0, exports.getLocalAccessGcpRoles)(config, env).length > 0);
|
|
135
|
+
exports.getLocalAccessGcpEnvs = getLocalAccessGcpEnvs;
|
|
136
|
+
/**
|
|
137
|
+
* name of the secret (stored under the `local` env of the component)
|
|
138
|
+
* holding the service account key of `env`'s local access
|
|
139
|
+
*/
|
|
140
|
+
const getLocalAccessGcpSecretName = (env) => `LOCAL_ACCESS_GCP_${env.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}_CREDENTIALS`;
|
|
141
|
+
exports.getLocalAccessGcpSecretName = getLocalAccessGcpSecretName;
|
|
142
|
+
/**
|
|
143
|
+
* the bindings to add and remove so that `current` matches `desired`.
|
|
144
|
+
* The local access account is dedicated, so every binding it carries
|
|
145
|
+
* beyond the declaration is extra.
|
|
146
|
+
*/
|
|
147
|
+
const diffRoleBindings = (desired, current) => ({
|
|
148
|
+
missing: desired.filter((role) => !current.includes(role)),
|
|
149
|
+
extra: current.filter((role) => !desired.includes(role)),
|
|
150
|
+
});
|
|
151
|
+
exports.diffRoleBindings = diffRoleBindings;
|
|
152
|
+
const ENV_VAR_NAME_FORMAT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
153
|
+
/**
|
|
154
|
+
* the extra env var names (`credentialsEnvVar`) under which the local
|
|
155
|
+
* env of a component exports the key file path, mapped to the env whose
|
|
156
|
+
* key they point at. Throws on a malformed name or on two envs
|
|
157
|
+
* exporting the same name (e.g. a project default declaring it).
|
|
158
|
+
*/
|
|
159
|
+
const getLocalAccessGcpCredentialsEnvVars = (config, componentName) => {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
const vars = {};
|
|
162
|
+
for (const env of (0, exports.getLocalAccessGcpEnvs)(config, componentName)) {
|
|
163
|
+
const name = (_b = (_a = (0, exports.getLocalAccessConfig)(config, env)) === null || _a === void 0 ? void 0 : _a.gcp) === null || _b === void 0 ? void 0 : _b.credentialsEnvVar;
|
|
164
|
+
if (!name)
|
|
165
|
+
continue;
|
|
166
|
+
if (!ENV_VAR_NAME_FORMAT.test(name)) {
|
|
167
|
+
throw new Error(`localAccess (${env}): credentialsEnvVar '${name}' is not a valid env var name`);
|
|
168
|
+
}
|
|
169
|
+
if (vars[name]) {
|
|
170
|
+
throw new Error(`localAccess (${componentName}): credentialsEnvVar '${name}' is declared by both ${vars[name]} and ${env}; declare it in one env only (environments.<env>.localAccess)`);
|
|
171
|
+
}
|
|
172
|
+
vars[name] = env;
|
|
173
|
+
}
|
|
174
|
+
return vars;
|
|
175
|
+
};
|
|
176
|
+
exports.getLocalAccessGcpCredentialsEnvVars = getLocalAccessGcpCredentialsEnvVars;
|
|
177
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/localAccess/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -201,6 +201,13 @@ export type EnvironmentConfig = {
|
|
|
201
201
|
* axes separately.
|
|
202
202
|
*/
|
|
203
203
|
inherit?: EnvironmentInheritConfig;
|
|
204
|
+
/**
|
|
205
|
+
* read-only access to this env's cloud resources for LOCAL use
|
|
206
|
+
* (agents, scripts), see {@link LocalAccessConfig}. Overrides the
|
|
207
|
+
* project default (top-level `localAccess`) entirely; `false`
|
|
208
|
+
* switches it off for this env.
|
|
209
|
+
*/
|
|
210
|
+
localAccess?: LocalAccessConfig;
|
|
204
211
|
};
|
|
205
212
|
/**
|
|
206
213
|
* what an environment inherits from another environment.
|
|
@@ -236,6 +243,65 @@ export type EnvironmentsConfig = Record<string, EnvironmentConfig | false>;
|
|
|
236
243
|
*/
|
|
237
244
|
export declare const getEnvironmentConfig: (environments: EnvironmentsConfig | undefined, env: string) => EnvironmentConfig | undefined;
|
|
238
245
|
export declare const isEnvironmentDisabled: (environments: EnvironmentsConfig | undefined, env: string) => boolean;
|
|
246
|
+
/**
|
|
247
|
+
* named bundles of gcp roles for {@link LocalAccessGcpConfig}
|
|
248
|
+
*
|
|
249
|
+
* - `logs`: `roles/logging.viewer` only. Deliberately NOT
|
|
250
|
+
* `roles/run.viewer`: cloud run services/jobs are deployed with
|
|
251
|
+
* their secrets as plain env vars, so anything with `run.*.get`
|
|
252
|
+
* can read every deployed secret.
|
|
253
|
+
* - `vertex`: `roles/aiplatform.user`, to call Vertex AI models (e.g.
|
|
254
|
+
* for local evals). NOT read-only: it may use (and bill) every Vertex
|
|
255
|
+
* AI API of the project and create, change and delete Vertex AI
|
|
256
|
+
* resources (datasets, endpoints, models, jobs). It reads no other
|
|
257
|
+
* service's data. Only available as a preset, `roles` rejects it.
|
|
258
|
+
*/
|
|
259
|
+
export type LocalAccessGcpPreset = "logs" | "vertex";
|
|
260
|
+
export type LocalAccessGcpConfig = {
|
|
261
|
+
/** preset role bundles, see {@link LocalAccessGcpPreset} */
|
|
262
|
+
presets?: LocalAccessGcpPreset[];
|
|
263
|
+
/**
|
|
264
|
+
* additional explicit roles (e.g. `roles/monitoring.viewer`).
|
|
265
|
+
* Write/admin roles are rejected; everything else you grant here is
|
|
266
|
+
* your responsibility — the key file lives on developer machines.
|
|
267
|
+
*/
|
|
268
|
+
roles?: string[];
|
|
269
|
+
/**
|
|
270
|
+
* lifts the default allow-list (only a few known-safe roles:
|
|
271
|
+
* logging/monitoring/errorreporting viewers; custom roles need this).
|
|
272
|
+
* Write/admin/impersonation roles and roles exposing secrets
|
|
273
|
+
* (`roles/viewer`, `roles/run.viewer`, secret accessors) stay denied.
|
|
274
|
+
* What you grant beyond the allow-list is your responsibility.
|
|
275
|
+
*/
|
|
276
|
+
allowAnyRole?: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* also export the key file path under this env var name in the
|
|
279
|
+
* `local` env of the component (in addition to
|
|
280
|
+
* `CATLADDER_LOCAL_ACCESS_GCP_<ENV>_KEY_FILE`), e.g.
|
|
281
|
+
* `GOOGLE_APPLICATION_CREDENTIALS` so google client libraries use the
|
|
282
|
+
* key instead of a personal ADC login. Every process that loads the
|
|
283
|
+
* component's env then acts as the service account, so declare it in
|
|
284
|
+
* one env only (two envs exporting the same name are rejected).
|
|
285
|
+
* Overrides a var of the same name declared in `vars` while the key
|
|
286
|
+
* file exists.
|
|
287
|
+
*/
|
|
288
|
+
credentialsEnvVar?: string;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* which access LOCAL use gets for an env (read-only, unless a preset
|
|
292
|
+
* like `vertex` says otherwise). `project setup`
|
|
293
|
+
* creates a dedicated service account per component/env, grants exactly
|
|
294
|
+
* these bindings (and removes bindings no longer declared) and stores
|
|
295
|
+
* its key in the secret store under the `local` env — never in CI
|
|
296
|
+
* variables. catenv writes the key to a gitignored file.
|
|
297
|
+
*
|
|
298
|
+
* `false` disables it. Without a declaration nothing is created: the
|
|
299
|
+
* env stays reachable with the personal gcloud login only.
|
|
300
|
+
*/
|
|
301
|
+
export type LocalAccessConfig = {
|
|
302
|
+
/** google cloud (only for components deploying to google cloud run) */
|
|
303
|
+
gcp?: LocalAccessGcpConfig;
|
|
304
|
+
} | false;
|
|
239
305
|
export type EnvironmentInheritConfig = string | {
|
|
240
306
|
/** env whose per-component config overrides are inherited */
|
|
241
307
|
config?: string;
|
|
@@ -451,6 +517,15 @@ export type Config<C extends ConfigProps = never> = {
|
|
|
451
517
|
* A single component opts out with `env.<name>: false` instead.
|
|
452
518
|
*/
|
|
453
519
|
environments?: EnvironmentsConfig;
|
|
520
|
+
/**
|
|
521
|
+
* project default for the read-only access LOCAL use gets per env
|
|
522
|
+
* (see {@link LocalAccessConfig}), overridable per env in
|
|
523
|
+
* `environments.<env>.localAccess`. Never applies to the `local` env.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* localAccess: { gcp: { presets: ["logs"] } }
|
|
527
|
+
*/
|
|
528
|
+
localAccess?: LocalAccessConfig;
|
|
454
529
|
/**
|
|
455
530
|
* components (sub apps)
|
|
456
531
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/config.ts"],"names":[],"mappings":";;;AAmBa,QAAA,qBAAqB,GAAG;IACnC,YAAY;IACZ,IAAI;IACJ,eAAe;CACP,CAAC;AAoBJ,MAAM,eAAe,GAAG,CAC7B,OAA2B,EACO,EAAE,CACpC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,IAAI,OAAO,CAAC;AAH5D,QAAA,eAAe,mBAG6C;AAElE,MAAM,gBAAgB,GAAG,CAC9B,CAA0B,EAC1B,CAAqB,EACZ,EAAE;IACX,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,IAAA,uBAAe,EAAC,CAAC,CAAC;QAAE,OAAO,IAAA,uBAAe,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;IAC3E,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC,CAAC;AAPW,QAAA,gBAAgB,oBAO3B;AAEF;;;GAGG;AACU,QAAA,SAAS,GAAG;IACvB,GAAG,EAAE;QACH,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;CACO,CAAC;AAEE,QAAA,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAS,CAAC,CAAC;AACtC,QAAA,iBAAiB,GAAG,oBAAyB,CAAC;AAGpD,MAAM,aAAa,GAAG,CAAC,GAAW,EAAkB,EAAE;IAC3D,OAAO,GAAG,IAAI,iBAAS,CAAC;AAC1B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/config.ts"],"names":[],"mappings":";;;AAmBa,QAAA,qBAAqB,GAAG;IACnC,YAAY;IACZ,IAAI;IACJ,eAAe;CACP,CAAC;AAoBJ,MAAM,eAAe,GAAG,CAC7B,OAA2B,EACO,EAAE,CACpC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,IAAI,OAAO,CAAC;AAH5D,QAAA,eAAe,mBAG6C;AAElE,MAAM,gBAAgB,GAAG,CAC9B,CAA0B,EAC1B,CAAqB,EACZ,EAAE;IACX,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,IAAA,uBAAe,EAAC,CAAC,CAAC;QAAE,OAAO,IAAA,uBAAe,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;IAC3E,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC,CAAC;AAPW,QAAA,gBAAgB,oBAO3B;AAEF;;;GAGG;AACU,QAAA,SAAS,GAAG;IACvB,GAAG,EAAE;QACH,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;CACO,CAAC;AAEE,QAAA,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAS,CAAC,CAAC;AACtC,QAAA,iBAAiB,GAAG,oBAAyB,CAAC;AAGpD,MAAM,aAAa,GAAG,CAAC,GAAW,EAAkB,EAAE;IAC3D,OAAO,GAAG,IAAI,iBAAS,CAAC;AAC1B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB;AA2MF;;;GAGG;AACI,MAAM,oBAAoB,GAAG,CAClC,YAA4C,EAC5C,GAAW,EACoB,EAAE,CAAC,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAG,GAAG,CAAC,KAAI,SAAS,CAAC;AAHxD,QAAA,oBAAoB,wBAGoC;AAE9D,MAAM,qBAAqB,GAAG,CACnC,YAA4C,EAC5C,GAAW,EACF,EAAE,CAAC,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAG,GAAG,CAAC,MAAK,KAAK,CAAC;AAH/B,QAAA,qBAAqB,yBAGU"}
|
|
@@ -6,7 +6,7 @@ description: Managing environment variables and secrets in a catladder project.
|
|
|
6
6
|
# Env vars and secrets with catladder
|
|
7
7
|
|
|
8
8
|
Environment variables are declared per component in `catladder.ts`;
|
|
9
|
-
secret
|
|
9
|
+
secret _values_ are never checked into the repo.
|
|
10
10
|
|
|
11
11
|
## Declaring variables
|
|
12
12
|
|
|
@@ -93,7 +93,7 @@ A few secrets are not typed in by a human but created by
|
|
|
93
93
|
(`GCLOUD_DEPLOY_credentialsKey`) and the kubernetes deploy credentials
|
|
94
94
|
(`KUBE_TOKEN`, `KUBE_CA_PEM`, `KUBE_URL`). They travel the same route as
|
|
95
95
|
every other secret — vault first, then the enabled CI backends — but
|
|
96
|
-
they are
|
|
96
|
+
they are _hidden_: they never show up in `secrets-list`, in the editor
|
|
97
97
|
or in a pulled document, and they must never be set by hand.
|
|
98
98
|
|
|
99
99
|
Re-running `project setup` **rotates** them (a fresh credential is
|
|
@@ -105,7 +105,7 @@ over, so re-run `project setup` to provision them into the new vault.
|
|
|
105
105
|
## GitHub pipelines
|
|
106
106
|
|
|
107
107
|
GitHub Actions cannot read the vault at runtime; secrets are mirrored
|
|
108
|
-
into GitHub
|
|
108
|
+
into GitHub _environment_ secrets (one GitHub environment per catladder
|
|
109
109
|
env). Sync them with:
|
|
110
110
|
|
|
111
111
|
```sh
|
|
@@ -122,6 +122,38 @@ direnv) for the `local` environment. If a var is missing locally,
|
|
|
122
122
|
check its declaration in `catladder.ts` and re-run `yarn catenv` before
|
|
123
123
|
suspecting anything else.
|
|
124
124
|
|
|
125
|
+
### Read-only cloud access for local use
|
|
126
|
+
|
|
127
|
+
For google cloud run components, `localAccess` in `catladder.ts`
|
|
128
|
+
(project default at the top level, per env in `environments.<env>`,
|
|
129
|
+
`false` to disable) declares read-only access for local use, e.g.
|
|
130
|
+
`localAccess: { gcp: { presets: ["logs"] } }`. `catladder project setup`
|
|
131
|
+
creates a service account per component/env with exactly those roles and
|
|
132
|
+
stores its key in the secret store under the `local` env (never mirrored
|
|
133
|
+
to CI); `catladder project doctor` reports drift. `catenv` writes the key
|
|
134
|
+
to `.catladder/local-access/gcp-<env>-<component>.json` (0600, gitignored)
|
|
135
|
+
and exports its path as `CATLADDER_LOCAL_ACCESS_GCP_<ENV>_KEY_FILE` — not
|
|
136
|
+
as `GOOGLE_APPLICATION_CREDENTIALS`, use it with an isolated
|
|
137
|
+
`CLOUDSDK_CONFIG`, never `gcloud auth` on the personal config. The
|
|
138
|
+
`logs` preset is `roles/logging.viewer` only (project-wide); extra `roles`
|
|
139
|
+
must be `roles/{logging,monitoring,errorreporting}.viewer` (never `roles/viewer`/`roles/run.viewer`:
|
|
140
|
+
cloud run revisions carry secrets as plain env vars), unless
|
|
141
|
+
`allowAnyRole: true` (also needed for custom roles); write/admin roles are always rejected. Needs a
|
|
142
|
+
non-CI vault (e.g. bitwarden) — `project setup` refuses with the gitlab
|
|
143
|
+
vault. After a key rotation on another machine, run
|
|
144
|
+
`catenv --vault-mode refresh`.
|
|
145
|
+
|
|
146
|
+
The `vertex` preset grants `roles/aiplatform.user` (to call Vertex AI
|
|
147
|
+
models, e.g. local evals). It is **not** read-only: it can use and bill
|
|
148
|
+
every Vertex AI API of the project and create/change/delete Vertex AI
|
|
149
|
+
resources; it is only accepted as a preset, never in `roles`. Add
|
|
150
|
+
`credentialsEnvVar: "GOOGLE_APPLICATION_CREDENTIALS"` (in one env only)
|
|
151
|
+
to have catenv export the key path under that name as well, so google
|
|
152
|
+
client libraries use it without `gcloud auth application-default login`.
|
|
153
|
+
The variable lands in the component's `.env` (e.g. `apps/web/.env`), not
|
|
154
|
+
in a root `.env`: scripts run from the repo root must load that file
|
|
155
|
+
(`node --env-file-if-exists=apps/web/.env …`).
|
|
156
|
+
|
|
125
157
|
## Related skills
|
|
126
158
|
|
|
127
159
|
- `catladder-config` — general catladder.ts structure and regeneration
|