この記事はCircleCI Advent Calendar 2015の7日目の為に考えていた記事です。
あまりに時間が立ってしまったので、元記事から切り離して再投稿。
やりたいこと
- AWS VPC上に組んだプライベートサブネット上に
- CircleCIからVagrantでインスタンスを生成して
- GitHubのプライベートリポジトリをクローンする
図にするとこう。
動機
インターネットむき出しじゃないインスタンスに対して、SSHの鍵をばら撒かずにデプロイしたい
前提知識
プライベートサブネット構成
以下の図のように、ユーザからのアクセスをELB経由に限定してアプリケーションサーバ自体は直接インターネットに接続できないプライベートサブネットに置く構成。
アプリケーションサーバを直接インターネットに晒したくない人向け。
ただ、全ての通信を遮断してしまうと、リリースもできない。時刻同期もできない。となってしまうので、そういった管理上必要な通信は別途用意するNATサーバ経由で行う。
プライベートサブネットに置かれたサーバからの通信は全てNAT経由になるので、
一枚目の画像についても正しくはこういうなる。
参考:classmethod - Amazon VPCでELBとNATを使ってよりセキュアな環境を作る【5日目】
2015年の年末にマネージドNATゲートウェイが新機能としてリリースされたので、今後NATサーバを別に立てる必要はないのかも。
本題
こちらのリポジトリにまとめました。内容そのままでプライベートリポジトリに変更してCircleCIでビルドすると動くと思います。
https://github.com/kotatsu360/private-repo-deploy-sample
サンプルでは、VagrantとChefを使っています。
その1:プライベートサブネットのインスタンスへアクセスするための記述
まず、プライベートサブネットに踏み台(NATサーバ)経由でアクセスしてやる必要があります。
重要なのは2箇所です。
echo "Host nat" >> ~/.ssh/config
echo " Hostname ${AWS_NAT_SERVER_IP_ADDRESS}" >> ~/.ssh/config
echo " User ec2-user" >> ~/.ssh/config
override.ssh.username = "ubuntu"
override.ssh.proxy_command = "ssh -A nat -W %h:%p"
ここでは、NATサーバがAmazonLinux, アプリケーションサーバがUbuntuの想定です。
まずpre.shの中でCircleCIの.ssh/configにNATの情報を書き込んでやります。
Vagrantでアクセスするときは、そのHostを使ってプロキシします。
また、この記事の範囲からは外れますが、pre.shはデプロイ前後で自分自身からのSSHを許可して、また閉じています。これは「普段はSSHを不許可、デプロイ時のみ許可」という運用をしたいためです。
※環境変数は最後に設定します
その2:プライベートリポジトリにへアクセスするための記述
config.ssh.forward_agent = true
# ...
override.ssh.private_key_path = [ENV['GITHUB_CLONE_SSH_KEY_PATH'], ENV['AWS_LOGIN_SSH_KEY_PATH']]
file "/etc/sudoers.d/root_ssh_agent" do
mode 0440
owner 'root'
group 'root'
content "Defaults env_keep += \"SSH_AUTH_SOCK\"\n"
end
bash "add ssh_setting to .ssh/config" do
not_if %!grep Host github.com /root/.ssh/config!
code <<-EOC
echo -e "Host github.com\n StrictHostKeyChecking no\n" >> /root/.ssh/config
EOC
end
override.ssh.private_key_path
VagrantでSSHのAgentForwardを有効にしています。
また、
- GitHubへアクセスするための鍵
- Chefがインスタンスへアクセスするための鍵
を指定しています。ここで指定した鍵がAgent Forwardで転送されます。
content "Defaults env_keep += "SSH_AUTH_SOCK"\n"
EC2インスタンスへSSHしているユーザはubuntu
。
Chefはroot
で動作する。ここではroot
へ引き継ぐ環境変数にssh-agentを追加
echo -e "Host github.com\n StrictHostKeyChecking no\n" >> /root/.ssh/config
GitHubからはsshでgit clone
する。git clone
するときにfingerprintの確認で止まるのを防ぐためにStrictHostKeyChecking no
を追加
その3:CircleCI
最後にCircleCIの「Environment variables」で環境変数を設定してやります。
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
についてはCircleCIの「AWS Permission」から設定してもいいかも。
変数名 | 概要 | サンプル |
---|---|---|
AWS_ACCESS_KEY_ID | AWSのアクセスキー | xxxxCIBA |
AWS_DEFAULT_REGION | AWS EC2のデフォルトリージョン | xxxxst-2 |
AWS_DEPLOY_MACHINE_SECURITY_GROUP | EC2インスタンスに割り当てるセキュリティグループ | xxxx228c |
AWS_DEPLOY_SUBNET | EC2インスタンスを立てるサブネット | xxxxbc2c |
AWS_KEYPAIR | AWSで作成するEC2インスタンスにSSHする時用keypair名 | xxxxpair |
AWS_LOGIN_SSH_KEY_PATH | ※1 | |
AWS_NAT_SECURITY_GROUP | デプロイに使うNATのセキュリティグループ | xxxx11db |
AWS_NAT_SERVER_IP_ADDRESS | デプロイに使うNATのEIP | xxxx9.71 |
AWS_SECRET_ACCESS_KEY | AWSのシークレットアクセスキー | xxxx1muU |
GITHUB_CLONE_SSH_KEY_PATH | ※2 |
AWS_LOGIN_SSH_KEY_PATH
AWS_KEYPAIRで設定したkeypairの秘密鍵をCircleCIの「SSH Permission」からHostnameを空にして登録する。
Hostnameが空の場合、CircleCIはfingerprintに従って鍵を作成するので、そのパスを指定する。
~/.ssh/id_xxxxxxxxxxxx3044
GITHUB_CLONE_SSH_KEY_PATH
CircleCIに一度SSHする。~/.ssh/id_github.com
という鍵(CircleCIがリポジトリをCheckoutするための鍵, GitHubでいうDeployKey)があるのでこれの中身を「SSH Permission」にHostname=github.comで登録する。
Hostnameが空でない場合、CircleCIはHostnameに従って鍵を作成するので、そのパスを指定する。
~/.ssh/id_github.com
制約
https://github.com/kotatsu360/private-repo-deploy-sample
このリポジトリのやり方だと、毎回Vagrantで新しいインスタンスを立ててしまうので
古いインスタンスは削除されるようにdeploy.shで設定しています。
EC2インスタンスの中にデータを入れておくと漏れ無く消えます。
振り返ってみて
「GitHubからクローンしたいリポジトリ」 = 「CircleCIでテストしているリポジトリ」の場合、シンプルにSCPで送るという手段もあったような。。
参考
怠け者のためのVagrant+Chef入門 - Qiita
CircleCIからサーバへのSSH接続確立時間を高速化する