GitHub
AWS
chef
vagrant
CircleCI

CircleCI から AWS VPC の private subnet に private repo を git clone

More than 1 year has passed since last update.

この記事はCircleCI Advent Calendar 2015の7日目の為に考えていた記事です。

あまりに時間が立ってしまったので、元記事から切り離して再投稿。

やりたいこと

  1. AWS VPC上に組んだプライベートサブネット上に
  2. CircleCIからVagrantでインスタンスを生成して
  3. GitHubのプライベートリポジトリをクローンする

図にするとこう。

image

動機

インターネットむき出しじゃないインスタンスに対して、SSHの鍵をばら撒かずにデプロイしたい

前提知識

プライベートサブネット構成

以下の図のように、ユーザからのアクセスをELB経由に限定してアプリケーションサーバ自体は直接インターネットに接続できないプライベートサブネットに置く構成。

アプリケーションサーバを直接インターネットに晒したくない人向け。

ただ、全ての通信を遮断してしまうと、リリースもできない。時刻同期もできない。となってしまうので、そういった管理上必要な通信は別途用意するNATサーバ経由で行う。

image

プライベートサブネットに置かれたサーバからの通信は全てNAT経由になるので、
一枚目の画像についても正しくはこういうなる。
image

参考:classmethod - Amazon VPCでELBとNATを使ってよりセキュアな環境を作る【5日目】

2015年の年末にマネージドNATゲートウェイが新機能としてリリースされたので、今後NATサーバを別に立てる必要はないのかも。

本題

こちらのリポジトリにまとめました。内容そのままでプライベートリポジトリに変更してCircleCIでビルドすると動くと思います。
https://github.com/kotatsu360/private-repo-deploy-sample

サンプルでは、VagrantとChefを使っています。

その1:プライベートサブネットのインスタンスへアクセスするための記述

まず、プライベートサブネットに踏み台(NATサーバ)経由でアクセスしてやる必要があります。
重要なのは2箇所です。

pre.sh
echo "Host nat" >> ~/.ssh/config
echo "  Hostname ${AWS_NAT_SERVER_IP_ADDRESS}" >> ~/.ssh/config
echo "  User ec2-user" >> ~/.ssh/config
Vagrantfile
    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:プライベートリポジトリにへアクセスするための記述

Vagrantfile
  config.ssh.forward_agent = true
  # ...

    override.ssh.private_key_path = [ENV['GITHUB_CLONE_SSH_KEY_PATH'], ENV['AWS_LOGIN_SSH_KEY_PATH']]

default.rb
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を空にして登録する。
スクリーンショット 2016-01-30 15.56.27.png

スクリーンショット 2016-01-30 15.56.54.png

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で登録する。

スクリーンショット 2016-01-30 16.14.15.png

スクリーンショット 2016-01-30 16.14.59.png

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接続確立時間を高速化する