AWS利用上のセキュリティを確保する方法

f:id:AK474747:20190810013536p:plain


本番環境を利用してChatSpaceをデプロイしたわけですが、セキュリティ的に色々あるみたいです。 色々あるってザックリしすぎだな。

超簡単にいうと、AWSアクセスキーが盗まれるとすげー金持ってかれるかもしれん、という話

ChatSpaceはメッセージアプリ(というよりwebサイト)になるんですが、ユーザ間でメッセージだけでなく画像ファイルの送受信をすることができます。 今回のデプロイでは画像ファイルの保存先として、AWSのS3を使ってバケットを作成し、バケットの中に画像ファイルをアップロードするという手法を取っています。

その際にRailsアプリケーションとS3との相互認証にaws_access_key_idやaws_secret_accesskeyといったものを設定する必要があります。

本当はデプロイ環境を整えるところからブログを書いていきたかったんですが、絶対押さえておかなきゃいけないところだと感じたので先にまとめます。

git-secretsを導入する

Homebrewを経由してgit-secretsを導入します。

ローカル環境

brew install git-secrets

導入ができたら、設定を適用したいディレクトリに移動して、git-secretsを有効にします。

ローカル環境

 cd chat-space
 git secrets --install

git-secretsの条件を設定する

下記コマンドを実行することで、アップロードしたくないAWS関連の秘密情報を一括で設定できます。

ローカル環境(chat-spaceディレクトリ)

git secrets --register-aws --global

これでgit secrets --installを行なったディレクトリでは、AWSの秘密情報を含んだコミットができなくなります。

「うっかりパスワードをGithubにのせてしまった」という事態を防ぐことができます。

git-secretsの設定を今後作成される全てのディレクトリに適用する

今後作成する全てのディレクトリについて、git-secretsを適用したい場合は、下記コマンドを実行することで、自動で設定が適用されるようになります。

ローカル環境(chat-spaceディレクトリ)

$ git secrets --install ~/.git-templates/git-secrets
$ git config --global init.templatedir '~/.git-templates/git-secrets'

GitHub Desktop経由でgit secretsを利用する

GitHub Desktop経由でgit secretsを利用する場合は、下記コマンドも実行する必要があります。

ローカル環境(chat-spaceディレクトリ)

sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/app/git/bin/git-secrets

※ No such file or directoryのエラーが出る場合 こっちを使いましょう。GitHub Desktopのバージョンが古い場合などに起こるようです。

sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/git/bin/git-secrets

S3で保存先を用意する

いろんなところに書いてあると思うので、バケットの作成方法は省略します。

ですが、バケットポリシーというものを記載する必要があるのでそれだけ書いておきます。

{
    "Version": "2012-10-17",
    "Id": "Policy1544152951996",
    "Statement": [
        {
            "Sid": "Stmt1544152948221",
            "Effect": "Allow",
            "Principal": {
                "AWS": "************①****************"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::************②**********"
        }
    ]
}

①にはIAMユーザーのARNをいれ、②には作成したバケット名を入れます。

画像のアップロード先をS3に変更する

fog-awsというgemを導入する必要があります。 Gemfileを編集します。

Gemfile

(省略)
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end
gem 'carrierwave'
gem 'fog-aws'

この状態でbundle installコマンドを実行しておきます。

次に、image_uploader.rbを編集します。 stroge: fogに変更しましょう。

# encoding: utf-8

class ImageUploader < CarrierWave::Uploader::Base

  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  # include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

(省略)

次に/config/initializers直下に、carrierwave.rbというファイルを作成します。


require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.storage = :fog
  config.fog_provider = 'fog/aws'
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: Rails.application.secrets.aws_access_key_id,
    aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
    region: '自分で調べて入れてください' #例 'ap-northeast-1'
  }

  config.fog_directory  = 'ここにバケット名を入れます'
  config.asset_host = 'https://s3-ここにリージョン名を入れます.amazonaws.com/ここにバケット名を入れます'
end

リージョンというのはAWSでEC2インスタンスやS3を利用するときに選択する地域のことです。 今回であればアジアパシフィック(東京)を選択しているので、「ap-northeast-1」となります。

別のリージョンを選択している場合はその都度調べる必要があります。

.gitignoreを編集する

設定ファイルの中にはawsaccesskeyidやawssecretaccesskeyなどのインターネットに公開してはいけないファイルもあります。

これらを公開してしまうと、AWSのリソースを悪用されてしまい、高額請求の被害に遭う可能性もあります。

そのため、絶対にGitHubに公開してはいけません。

gitignoreを編集することで、コミットしてはいけないファイルを指定します。

ここではsecrets.ymlというファイルにアクセスキーなどを記載する必要があるので、コミット対象外にする必要があります。

.gitignore

# ファイルの最下部に追記
config/secrets.yml

※一度gitの監視下に置かれてしまったファイルは、後からgitignoreに記載しても監視下から除外されません。

下記コマンドを実行して、secrets.ymlをgitの監視下から外す必要があります。

ローカル環境(chat-spaceディレクトリ)

git rm --cached config/secrets.yml

環境変数を設定する

S3にへの接続に必要な認証情報を環境変数として設定します。

この記事では省いていますが、IAMという擬似アカウント?のようなものを事前に作成する必要があり、認証情報をCSV ファイルでダウンロードできます。下記コマンドに乗っているものはそのCSVファイルに書いてある値を示しています。

ローカル環境(chat-spaceディレクトリ)

 vim ~/.bash_profile

# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
export AWS_SECRET_ACCESS_KEY='ここにCSVファイルに乗っている値をコピー'
export AWS_ACCESS_KEY_ID='ここにCSVファイルに乗っている値をコピー'

# 編集した.bash_profileを読み込み直して、追加した環境変数を使えるようにする
 source ~/.bash_profile

本番環境(chat-spaceディレクトリ)

 vim ~/.bash_profile

# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
export AWS_SECRET_ACCESS_KEY='ここにCSVファイルに乗っている値をコピー'
export AWS_ACCESS_KEY_ID='ここにCSVファイルに乗っている値をコピー'

# 編集した.bash_profileを読み込み直して、追加した環境変数を使えるようにする
 source ~/.bash_profile

secrets.ymlへの登録

config/secrets.ymlでは、外部に公開したくないIDやパスワードなどを記述して管理します。

先ほど設定した環境変数をここで読み込むように記述します。

config/secrets.yml

development:
  secret_key_base: ~~~~~~~~
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

test:
  secret_key_base: ~~~~~~~~

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

capistranoの記述を変更する

自動デプロイで一度設定したcapistranoですが、環境変数の読み出しのために記述を変更します。

config/deploy.rb

set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  AWS_ACCESS_KEY_ID: ENV["AWS_ACCESS_KEY_ID"],
  AWS_SECRET_ACCESS_KEY: ENV["AWS_SECRET_ACCESS_KEY"]
}

secrets.ymlは.gitignoreに追加されているため、本番環境のリポジトリには追加されません。 環境変数を本番環境にアップロードするよう、記述を追加する必要があります。 本番環境でsecrets.ymlを参照する必要がある場合、

config/deploy.rb

# secrets.yml用のシンボリックリンクを追加
set :linked_files, %w{ config/secrets.yml }

# 元々記述されていた after 「'deploy:publishing', 'deploy:restart'」以下を削除して、次のように書き換え

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end

  desc 'upload secrets.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml")
    end
  end
  before :starting, 'deploy:upload'
  after :finishing, 'deploy:cleanup'
end

set :linked_filesで指定されたファイルは、config/secrets.ymlを参照する代わりに、shared/config/secrets.ymlを参照するようになります。

desc upload secrets.yml以下の記述は、ローカル環境にあるconfig/secrets.ymlを本番環境のshared/config/secrets.ymlに反映するための設定を行なっています。

これで、.gitignoreに記載されているsecrets.ymlを、Githubを経由せずにデプロイすることができます。

変更をデプロイする

ここまで設定ができたら、変更をコミットしてGithubにプッシュ、本番環境にgit pullした後、ローカル環境のchat-spaceディレクトリでbundle exec cap production deployを実行します。

設定がうまく言っていれば、S3のバケットに「uploads」というディレクトリが出来上がり、ChatSpaceで投稿された画像ファイルが格納されています。

あ〜〜〜書けたんじゃ〜〜〜〜。

なっがいね!そしてIAMとS3の設定を省いたから途中途中でわけわっかんないね!

明日はIAMとS3についてなんとか書きたい・・・。

おやすみなさーい。