@himorogy/enclave-env 0.2.0 → 0.2.1
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 +85 -87
- package/dist/cli.js +23 -5
- package/package.json +3 -3
- package/scripts/check.sh +1 -1
- package/scripts/init-check-dev.sh +1 -1
package/README.md
CHANGED
|
@@ -4,119 +4,77 @@
|
|
|
4
4
|
|
|
5
5
|
[dotenvx](https://dotenvx.com/) による暗号化と実行時ガードを組み合わせ、LLM が動く devcontainer から prod キーを構造的に隔離します。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| 種別 | 内容 |
|
|
10
|
-
|---|---|
|
|
11
|
-
| **CLI** | `encrypt` / `decrypt` — env ファイルの暗号化・復号 |
|
|
12
|
-
| **CLI** | `check` — 平文 `.env*` のコミットをブロック(pre-commit hook 用) |
|
|
13
|
-
| **シェルスクリプト** | `scripts/init-check-dev.sh` — dev コンテナ起動時のセキュリティチェック(`initializeCommand` 用) |
|
|
14
|
-
| **シェルスクリプト** | `scripts/init-check-prod.sh` — prod コンテナ起動時の相互排他チェック(`initializeCommand` 用) |
|
|
15
|
-
| **テンプレート** | `templates/` — prod-shell スクリプト・devcontainer 構成の参照実装 |
|
|
16
|
-
|
|
17
|
-
**このパッケージが担わないもの:** devcontainer.json の作成・管理、prod 環境の構築。これらはプロジェクト側の責務です。テンプレートはその参照実装として提供します。
|
|
18
|
-
|
|
19
|
-
# セキュリティ思想
|
|
7
|
+
---
|
|
20
8
|
|
|
21
|
-
|
|
9
|
+
# できること
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
prod用復号キーをワークスペース外に置くことで devcontainer への同期を防ぎ、 `.env.production` が平文の状態で devcontainer に同期されないように複数層でブロックします。
|
|
11
|
+
## 平文 env のコミットをブロックする
|
|
25
12
|
|
|
26
|
-
`.env
|
|
13
|
+
ステージ済みの `.env*` ファイルに暗号化されていないものがあればコミットを中断します。
|
|
27
14
|
|
|
28
|
-
```
|
|
29
|
-
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"simple-git-hooks": {
|
|
18
|
+
"pre-commit": "sh node_modules/@himorogy/enclave-env/scripts/check.sh"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
30
21
|
```
|
|
31
22
|
|
|
32
|
-
-
|
|
33
|
-
- 平文ファイルが一切ディスクに書き出されない
|
|
34
|
-
- dev コンテナ内でも実行可能(ただしコマンドヒストリーを削除しないと、LLMがセットした値を読み取ることが可能である点に留意)
|
|
35
|
-
|
|
36
|
-
## prod キー隔離の弊害
|
|
37
|
-
|
|
38
|
-
prod の private key を dev コンテナの外に置くと、dev コンテナ内から `.env.production` を復号できなくなります。
|
|
39
|
-
これは意図した動作ですが、複数変数を一覧で確認しながら編集したい場合や、prod deploy・DB メンテナンスを伴う作業には対応できません。そうしたワークフローのために prod 環境を用意します。
|
|
40
|
-
|
|
41
|
-
## 守るべき原則
|
|
42
|
-
|
|
43
|
-
prod 環境の実現手段は問いませんが、以下の 2 つを守ることがこのツールの前提です:
|
|
44
|
-
|
|
45
|
-
| 原則 | 目的 |
|
|
46
|
-
|---|---|
|
|
47
|
-
| **prod の private key は常にワークスペース外に置く** | bind mount 経由で LLM に渡らない |
|
|
48
|
-
| **prod 操作は dev コンテナが停止した状態で行う** | 復号した平文を LLM から守る |
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
# prod 環境の用意
|
|
53
|
-
|
|
54
|
-
## prod 環境の活用シナリオ
|
|
55
|
-
|
|
56
|
-
### 全復号して操作する
|
|
23
|
+
使用する機能:`scripts/check.sh`(内部で `enclave-env check` を呼び出し)
|
|
57
24
|
|
|
58
|
-
|
|
25
|
+
## env ファイルを暗号化・復号する
|
|
59
26
|
|
|
60
27
|
```sh
|
|
61
|
-
#
|
|
62
|
-
enclave-env decrypt --env
|
|
63
|
-
|
|
64
|
-
# 2. エディタで編集
|
|
65
|
-
vim .env.production
|
|
66
|
-
|
|
67
|
-
# 3. 再暗号化
|
|
68
|
-
enclave-env encrypt --env prod
|
|
28
|
+
enclave-env encrypt --env local # .env を in-place 暗号化
|
|
29
|
+
enclave-env decrypt --env local # .env を in-place 復号
|
|
69
30
|
```
|
|
70
31
|
|
|
71
|
-
`
|
|
32
|
+
`--env` に指定する名前は `enclave-env` 設定ファイルの `ENV_<NAME>_FILE` に対応します。
|
|
33
|
+
|
|
34
|
+
## prod secrets を LLM から守る
|
|
72
35
|
|
|
73
|
-
###
|
|
36
|
+
### 単一変数を追加・更新する
|
|
74
37
|
|
|
75
|
-
|
|
38
|
+
`dotenvx set` を使えば private key 不要で暗号化したまま書き込めます:
|
|
76
39
|
|
|
77
40
|
```sh
|
|
78
|
-
|
|
79
|
-
pnpm db:migrate
|
|
41
|
+
dotenvx set DATABASE_URL "postgres://..." -f .env.production
|
|
80
42
|
```
|
|
81
43
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
44
|
+
- `.env.production` の public key だけで暗号化するため private key 不要
|
|
45
|
+
- 平文ファイルが一切ディスクに書き出されない
|
|
46
|
+
- dev コンテナ内でも実行可能(コマンドヒストリー経由で LLM が値を読み取れる点に留意)
|
|
85
47
|
|
|
86
|
-
|
|
48
|
+
### prod キーをワークスペース外に置く
|
|
87
49
|
|
|
88
|
-
|
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
50
|
+
| 環境 | キーの保管場所 |
|
|
51
|
+
|---|---|
|
|
52
|
+
| local / dev | `.devcontainer/dev/.env.container`(gitignore 対象) |
|
|
53
|
+
| prod | `~/.config/<your-project>/.env.container`(ワークスペース外) |
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
prod キーをワークスペース外に置くことで bind mount 経由で LLM に渡るリスクをなくします。`ENV_PROD_PROTECTED=true` を設定すると、`decrypt` 実行時に dev コンテナが稼働中かどうかを確認しブロックします。
|
|
94
56
|
|
|
95
|
-
##
|
|
57
|
+
## prod 操作環境を用意する(全復号・deploy・DB)
|
|
96
58
|
|
|
97
|
-
prod
|
|
59
|
+
prod の private key を dev コンテナ外に置くと、コンテナ内から `.env.production` を復号できなくなります。これは意図した動作ですが、複数変数の編集や prod deploy には prod 専用環境が必要です。
|
|
98
60
|
|
|
99
61
|
### 選択肢 1:ホスト直実行
|
|
100
62
|
|
|
101
|
-
最もシンプル。dev
|
|
63
|
+
最もシンプル。dev コンテナを停止した状態でホストから実行します。ホストに Node.js と `enclave-env` のインストールが必要です。
|
|
102
64
|
|
|
103
65
|
```sh
|
|
104
66
|
# DOTENV_PRIVATE_KEY_PRODUCTION が使える状態で
|
|
105
67
|
pnpm decrypt-env:prod
|
|
106
68
|
```
|
|
107
69
|
|
|
108
|
-
ホストに Node.js と `enclave-env` のインストールが必要です。
|
|
109
|
-
|
|
110
70
|
### 選択肢 2:prod-shell スクリプト(推奨)
|
|
111
71
|
|
|
112
|
-
|
|
72
|
+
相互排他チェック・コンテナ起動・prod キー注入を 1 コマンドで行います。コンテナ内で `enclave-env`・`dotenvx`・各種デプロイツールが使えます。
|
|
113
73
|
|
|
114
74
|
```sh
|
|
115
75
|
sh scripts/prod-shell.sh
|
|
116
76
|
```
|
|
117
77
|
|
|
118
|
-
コンテナ内で `enclave-env`・`dotenvx`・各種デプロイツールが使えます。macOS / Linux 間の環境差が出ないため、CI/CD 安定前の prod deploy や DB メンテナンスも同じコンテナ内で完結します。
|
|
119
|
-
|
|
120
78
|
テンプレート:[`templates/prod-shell.sh`](./templates/prod-shell.sh)
|
|
121
79
|
|
|
122
80
|
### 選択肢 3:2 層 devcontainer
|
|
@@ -138,6 +96,23 @@ VS Code の「Reopen in Container」で prod devcontainer に切り替える構
|
|
|
138
96
|
|
|
139
97
|
テンプレート:[`templates/devcontainer/`](./templates/devcontainer/)
|
|
140
98
|
|
|
99
|
+
## devcontainer 起動時に安全性を検証する
|
|
100
|
+
|
|
101
|
+
`initializeCommand` にシェルスクリプトを登録することで、コンテナ起動前に安全性を確認します。
|
|
102
|
+
|
|
103
|
+
| スクリプト | 登録先 | チェック内容 |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `scripts/init-check-dev.sh` | dev コンテナの `initializeCommand` | prod コンテナ稼働中なら起動をブロック(2 層 devcontainer 使用時) |
|
|
106
|
+
| `scripts/init-check-prod.sh` | prod コンテナの `initializeCommand` | dev コンテナ稼働中なら起動をブロック |
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"initializeCommand": "source enclave-env && sh node_modules/@himorogy/enclave-env/scripts/init-check-dev.sh"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> 実行時ガード(`protected` フラグ)と起動時ガードの 2 層で平文露出を防ぎます。詳細は「セキュリティ設計」を参照してください。
|
|
115
|
+
|
|
141
116
|
---
|
|
142
117
|
|
|
143
118
|
# インストール
|
|
@@ -192,7 +167,37 @@ pnpm encrypt-env
|
|
|
192
167
|
|
|
193
168
|
生成された `DOTENV_PUBLIC_KEY` はリポジトリにコミットします。`.env.keys` はコミットしないでください。
|
|
194
169
|
|
|
195
|
-
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
# セキュリティ設計
|
|
173
|
+
|
|
174
|
+
## 守るべき原則
|
|
175
|
+
|
|
176
|
+
prod 環境の実現手段は問いませんが、以下の 2 つを守ることがこのツールの前提です:
|
|
177
|
+
|
|
178
|
+
| 原則 | 目的 |
|
|
179
|
+
|---|---|
|
|
180
|
+
| **prod の private key は常にワークスペース外に置く** | bind mount 経由で LLM に渡らない |
|
|
181
|
+
| **prod 操作は dev コンテナが停止した状態で行う** | 復号した平文を LLM から守る |
|
|
182
|
+
|
|
183
|
+
## 2 層の防御
|
|
184
|
+
|
|
185
|
+
全復号フロー中は `.env.production` が平文でディスクに存在します。この間に dev コンテナが起動すると、bind mount 経由で LLM が平文にアクセスできてしまいます。
|
|
186
|
+
|
|
187
|
+
enclave-env はこれを 2 つのレイヤーで防ぎます:
|
|
188
|
+
|
|
189
|
+
| レイヤー | タイミング | 担う側 | 実装 |
|
|
190
|
+
|---|---|---|---|
|
|
191
|
+
| 起動時 | devcontainer 起動 | **プロジェクト** が `initializeCommand` に登録 | `scripts/init-check-dev.sh` / `scripts/init-check-prod.sh` |
|
|
192
|
+
| 実行時 | `decrypt` 実行時 | **ライブラリ** が自動で実行 | `protected` フラグが付いた環境で dev コンテナの稼働を確認しブロック |
|
|
193
|
+
|
|
194
|
+
実行時ガードは `DEVCONTAINER=true`(コンテナ内からの実行)の場合はスキップされます。起動時ガードで保証済みのためです。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
# リファレンス
|
|
199
|
+
|
|
200
|
+
## CLI
|
|
196
201
|
|
|
197
202
|
```
|
|
198
203
|
enclave-env encrypt --env <environment> # 指定環境の env ファイルを in-place 暗号化
|
|
@@ -209,7 +214,7 @@ scripts/init-check-dev.sh # dev コンテナ起動時(enclave-env を sourc
|
|
|
209
214
|
scripts/init-check-prod.sh # prod コンテナ起動時(enclave-env を source して実行)
|
|
210
215
|
```
|
|
211
216
|
|
|
212
|
-
|
|
217
|
+
## `enclave-env` 設定キー
|
|
213
218
|
|
|
214
219
|
| キー | 説明 |
|
|
215
220
|
|---|---|
|
|
@@ -221,16 +226,7 @@ scripts/init-check-prod.sh # prod コンテナ起動時(enclave-env を sour
|
|
|
221
226
|
|
|
222
227
|
`<NAME>` は大文字・数字・アンダースコアで構成し、`--env` オプションでは小文字で参照します。例:`ENV_PROD_FILE` → `--env prod`
|
|
223
228
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
| 環境 | キーの保管場所 |
|
|
227
|
-
|---|---|
|
|
228
|
-
| local / dev | `.devcontainer/dev/.env.container`(gitignore 対象) |
|
|
229
|
-
| prod | `~/.config/<your-project>/.env.container`(ワークスペース外) |
|
|
230
|
-
|
|
231
|
-
prod キーをワークスペース外に置くことで、bind mount 経由で LLM に渡るリスクをなくします。
|
|
232
|
-
|
|
233
|
-
# Git 管理ポリシー(推奨)
|
|
229
|
+
## Git 管理ポリシー(推奨)
|
|
234
230
|
|
|
235
231
|
| ファイル | Git 管理 |
|
|
236
232
|
|---|---|
|
|
@@ -239,6 +235,8 @@ prod キーをワークスペース外に置くことで、bind mount 経由で
|
|
|
239
235
|
| `.env.keys*` | ❌ 必ず gitignore |
|
|
240
236
|
| `enclave-env` | ✅ |
|
|
241
237
|
|
|
238
|
+
---
|
|
239
|
+
|
|
242
240
|
# 機能一覧とテスト状況
|
|
243
241
|
|
|
244
242
|
| 機能 | 種別 | テスト |
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@himorogy/enclave-env",
|
|
9
|
-
version: "0.2.
|
|
9
|
+
version: "0.2.1",
|
|
10
10
|
description: "Encrypted env management that keeps secrets out of LLM reach",
|
|
11
11
|
packageManager: "pnpm@10.33.2",
|
|
12
12
|
type: "module",
|
|
@@ -20,8 +20,8 @@ var package_default = {
|
|
|
20
20
|
format: "biome check --write .",
|
|
21
21
|
lint: "biome ci .",
|
|
22
22
|
test: "pnpm test:ts && pnpm test:sh",
|
|
23
|
-
"test:ts": "tsc -p tsconfig.test.json && node --test dist/test/config.test.js",
|
|
24
|
-
"test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh",
|
|
23
|
+
"test:ts": "tsc -p tsconfig.test.json && node --test dist/test/tests/config.test.js dist/test/tests/security.test.js",
|
|
24
|
+
"test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh && sh tests/init-check-prod.test.sh",
|
|
25
25
|
release: "sh release.sh"
|
|
26
26
|
},
|
|
27
27
|
dependencies: {
|
|
@@ -87,6 +87,8 @@ function check() {
|
|
|
87
87
|
|
|
88
88
|
// src/commands/decrypt.ts
|
|
89
89
|
import { execSync as execSync3 } from "child_process";
|
|
90
|
+
import { createRequire } from "module";
|
|
91
|
+
import path from "path";
|
|
90
92
|
|
|
91
93
|
// src/config.ts
|
|
92
94
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -176,6 +178,11 @@ function checkDevContainerNotRunning(containerName) {
|
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
// src/commands/decrypt.ts
|
|
181
|
+
var require2 = createRequire(import.meta.url);
|
|
182
|
+
var dotenvxBin = path.join(
|
|
183
|
+
path.dirname(require2.resolve("@dotenvx/dotenvx/package.json")),
|
|
184
|
+
"src/cli/dotenvx.js"
|
|
185
|
+
);
|
|
179
186
|
function decryptEnv(config, env) {
|
|
180
187
|
if (config.mode !== "single") {
|
|
181
188
|
throw new Error(`Mode "${config.mode}" is not yet supported`);
|
|
@@ -185,17 +192,28 @@ function decryptEnv(config, env) {
|
|
|
185
192
|
checkDevContainerNotRunning(config.security.devContainerName);
|
|
186
193
|
}
|
|
187
194
|
const filePath = resolveEnvFile(config, env);
|
|
188
|
-
execSync3(`
|
|
195
|
+
execSync3(`node "${dotenvxBin}" decrypt -f "${filePath}"`, {
|
|
196
|
+
stdio: "inherit"
|
|
197
|
+
});
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
// src/commands/encrypt.ts
|
|
192
201
|
import { execSync as execSync4 } from "child_process";
|
|
202
|
+
import { createRequire as createRequire2 } from "module";
|
|
203
|
+
import path2 from "path";
|
|
204
|
+
var require3 = createRequire2(import.meta.url);
|
|
205
|
+
var dotenvxBin2 = path2.join(
|
|
206
|
+
path2.dirname(require3.resolve("@dotenvx/dotenvx/package.json")),
|
|
207
|
+
"src/cli/dotenvx.js"
|
|
208
|
+
);
|
|
193
209
|
function encryptEnv(config, env) {
|
|
194
210
|
if (config.mode !== "single") {
|
|
195
211
|
throw new Error(`Mode "${config.mode}" is not yet supported`);
|
|
196
212
|
}
|
|
197
213
|
const filePath = resolveEnvFile(config, env);
|
|
198
|
-
execSync4(`
|
|
214
|
+
execSync4(`node "${dotenvxBin2}" encrypt -f "${filePath}"`, {
|
|
215
|
+
stdio: "inherit"
|
|
216
|
+
});
|
|
199
217
|
}
|
|
200
218
|
|
|
201
219
|
// src/cli.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@himorogy/enclave-env",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Encrypted env management that keeps secrets out of LLM reach",
|
|
5
5
|
"packageManager": "pnpm@10.33.2",
|
|
6
6
|
"type": "module",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"format": "biome check --write .",
|
|
15
15
|
"lint": "biome ci .",
|
|
16
16
|
"test": "pnpm test:ts && pnpm test:sh",
|
|
17
|
-
"test:ts": "tsc -p tsconfig.test.json && node --test dist/test/config.test.js",
|
|
18
|
-
"test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh",
|
|
17
|
+
"test:ts": "tsc -p tsconfig.test.json && node --test dist/test/tests/config.test.js dist/test/tests/security.test.js",
|
|
18
|
+
"test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh && sh tests/init-check-prod.test.sh",
|
|
19
19
|
"release": "sh release.sh"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
package/scripts/check.sh
CHANGED
|
@@ -16,7 +16,7 @@ for file in $(git diff --cached --name-only | grep -E '(^|/)\.env|(^|/)secret\.e
|
|
|
16
16
|
''|'#'*|DOTENV_PUBLIC_KEY*) continue ;;
|
|
17
17
|
esac
|
|
18
18
|
|
|
19
|
-
if ! echo "$line" | grep -qE '^[A-Z0-9_]+=encrypted:'; then
|
|
19
|
+
if ! echo "$line" | grep -qE '^[A-Z0-9_]+="?encrypted:'; then
|
|
20
20
|
echo "❌ Error: $file contains unencrypted value:"
|
|
21
21
|
echo " $line"
|
|
22
22
|
echo " Run: pnpm dotenvx encrypt"
|
|
@@ -32,7 +32,7 @@ find . -type f \
|
|
|
32
32
|
case "$line" in
|
|
33
33
|
''|'#'*|DOTENV_PUBLIC_KEY*) continue ;;
|
|
34
34
|
esac
|
|
35
|
-
if ! echo "$line" | grep -qE '^[A-Z0-9_]+=encrypted:'; then
|
|
35
|
+
if ! echo "$line" | grep -qE '^[A-Z0-9_]+="?encrypted:'; then
|
|
36
36
|
if [ "$FILE_HEADER_SHOWN" = "0" ]; then
|
|
37
37
|
echo "❌ ERROR: $FILE contains unencrypted values:"
|
|
38
38
|
FILE_HEADER_SHOWN=1
|