カマルとの問題に直面しました。私の.kamal/secretsファイルには、ラップトップ上に平文でAPIキーが大量に保存されていました。アクセスできる誰でもそれらを読み取ることができました。
TLDR; カマルを使用し、AWS Secrets Managerと、Hetzner VPSにデプロイしてください。平文の秘密はありません、安価なホスティング、コンプライアンスに満足です。
問題
カマルはアプリのデプロイに最適です。しかし、デフォルトではシークレットは平文ファイルに保存されています。SOC 2やGDPRの要件にはこれでは不適切です。管理されたストアが必要です。私はAWS Secrets Managerを選びました
しかし、それから別の問題に直面しました。kamal secrets fetch --adapter aws_secrets_managerコマンドは--fromそれぞれのキーが独自のAWSシークレットであることを期待します。すべてをJSONの塊として保存すると(私がしたように)、以下のようになります:
ERROR (RuntimeError): myapp/production/secrets//DEEPGRAM_API_KEY: Secrets Manager can't find the specified secret.
ステップ1: Hetzner VPS
Hetzner CAXシリーズは月約4ユーロからスタートします。私はCX22を使用しており、2つのvCPUと4GBのRAMを搭載しています。生産用には十分です.
# On your Hetzner server
apt update && apt install -y docker.io
# Copy your SSH key so Kamal can connect
ssh-copy-id root@your-server-ip
あなたのconfig/deploy.yml:
servers:
web:
hosts:
- runtime.yourdomain.com
proxy:
ssl: true
hosts:
- runtime.yourdomain.com
healthcheck:
path: /health/ready
registry:
server: docker.io
username: your-docker-user
password:
- KAMAL_REGISTRY_PASSWORD
Docker Hubのアカウントと個人アクセストークンが必要ですKAMAL_REGISTRY_PASSWORD.
ステップ2: AWSでシークレットを作成
AWS Secrets Managerコンソールで:
- シークレットマネージャーに移動>新しいシークレットを保存
- 「他の種類の秘密」を選択
- プレートテキストタブに切り替え、JSONを貼り付ける
{
"DEEPGRAM_API_KEY": "your_deepgram_key",
"ASSEMBLY_AI_API_KEY": "your_assemblyai_key",
"REDIS_URL": "redis://:password@your-redis:6379",
"KAMAL_REGISTRY_PASSWORD": "your_docker_token"
}
- 名前をつける
myapp/production/secrets - 保存する
サーバーに近い地域を選択。Hetznerのボックスがドイツにある場合はeu-central-1(フランクフルト)を使用する。遅延を低くし、GDPRを満たす。
ステップ3: テーブルトップ用のIAMユーザー
テーブルトップはデプロイ中にシークレットを読むための許可が必要です。
- IAMに移動して、 > ユーザー > > ユーザーを作成
- ユーザー名を
kamal-deploy - コンソールアクセスをオフにする(CLIのみ)
- グループを作成して
secrets-managerSecretsManagerReadWriteポリシーを持つ - バッチ読み取り用のインラインポリシーを追加:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:BatchGetSecretValue",
"secretsmanager:ListSecrets"
],
"Resource": "*"
}
]
}
- あなたのユーザーをグループに追加
IAMポリシーは数分かかることがあります。最初に失敗した場合は30秒待って再度試す
ステップ4: AWS CLIを設定
aws configure
# AWS Access Key ID: paste from IAM user
# AWS Secret Access Key: paste
# Default region name: eu-central-1
# Default output format: json
テストを実行
aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text | head -c 50
あなたのJSONの始まりが見えるはずです
ステップ5:.kamal/secretsファイルをフォーマットする
ここでつまずきました。--fromフラグは、各キーごとに1つのAWSシークレットを要求します。20個の別々のシークレットがあるのは面倒です。確認してくださいカマルの秘密文書について詳しくはこちらをご覧ください.
代わりに、私はPythonを使用してAWS CLIで抽出します。各行は独立しています:
# AWS Secrets Manager: myapp/production/secrets (eu-central-1)
DEEPGRAM_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['DEEPGRAM_API_KEY'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
ASSEMBLY_AI_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['ASSEMBLY_AI_API_KEY'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
REDIS_URL=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['REDIS_URL'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
KAMAL_REGISTRY_PASSWORD=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['KAMAL_REGISTRY_PASSWORD'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
各行は完全なJSONを取得し、1つのキーを抽出します。カマルは各行を独自のサブシェルで評価するため、各行間で共有変数はありません。これが機能します。
お好みでjqを使用することもできます:
DEEPGRAM_API_KEY=$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text | jq -r '.DEEPGRAM_API_KEY')
ステップ6:デプロイ
kamal deploy
カマルはデプロイ時にAWSからシークレットを取得し、それをあなたのコンテナに注入します。平文ファイルは決してサーバーに触れることはありません。
製造とステージング
各環境ごとに異なるAWSシークレットを使用しています。両方ともAWSからプルし、平文はどこにも存在しません.
# .kamal/secrets (used by kamal deploy)
DEEPGRAM_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['DEEPGRAM_API_KEY'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
KAMAL_REGISTRY_PASSWORD=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['KAMAL_REGISTRY_PASSWORD'])" "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")
# .kamal/secrets.staging (used by kamal deploy -d staging)
DEEPGRAM_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['DEEPGRAM_API_KEY'])" "$(aws secretsmanager get-secret-value --secret-id myapp/staging/secrets --query SecretString --output text)")
KAMAL_REGISTRY_PASSWORD=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['KAMAL_REGISTRY_PASSWORD'])" "$(aws secretsmanager get-secret-value --secret-id myapp/staging/secrets --query SecretString --output text)")
ファイル間ではシークレット名のみが変更されます。myapp/production/secrets プロダクション用、myapp/staging/secrets ステージング用。kamal deploy -d staging を実行し、Kamalはステージングファイルから読み取ります。
両方の秘密はAWSにあります。プレーンテキストのステージング資格情報もありません。これはSOC 2にとって重要ですなぜなら監査人はすべての環境を確認するからです
やりました
プレーンテキストの秘密はもうありません。SOC 2とGDPRの要件を満たしました。Hetznerの請求額は月5ユーロを下回ります
大きく感謝しますAWSドキュメントチーム、Kamalメンテナー、Hetznerがホスティングを手頃な価格で提供してくれてありがとう。これであなたも私が直面したような頭痛を回避できるといい。それでは、再度開発に戻ろう。










