資格情報
Cornus は、実行中のワークロードにクラウド資格情報、LLM API キーなどのシークレットを渡せます。しかも シークレットがイメージ、デプロイスペック、Pod 仕様に入ることはありません。資格情報は呼び出し元のマシン上でローカルの資格情報から発行され、稼働中の deploy-attach 接続でサーバーを経由して中継され、Pod ごとの caretaker サイドカーによってコンテナへ渡されます。必要に応じて取得され、TTL 付きでキャッシュされ、有効期限が近づくと更新されます。関連機能として、ワークロードの外向き通信を呼び出し元経由にするクライアント側エグレスがあります。
仕組み
これはデプロイスペックの credentials: ブロック、または Compose サービスの x-cornus-credentials: として宣言されます (後者もフィールドは同じです。下記の Compose ファイルから を参照してください) 。実現されるのは Kubernetes バックエンドで、クライアントはワークロードの存続中に行われる取得要求へ応答するためセッションを維持します。したがって cornus deploy --detach とホストバックエンドはこれを拒否します。sources: の各項目は、シークレットを生成するクライアント側バックエンドと、コンテナから利用できるようにする 1 つ以上の配送方法を指定します。
サーバーに送られるのはバックエンド名と非シークレットの config だけです。シークレットは取得時にバックエンドによって生成されます。
ソースバックエンド
各バックエンドは呼び出し元自身の環境から資格情報を生成します。
backend | 生成元 | 備考 |
|---|---|---|
static | リテラルの config 値 (またはファイル) | |
exec | config.command の標準出力 | JSON、または config.key 下の単一 raw 値 |
env | クライアント環境変数 (config.var) | 例: ANTHROPIC_API_KEY |
aws-sts | AWS 資格情報チェーンを使う STS 経由の短命 AWS 資格情報 | credaws タグ付きバイナリが必要。モードは auto / assume-role / session-token / passthrough |
anthropic / claude-code / codex | ローカルの LLM ログイン情報 | 短命トークンを有効期限が近づくと再読み込み |
github-cli | ローカルの gh auth login | gh auth token を実行。GitHub Enterprise には hostname、アカウントの選択には user |
配送方法
deliveries[].kind の既定は endpoint です。
endpoint- caretaker がループバック HTTP エンドポイントから資格情報を提供します。provider: generic(既定) はネイティブ契約 (GET /credentials/<name>が{"values":{...},"expiration":"..."}を返す) を提供し、CORNUS_CREDENTIALS_URL/CORNUS_CREDENTIAL_<NAME>_URLでアプリケーションに通知します。provider: aws-imdsは変更していない AWS SDK が期待する形式で資格情報を描画します。下のAWS STS から資格情報を取得するを参照してください。認証を注入するプロバイダー (anthropic-proxy、openai-proxy、github-proxy) はさらに一歩進んで資格情報を自ら保持するため、コンテナはそれを一切受け取りません。file- 共有ボリューム内のpath:に実体化します。format:はjson(既定)、env(KEY=VALUE行)、raw(単一値)、またはaws-credentials(ini プロファイル) です。モード0600で書き込まれます。env- アプリケーションコンテナにenvVar:を注入します。値はデプロイ時に一度取得され、secretKeyRef経由で参照される Kubernetes シークレットに保存されます (Pod 仕様のリテラルにはなりません)。ただし静的で更新されず etcd に残るため、短命または実体化したくないシークレットにはendpoint/fileを推奨します。
信頼性
シークレットは稼働中のセッションで取得のたびに返され、仕様や wire 制御フレームには決して入りません。ワークロードが取得できるのは、自身のデプロイセッションが宣言した資格情報名 だけです。セッション ID は推測不能な能力トークンであり、サーバー中継と caretaker の両方で検査されます。認証プロキシは本物の資格情報を注入する前にクライアント提供の認証情報を除去するため、ワークロードは生のシークレットを読むことも偽装することもできません。
関連項目: デプロイスペック
イメージに焼き込まず資格情報をワークロードへ仲介する
credentials: ブロックを宣言します。シークレットは自分のマシンで発行され、caretaker が配送するため、イメージ、仕様、Pod 仕様には一切入りません。
name: app
image: localhost:5000/app:v1
credentials:
sources:
- name: db
backend: static # クライアント上でシークレットを生成
config: { username: app, password: s3cret } # 他のバックエンド向けの非シークレット設定
deliveries:
- { kind: endpoint, provider: generic } # GET $CORNUS_CREDENTIALS_URL -> JSON
- { kind: file, path: /creds/db.json, format: json }- kubernetes バックエンドで、フォアグラウンドの
cornus deploy --serverセッションを通じて実現されます (ワークロードの存続期間中クライアントが取得要求へ応答するため、--detachは拒否します)。 deliveries[].kindはendpoint(既定)、file、envのいずれかです。ワークロードが取得できるのは自身のセッションが宣言した資格情報名だけです。
関連項目: デプロイスペック
LLM API をプロキシする、または API キーをワークロードへ注入する
anthropic-proxy と openai-proxy のエンドポイントプロバイダーは、資格情報を提供するだけでなくさらに一歩進みます。caretaker がベンダー API へのループバックリバースプロキシを実行し、認証ヘッダーを自ら注入 します。そのためワークロードは自前のキーなしで LLM を呼び出せます。アプリケーションには ANTHROPIC_BASE_URL / OPENAI_BASE_URL を設定し、あわせてプレースホルダーの ANTHROPIC_API_KEY / OPENAI_API_KEY も設定します (資格情報を実際に保持しているのはプロキシであるにもかかわらず、SDK や CLI はキーの環境変数が無いと起動を拒否するためです) 。そのうえでクライアントが送った認証情報をすべて除去して、要求ごとに本物の資格情報を追加します。つまりコーディングエージェントのワークロードは、シークレットがコンテナに入ることなく 自分自身の Claude Code / Codex のログイン情報を利用できます。
credentials:
sources:
- name: claude
backend: claude-code # または: anthropic / env (config.var: ANTHROPIC_API_KEY)
deliveries:
- kind: endpoint
provider: anthropic-proxy # ANTHROPIC_BASE_URL を設定し、ヘッダーを注入
# upstream: https://my-gateway # 任意: Azure OpenAI、オンプレミスゲートウェイ、モックupstreamはベンダーの既定値 (https://api.anthropic.com/https://api.openai.com) の代わりに、任意の Anthropic 互換または OpenAI 互換ゲートウェイをプロキシ先にします。- 単純な環境変数を注入するには、
backend: envをconfig.varとともに使い、env種別で配送します (静的な資格情報は Kubernetes シークレットに保存されます。短期間有効なシークレットにはendpoint/fileを推奨します)。
API キーと OAuth トークン
プロキシは両方の資格情報形式を透過的に扱うため、プレーンな API キー または OAuth ログイントークンのどちらでも、ワークロードを変更せずに動作します。
- API キー はベンダーが通常使うキーヘッダーで送られます (Anthropic では
x-api-key)。 - OAuth トークン、たとえば
claude/ant auth loginでログインして得るsk-ant-oat...トークンは、Anthropic API が OAuth ベアラートークンに要求するanthropic-beta: oauth-2025-04-20ヘッダーとともにAuthorization: Bearer <token>として送られます。プロキシは資格情報値をoauth_token(OAuth を強制)、api_key(API キーを強制)、それ以外はvalue/tokenの順で選びます。
anthropic / claude-code / codex ソースバックエンドはローカルのログインストアを読み、有効期限が近づくと 短命 OAuth アクセストークンを更新 します (codex は ChatGPT のログイン情報にある tokens.access_token を読み、API キーにフォールバックします)。そのため長時間稼働するエージェントは再認証なしに動き続け、トークンはそれでもコンテナには入りません。
関連項目: デプロイスペック
AWS STS から資格情報を取得する
自分の AWS 資格情報チェーンから短期間有効な AWS 資格情報を発行し、SDK が期待する形式で渡します。
credentials:
sources:
- name: aws
backend: aws-sts
config: { role_arn: arn:aws:iam::123456789012:role/app, region: us-east-1 }
deliveries:
- { kind: endpoint, provider: aws-imds, wellKnown: true }
- { kind: file, path: /root/.aws/credentials, format: aws-credentials }aws-stsは STS を介して AWS 資格情報チェーンを使います。credawsタグ付きバイナリが必要で、auto/assume-role/session-token/passthroughモードをサポートします。
aws-imds エンドポイントプロバイダーは、ブローカーされた資格情報を AWS SDK がすでに探す形式で提供します。そのため 変更していない SDK がコードやアプリケーションを変更せずに取得できます。アダプターは AWS SDK への依存を持たない純粋な HTTP 実装で、1 つのエンドポイントから 2 種類の形式で応答します。
- ECS コンテナ資格情報 -
GET /credsは{AccessKeyId, SecretAccessKey, Token, Expiration}を返します。 - EC2 IMDSv2 -
PUT /latest/api/tokenの後、GET /latest/meta-data/iam/security-credentials/<role>を呼び出します (一覧は単一の合成ロールcornusを通知します)。IMDSv1 クライアントはトークン取得を単にスキップします。
SDK がそこへ到達する方法は wellKnown によって異なります。
wellKnown | バインド先 | SDK の見つけ方 | 必要なもの |
|---|---|---|---|
false (既定) | ループバック | Cornus が AWS_CONTAINER_CREDENTIALS_FULL_URI=http://<loopback>/creds を注入します。これは AWS SDK が尊重する標準の ECS 資格情報用環境変数です。 | 追加なし |
true | Pod netns 内のリンクローカル 169.254.169.254:80 | SDK 組み込みの IMDSv2 パス。環境変数は不要で、本物の EC2 インスタンスと同じです。 | caretaker に NET_ADMIN |
これは配送用の アダプター であり、汎用メタデータサービスを運用するものではありません。ワークロードのセッションに対する、ブローカーされた 1 つの資格情報だけを提供します。同じ仕組みに GCP / Azure のメタデータアダプターを組み込めます。
関連項目: デプロイスペック
自分の gh ログイン情報でワークロードに GitHub API アクセスを与える
github-cli ソースは自分のマシンで gh auth token を実行し、github-proxy エンドポイントプロバイダーはそのトークンをワークロードの GitHub REST API 呼び出しへ注入します。コンテナはトークンを一切保持せずに認証済みの呼び出しを行えます。
credentials:
sources:
- name: gh
backend: github-cli
ttl: 1h # gh は有効期限を報告しません。下記参照
deliveries:
- kind: endpoint
provider: github-proxy # GITHUB_API_URL を設定し、ヘッダーを注入
# upstream: https://ghe.corp/api/v3 # GitHub Enterprise Serverトークンは gh が保存している場所から読み取ります。多くのマシンでは OS のキーリングであり、~/.config/gh/hosts.yml を読むだけでは取得できない場合でもこの方法なら動作します。gh は GH_TOKEN / GITHUB_TOKEN (他ホストでは GH_ENTERPRISE_TOKEN) も尊重するため、同じスペックが CI でもそのまま動きます。設定キーは hostname (GitHub Enterprise) 、user (ログイン済みアカウントの選択) 、command、timeout、key です。
gh が PATH に無い場合や別の名前でインストールされている場合は、デプロイセッションを保持しているマシンで CORNUS_GH_BIN を設定してください。トークンが発行されるのはそのマシンであり、この変数もそこで読まれます。これは共有スペックを編集せずに 1 台のマシンへ適合させるためのものです。明示的な config.command はスペックを書いた人の意図的な選択 (たとえば迂回されては困るラッパー) であるため、こちらが優先されます。優先順位は config.command、次に CORNUS_GH_BIN、最後に gh です。
CORNUS_GH_BIN=/opt/homebrew/bin/gh cornus deploy --server -f app.yamlttl: は明示的に設定してください。gh auth token は有効期限を報告しないため、既定の 5 分ではレプリカごとに 5 分おきに gh が実行され、キーリングに触れる可能性があります。有効期限のないトークンには 1 時間で十分です。
これは REST API であり、git ではありません
git clone と git push はこのプロキシを通りません。git over HTTPS は github.com:443 へ直接向かうため影響を受けません。gh CLI 自体もこのプロキシでは動きません。gh はベース URL ではなくホスト名を受け取り、常に HTTPS で通信するため、平文のループバックサイドカーを指定できないからです。どちらの場合も、代わりにトークン自体を配送し、それがコンテナに入ることを受け入れてください。
deliveries:
- { kind: endpoint } # GET $CORNUS_CREDENTIALS_URL -> JSON
- { kind: file, path: /run/secrets/gh-token, format: raw }クライアントをプロキシに向ける
GITHUB_API_URL を自動的に読むのは @actions/github だけです。他のクライアントはいずれも 1 行の設定が必要です。
| クライアント | |
|---|---|
| Octokit (JS) | new Octokit({ baseUrl: process.env.GITHUB_API_URL }) |
| PyGithub | Github(base_url=os.environ["GITHUB_API_URL"]) |
| go-github | c := github.NewClient(nil); c.BaseURL, _ = url.Parse(os.Getenv("GITHUB_API_URL") + "/") |
go-github では BaseURL を直接、末尾のスラッシュ付きで設定してください。WithEnterpriseURLs は使ってはいけません。これは api.github.com に見えないホストすべてに api/v3/ を付け足すため、ループバックプロキシのアドレスが https://api.github.com/api/v3/... になって 404 になります。
既定のアップストリームでは GITHUB_GRAPHQL_URL も GITHUB_API_URL とあわせて設定されます。GitHub Enterprise の upstream では意図的に省略されます。GHES は REST を /api/v3 の下で、GraphQL を兄弟の /api/graphql の下で提供しており、単一のプロキシでは到達できないためです。誤った URL を通知すると、クライアントは資格情報なしで GHES へ向かってしまいます。
コンテナに GITHUB_TOKEN は設定されません。これは意図的です。プレースホルダーを置くと gh、git の資格情報ヘルパー、api.github.com を直接呼ぶスクリプトがそれを拾ってしまい、「資格情報が無い」という状態が原因から遠く離れた場所での不可解な 401 に変わるからです。クライアントがこの環境変数の存在を要求する場合は、スペックの env: に自分でダミー値を設定してください。プロキシはクライアントが送った値を除去します。
押さえるべき 2 点
hostname と upstream を揃えてください。 ソースの hostname は自分のマシンで、配送の upstream はデプロイ経路で解決され、両者が一致しているかを検査する仕組みはありません。hostname: ghe.corp の github-cli ソースと既定の upstream を組み合わせると、有効な GitHub Enterprise の資格情報が api.github.com へ送られます。この 2 つは必ずセットで設定してください。
スコープに注意してください。 gh auth login のトークンは通常 repo (アクセスできるすべてのプライベートリポジトリへの読み書き) 、read:org、多くの場合 workflow を含みます。Pod 内で動作するものはすべてループバックプロキシを通じて自分自身として振る舞え、それを狭める方法はありません。LLM のキーよりもはるかに影響範囲が広いということです。暴走したループは自分のレート制限も消費します。完全に信頼できるワークロード以外では、fine-grained PAT を発行して static / env / exec で配送してください。
対象外: uploads.github.com (リリースアセット) は別のホストです。レスポンスボディ内の絶対 URL は書き換えられません (Link と Location ヘッダーは書き換えられるため、ページネーションとリダイレクトはプロキシ上に留まります) 。プライベート CA の背後にある GitHub Enterprise インスタンスでは、その CA を caretaker のイメージに含めるか SSL_CERT_FILE で渡す必要があります。
関連項目: デプロイスペック
Compose ファイルから
Compose サービスは同じブロックを x-cornus-credentials: として宣言します。したがってエージェント、そのデータベース、そのキャッシュといったスタック全体を cornus compose up 一発で立ち上げつつ、エージェントはあなた自身のログインに相乗りできます。
services:
agent:
image: localhost:5000/agent:v1
x-cornus-credentials:
- name: claude
backend: claude-code
deliveries:
- { kind: endpoint, provider: anthropic-proxy }
db:
image: postgres:16- ブロックは上記の素のソースリストか、スペックのオブジェクト形式 (同じリストを持つ
sources:) です。スペックのブロックをそのまま貼り付けられます。 - 配送フィールドは Compose の snake_case 綴り (
well_known、env_var、value_key) を取ります。スペックの camelCase 綴りも動作します。どちらでもないキーは、黙って無視されるのではなくエラーになります。 - 宣言したサービスは、そのライフタイムにわたって deploy-attach セッションを保持します。
cornus compose up -dではプロジェクトのバックグラウンドエージェントが保持するため、cornus deploy --detachとは異なり、デタッチした compose のupでも資格情報を利用できます。upの行はその理由 (brokering credentials) を示します。 - ホストバックエンドは資格情報の配送を実装していません。デプロイを拒否するか、警告してブロックを無視します。
- プロジェクトレベルの
x-cornus-credentials:ブロックは、自分で宣言していないすべてのサービスの既定値になります。サービス側のブロックはそれを丸ごと上書きします。継承した各サービスはそれぞれ自分のセッションを保持します。
関連項目: cornus compose