安全に `cargo` で `ssh` する


GitHub のプライベートレポジトリに依存する Rust アプリケーションを ビルドする Dockerfile の書き方について説明します。 結論としては、以下の Dockerfile をベースにアプリケーションごとにアレンジするとよさそうです。

# Use the official Rust image as the base image
FROM rust
WORKDIR /app 
# Build the application with cargo, using SSH
COPY Cargo.lock Cargo.toml ./
COPY src ./src
RUN --mount=type=ssh \ cargo build --release
CMD ["./target/release/your-executable"]

ビルドするときは以下です。

$ DOCKER_BUILDKIT=1 docker build . --ssh default

背景

私たちのチームは Rust アプリケーションの開発に従事しています。チームメンバは全員、唯一の GitHub Enterprise Organization に所属しています。私たちの Rust アプリケーションやライブラリのソースコードはチームメンバだけがアクセスできるプライベートレポジトリにホスティングしています。 アプリケーションが git レポジトリにあるライブラリに依存する場合は、アプリケーションの Cargo.toml を以下のように記述します。

[dependencies]
yourrustlib = { git = "ssh://git@github.com/.../yourrustlib.git", branch = "main" }

https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories

このときビルド環境は、このレポジトリにアクセスする権限を有する必要があります。したがって、このアプリケーションを Docker コンテナイメージとしてビルドする場合は何らかの工夫が必要です。うっかり、認証情報が漏洩してしまうことのない安全な方法を調べました。

Prerequisites

安全にビルド時にSSHする

--mount=type=ssh と書いてある RUN コマンドだけは、ホストPC上のように SSH が使えます。 Dockerfile RUN --mount=type=ssh \ cargo build --release この手順の優れている点は以下です。 - 秘密鍵が曝される必要はありません。 - 他のビルド手順では認証情報をうっかりイメージに残してしまう可能性があります。仮に安全だとしても、ただの一度も秘密鍵は曝されるべきではないと思います。 - SSH フォワーディングは明示的です。 - 意図しないタイミングで SSH アクセスすることがないので安心できます。 - Dockerfile はチームメンバーに共通です。 - ホストPC上で依存ライブラリに SSH アクセスができていれば、だれでもそのままビルドできるはずです。

https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09-ae8161d066

トラブルシューティング

Cargo で git cargo の built-in git は ~/.ssh/config を読まない

ので、ホストPCでは CARGO_NET_GIT_FETCH_WITH_CLItrue にしていました。はじめとくに何も考えず Dockerfile に以下を書いていました。

# Configure cargo to use SSH
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true

https://doc.rust-lang.org/cargo/appendix/git-authentication.html#ssh-authenticationhttps://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli

この場合、以下のように接続先の公開鍵を known_hosts に追加する必要があります。

# Add GitHub's SSH key to known hosts
RUN mkdir -p -m 0600 ~/.ssh && \ ssh-keyscan github.com >> ~/.ssh/known_hosts 

追加していない場合は Host key verification failed. エラーになります。

$ docker build . 
// ... 
#16 1.038 Host key verification failed. 
#16 1.039 fatal: Could not read from remote repository. 
#16 1.039 #16 1.039 Please make sure you have the correct access rights 
#16 1.039 and the repository exists. 
// ... 

今回の手順では SSHフォワーディングしているので CARGO_NET_GIT_FETCH_WITH_CLIfalse のまま、cargo の built-in git を使えば良いです。