Skip to content

fix: プラグインインストールを vscode ユーザーで実行 - #635

Merged
keito4 merged 2 commits into
mainfrom
fix/plugin-install-as-vscode-user
Mar 23, 2026
Merged

fix: プラグインインストールを vscode ユーザーで実行#635
keito4 merged 2 commits into
mainfrom
fix/plugin-install-as-vscode-user

Conversation

@keito4

@keito4 keito4 commented Mar 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Docker ビルド時のプラグインインストールを root → vscode ユーザーで実行するように変更
  • BuildKit secret を一時ファイルにコピーして vscode ユーザーからアクセス可能にする

Why

root ユーザーで install-claude-plugins.sh を実行すると、vscode ユーザーとしてインストールされた claude バイナリにアクセスできず、全15プラグインのインストールが失敗していた。

What

Dockerfile

  • root RUN で BuildKit secret を /tmp/claude-secret/token にコピー
  • USER vscode に切り替えてからプラグインインストールを実行
  • 終了後に一時ファイルを削除し、USER root に戻って権限修正

install-claude-plugins.sh

  • /run/secrets/claude_credentials(従来)と /tmp/claude-secret/token(新)の両方を探索
  • 先に見つかった方を使用

Test plan

  • ビルドログで Claude version: x.x.x が表示されること
  • プラグインが正常にインストールされること(0 失敗

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Improved dev container plugin installation workflow with safer credential handling
    • Added flexible credential discovery to support multiple secret locations
    • Adjusted install sequencing and cleanup for more reliable setup and reduced permission issues

root で実行すると claude バイナリにアクセスできない問題を修正。
BuildKit secret を一時ファイルにコピーし、vscode ユーザーで
install-claude-plugins.sh を実行するように変更。

- Dockerfile: secret を /tmp/claude-secret/token にコピー後、USER vscode で実行
- install-claude-plugins.sh: /tmp/claude-secret/token もシークレットソースとして探索

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Refactors DevContainer Claude plugin installation to copy BuildKit-mounted credentials into a temporary file as root, run the installer as the vscode user probing multiple secret locations, then clean up the temp secret and restore ownership. The installer script now discovers credentials from several paths.

Changes

Cohort / File(s) Summary
Docker Build Configuration
.devcontainer/Dockerfile
Splits Claude installer step into root-created /tmp/claude-secret (copy secret + set perms), switches to USER vscode to run /tmp/install-claude-plugins.sh, then returns to root to remove /tmp/claude-secret and chown -R vscode:vscode /home/vscode/.claude. Removes inline env-export approach.
Installation Script
script/install-claude-plugins.sh
Adds dynamic credential discovery: probe multiple candidate files (/run/secrets/claude_credentials, /tmp/claude-secret/token) and set CREDENTIALS_SECRET to the first non-empty candidate; conditional checks use -n "$CREDENTIALS_SECRET" instead of single hardcoded file tests.

Sequence Diagram(s)

sequenceDiagram
  participant BuildKit as BuildKit (secret mount)
  participant Docker as Dockerfile build
  participant Root as root (build stage)
  participant VSCode as vscode (user)
  participant Installer as /tmp/install-claude-plugins.sh
  participant FS as Filesystem (/home/vscode/.claude)

  BuildKit->>Docker: mount secret at /run/secrets/claude_credentials
  Docker->>Root: RUN step creates /tmp/claude-secret/token (copy, chmod)
  Root->>VSCode: switch USER to vscode
  VSCode->>Installer: execute installer (probes /run/secrets/... and /tmp/claude-secret/token)
  Installer->>FS: install plugins under /home/vscode/.claude
  VSCode->>Root: return to root user
  Root->>Root: remove /tmp/claude-secret
  Root->>FS: chown -R vscode:vscode /home/vscode/.claude
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

Suggested Labels

size/M

Poem

🐰 In tunnels of mounts and tokens tucked tight,
I hop and I copy by dim buildkit light.
As root I prepare, then vscode takes stage—
Plugins bloom in a clean, homely cage.
A tidy cleanup, a chown with delight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: running the Claude plugin installation as the vscode user instead of root, which directly addresses the core issue preventing plugin installation.
Description check ✅ Passed The description includes all required sections from the template: Summary, Why, What, and a Test plan with concrete verification steps, providing clear context for the changes made.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/plugin-install-as-vscode-user

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62cda75006

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .devcontainer/Dockerfile
env HOME=/home/vscode CLAUDE_CODE_OAUTH_TOKEN="${CLAUDE_CODE_OAUTH_TOKEN}" ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}" /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt || ( \
mkdir -p /tmp/claude-secret \
&& if [ -f /run/secrets/claude_credentials ]; then \
cp /run/secrets/claude_credentials /tmp/claude-secret/token && \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Avoid persisting secret token in image layers

Copying /run/secrets/claude_credentials into /tmp/claude-secret/token inside a standalone RUN step writes the credential into a committed image layer, and deleting it later in another layer does not remove the underlying bytes. When a real BuildKit secret is supplied, this can leak auth material to anyone with access to the image or layer cache. Keep secret use within a single secret-mounted RUN so it is never persisted.

Useful? React with 👍 / 👎.

Comment thread .devcontainer/Dockerfile Outdated
) \
&& chown -R vscode:vscode /home/vscode/.claude
)
RUN rm -rf /tmp/claude-secret

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clean up temp secret directory with root privileges

/tmp/claude-secret is created in the previous layer while running as root, but cleanup (rm -rf /tmp/claude-secret) now runs as vscode. On typical Ubuntu /tmp (sticky bit) with root-owned entries, this deletion is not permitted for non-owner users, so the Docker build can fail at this step even after successful plugin installation.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
.devcontainer/Dockerfile (1)

186-192: Consider stricter permissions on the temp credentials file.

chmod 644 makes the token world-readable. While the file is temporary and deleted after use, chmod 600 (or 640) would follow the principle of least privilege.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.devcontainer/Dockerfile around lines 186 - 192, The temporary credential
file /tmp/claude-secret/token is made world-readable via the chmod 644 step in
the RUN block; change the permission to a more restrictive mode (e.g., chmod 600
or 640) in that same RUN command that creates and copies the secret to
/tmp/claude-secret/token so the token is only readable by the owner (and group
if chosen) while preserving the rest of the flow that copies and later removes
the file.
script/install-claude-plugins.sh (1)

101-105: Consider showing searched paths in error message.

When CREDENTIALS_SECRET is empty, line 103 outputs an unhelpful blank value. Showing the paths that were checked would aid debugging.

💡 Suggested improvement
 else
     log_warn "認証情報が見つかりません"
-    echo "  - BuildKit secret: $CREDENTIALS_SECRET"
+    echo "  - BuildKit secret paths checked: /run/secrets/claude_credentials, /tmp/claude-secret/token"
     echo "  - 環境変数: CLAUDE_CODE_OAUTH_TOKEN または ANTHROPIC_API_KEY"
     exit 1
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@script/install-claude-plugins.sh` around lines 101 - 105, The error branch
that calls log_warn "認証情報が見つかりません" prints an empty CREDENTIALS_SECRET which is
unhelpful; update that block to also print the specific locations the script
searched for credentials (e.g., the value of CREDENTIALS_SECRET and any search
list variable such as CREDENTIALS_SEARCH_PATHS or other path variables used when
resolving credentials), and keep the existing hints about
CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY; modify the echo lines in the else
branch that reference CREDENTIALS_SECRET so they show the checked paths or
filenames (or the variable name that holds them) to aid debugging while still
calling log_warn and exit 1.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.devcontainer/Dockerfile:
- Around line 193-200: The cleanup of /tmp/claude-secret is executed while USER
is vscode (see the RUN /tmp/install-claude-plugins.sh ... line and the
subsequent RUN rm -rf /tmp/claude-secret), but the temp dir was created by root;
either move the rm -rf /tmp/claude-secret to run as root (after the final USER
root) or ensure the directory is created/chowned to vscode before switching
users; update the Dockerfile so the RUN rm -rf /tmp/claude-secret executes with
root privileges (or add chown to give vscode ownership prior to the deletion)
and keep the USER switching (USER vscode / USER root) consistent with that
change.

---

Nitpick comments:
In @.devcontainer/Dockerfile:
- Around line 186-192: The temporary credential file /tmp/claude-secret/token is
made world-readable via the chmod 644 step in the RUN block; change the
permission to a more restrictive mode (e.g., chmod 600 or 640) in that same RUN
command that creates and copies the secret to /tmp/claude-secret/token so the
token is only readable by the owner (and group if chosen) while preserving the
rest of the flow that copies and later removes the file.

In `@script/install-claude-plugins.sh`:
- Around line 101-105: The error branch that calls log_warn "認証情報が見つかりません"
prints an empty CREDENTIALS_SECRET which is unhelpful; update that block to also
print the specific locations the script searched for credentials (e.g., the
value of CREDENTIALS_SECRET and any search list variable such as
CREDENTIALS_SEARCH_PATHS or other path variables used when resolving
credentials), and keep the existing hints about CLAUDE_CODE_OAUTH_TOKEN and
ANTHROPIC_API_KEY; modify the echo lines in the else branch that reference
CREDENTIALS_SECRET so they show the checked paths or filenames (or the variable
name that holds them) to aid debugging while still calling log_warn and exit 1.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62a57380-2635-4780-875a-c99d0a3a8cb0

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7f97d and 62cda75.

📒 Files selected for processing (2)
  • .devcontainer/Dockerfile
  • script/install-claude-plugins.sh

Comment thread .devcontainer/Dockerfile Outdated
@keito4 keito4 self-assigned this Mar 23, 2026
@claude

claude Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

コードレビュー

概要

root ユーザーで claude バイナリにアクセスできなかった問題の根本原因を正しく特定しており、アプローチの方向性は妥当です。


重大な問題

セキュリティ: secret が中間レイヤーに残留する

現在のコードでは cprm が別々の RUN ステップになっているため、/tmp/claude-secret/token の内容が中間レイヤーに書き込まれます。BuildKit の secret mount はレイヤーに残らない設計ですが、/tmp にコピーした時点でその保護が失われます。

推奨する修正 ① : su を使い単一の RUN ブロックで完結させる

RUN --mount=type=secret,id=claude_credentials,uid=0,gid=0 \
    mkdir -p /tmp/claude-secret \
    && if [ -f /run/secrets/claude_credentials ]; then \
        cp /run/secrets/claude_credentials /tmp/claude-secret/token \
        && chmod 600 /tmp/claude-secret/token; \
    fi \
    && su vscode -c "/tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt" \
    || (echo "[WARN] プラグインインストール失敗"; echo "[INFO] 手動インストールしてください") \
    ; rm -rf /tmp/claude-secret \
    && chown -R vscode:vscode /home/vscode/.claude

推奨する修正 ② (よりクリーン) : secret の uid を vscode に設定

vscode ユーザーが uid=1000 の場合、コピー不要で直接アクセス可能です。

USER vscode
RUN --mount=type=secret,id=claude_credentials,uid=1000,gid=1000 \
    /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt \
    || (echo "[WARN] プラグインインストール失敗"; echo "[INFO] 手動インストールしてください")
USER root
RUN chown -R vscode:vscode /home/vscode/.claude

軽微な問題

CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY が未伝達

変更前は明示的に環境変数を渡していましたが、変更後の USER vscode 下の RUN では渡されていません。Build arg 経由で ENV に設定されている場合は問題ありませんが、意図的な変更かどうか確認が必要です。

スクリプト内の不要なフォールバック

install-claude-plugins.sh/run/secrets/claude_credentials を先に探索していますが、vscode ユーザーで実行する場合このパスにはアクセスできません。シンプルに /tmp/claude-secret/token のみを参照するか、コメントで意図を明記することを推奨します。

chmod 644 ではなく chmod 600 を推奨

認証情報ファイルは owner のみ読み取り可能にすべきです(他のプロセスからも読める状態を最小化)。


まとめ

項目 評価
問題の原因特定 ✅ 正確
アプローチの方向性 ✅ 妥当
セキュリティ(レイヤー残留) ⚠️ 要修正
環境変数の伝達 ❓ 要確認
コードの複雑度 ⚠️ 軽微なデッドコード

上記のうち「secret の中間レイヤー残留」は認証情報漏洩リスクがあるため、マージ前に対処を推奨します。修正 ② の --mount=type=secret,uid=1000 アプローチが最もシンプルで安全です。

🤖 Generated with Claude Code

root で作成したディレクトリを vscode ユーザーで rm できない問題を修正。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.devcontainer/Dockerfile:
- Around line 187-200: The Dockerfile currently copies the BuildKit secret into
a layer (creating /tmp/claude-secret/token with mode 644) then removes it in a
later RUN, leaving the token recoverable; fix by consolidating the secret copy,
plugin install, and cleanup into a single RUN so the token never persists across
layers, switch to the vscode user inside that same RUN when invoking
/tmp/install-claude-plugins.sh (or run the install script via sudo -u vscode),
and set restrictive permissions (600) on the temporary token file before running
the installer; ensure the RUN references the same paths
(/run/secrets/claude_credentials, /tmp/claude-secret/token,
/home/vscode/.claude) and the installer script name
(/tmp/install-claude-plugins.sh) so the steps are performed atomically and the
secret is removed at the end of that same RUN.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47a2cb9b-56cf-4728-bc73-3da3b6d632c5

📥 Commits

Reviewing files that changed from the base of the PR and between 62cda75 and 2580dfc.

📒 Files selected for processing (1)
  • .devcontainer/Dockerfile

Comment thread .devcontainer/Dockerfile
Comment on lines 187 to +200
RUN --mount=type=secret,id=claude_credentials,uid=0,gid=0 \
env HOME=/home/vscode CLAUDE_CODE_OAUTH_TOKEN="${CLAUDE_CODE_OAUTH_TOKEN}" ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}" /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt || ( \
mkdir -p /tmp/claude-secret \
&& if [ -f /run/secrets/claude_credentials ]; then \
cp /run/secrets/claude_credentials /tmp/claude-secret/token && \
chmod 644 /tmp/claude-secret/token; \
fi
USER vscode
RUN /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt || ( \
echo "[WARN] Claude プラグインのインストールに失敗しました(認証情報が不足している可能性があります)" && \
echo "[INFO] コンテナ起動後に手動でインストールしてください: claude plugin install <plugin>@<marketplace>" \
) \
&& chown -R vscode:vscode /home/vscode/.claude
)
USER root
RUN rm -rf /tmp/claude-secret \
&& chown -R vscode:vscode /home/vscode/.claude

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n .devcontainer/Dockerfile | sed -n '180,210p'

Repository: keito4/config

Length of output: 1714


🏁 Script executed:

# Look at the install script to understand dependencies
cat -n /tmp/install-claude-plugins.sh 2>/dev/null || find .devcontainer -name "*install-claude*" -type f

Repository: keito4/config

Length of output: 39


🏁 Script executed:

find .devcontainer -name "*install-claude*" -type f

Repository: keito4/config

Length of output: 39


🏁 Script executed:

# Search for install-claude-plugins references
rg "install-claude-plugins" .devcontainer/

Repository: keito4/config

Length of output: 280


🏁 Script executed:

cat -n script/install-claude-plugins.sh

Repository: keito4/config

Length of output: 5443


🏁 Script executed:

# Check if this is single or multi-stage build and see the base image
head -50 .devcontainer/Dockerfile | cat -n

Repository: keito4/config

Length of output: 1760


🏁 Script executed:

# Check the coding guidelines to see if there's a base image requirement
grep -i "base image" .devcontainer/Dockerfile | head -5

Repository: keito4/config

Length of output: 172


🏁 Script executed:

# Confirm the actual BuildKit secret behavior with a quick check of Docker documentation and layer behavior
# Verify that secrets mounted with --mount are NOT persisted in layers
cat <<'EOF' > /tmp/test_analysis.py
# Docker BuildKit secret semantics:
# --mount=type=secret mounts read-only at /run/secrets/ID
# This mount is temporary and NOT included in the layer
# BUT: If you CP that secret to another path, that copy IS included in the layer
# So the concern is valid: copying secret to /tmp/... and deleting later leaves it in earlier layer

print("Docker BuildKit secret analysis:")
print("1. --mount=type=secret,id=X: Temporary, not in layer")
print("2. COPY from /run/secrets/X to /tmp/...: Creates committed copy in layer")
print("3. DELETE in later RUN: Does NOT remove from earlier layer")
print("\nConclusion: Original review concern is CORRECT")
print("Solution: Single RUN to copy→install→delete before layer commit")
EOF
python3 /tmp/test_analysis.py

Repository: keito4/config

Length of output: 386


Don't bake the Claude token into an image layer.

Lines 187–200 copy the BuildKit secret into /tmp/claude-secret/token in one RUN, then delete it in a later RUN. In a single-stage Docker build, this leaves the token recoverable from the earlier layer. Additionally, line 191 uses mode 644, which is unnecessarily readable. Consolidate the copy, install, and cleanup into a single RUN instruction, switch to vscode within that step, and use restrictive permissions (600) so the secret never lands in a committed layer.

🔐 Safer one-layer pattern
-# Note: Secret is copied to a temp file readable by vscode, then plugin install runs as vscode
-RUN --mount=type=secret,id=claude_credentials,uid=0,gid=0 \
-    mkdir -p /tmp/claude-secret \
-    && if [ -f /run/secrets/claude_credentials ]; then \
-        cp /run/secrets/claude_credentials /tmp/claude-secret/token && \
-        chmod 644 /tmp/claude-secret/token; \
-    fi
-USER vscode
-RUN /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt || ( \
-        echo "[WARN] Claude プラグインのインストールに失敗しました(認証情報が不足している可能性があります)" && \
-        echo "[INFO] コンテナ起動後に手動でインストールしてください: claude plugin install <plugin>@<marketplace>" \
-    )
-USER root
-RUN rm -rf /tmp/claude-secret \
- && chown -R vscode:vscode /home/vscode/.claude
+RUN --mount=type=secret,id=claude_credentials,uid=0,gid=0 \
+    set -eu; \
+    install -d -m 700 -o vscode -g vscode /tmp/claude-secret; \
+    if [ -f /run/secrets/claude_credentials ]; then \
+        install -m 600 -o vscode -g vscode /run/secrets/claude_credentials /tmp/claude-secret/token; \
+    fi; \
+    su -s /bin/bash vscode -c 'HOME=/home/vscode /tmp/install-claude-plugins.sh /home/vscode/.claude/plugins/plugins.txt' || { \
+        echo "[WARN] Claude プラグインのインストールに失敗しました(認証情報が不足している可能性があります)"; \
+        echo "[INFO] コンテナ起動後に手動でインストールしてください: claude plugin install <plugin>@<marketplace>"; \
+    }; \
+    rm -rf /tmp/claude-secret; \
+    chown -R vscode:vscode /home/vscode/.claude
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.devcontainer/Dockerfile around lines 187 - 200, The Dockerfile currently
copies the BuildKit secret into a layer (creating /tmp/claude-secret/token with
mode 644) then removes it in a later RUN, leaving the token recoverable; fix by
consolidating the secret copy, plugin install, and cleanup into a single RUN so
the token never persists across layers, switch to the vscode user inside that
same RUN when invoking /tmp/install-claude-plugins.sh (or run the install script
via sudo -u vscode), and set restrictive permissions (600) on the temporary
token file before running the installer; ensure the RUN references the same
paths (/run/secrets/claude_credentials, /tmp/claude-secret/token,
/home/vscode/.claude) and the installer script name
(/tmp/install-claude-plugins.sh) so the steps are performed atomically and the
secret is removed at the end of that same RUN.

@keito4
keito4 merged commit 821a647 into main Mar 23, 2026
21 checks passed
@keito4
keito4 deleted the fix/plugin-install-as-vscode-user branch March 23, 2026 07:36
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.106.3 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released リリース済み

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant