駆け出しインフラエンジニアが「インフラエンジニアの教科書2」を読んだ

インフラエンジニアになって半年が経った(駆け出しインフラエンジニアというのは怪しい?).インフラエンジニアたるもの1度は読んでおきたい本だったので「インフラエンジニアの教科書2」を読んでみた.本の中でインフラエンジニアが実務で注目しておくべき点がまとめられていたのでそれらを紹介していこうと思う.

読んだ感想としては,書名に教科書という単語が入っているのが納得なほどインフラエンジニアに欠かせない知識が体系的にまとめられている本だった.自分のようにインフラエンジニアになって日が浅い人は,インフラエンジニアとして学ぶべき技術を把握することがそもそも大変なので,この本を読むことで学ぶべき技術の対象がある程度はっきりする.また,オンプレでインフラを構築している方なら,この本を読んだ後に「これインフラエンジニアの教科書2で見たやつだ」ということが実務で起こりうる内容になっていると思う.駆け出しインフラエンジニアには何はともあれ読んでほしい!!

インフラエンジニアの教科書2 スキルアップに効く技術と知識

インフラエンジニアの教科書2 スキルアップに効く技術と知識

  • 作者:佐野 裕
  • 出版社/メーカー: シーアンドアール研究所
  • 発売日: 2016/08/26
  • メディア: 単行本(ソフトカバー)

目次

  • 第1章:プロトコル
  • 第2章:OS
  • 第3章:ネットワーク
  • 第4章:データベース
  • 第5章:WEBのサーバサイド開発言語
  • 第6章:共通鍵暗号方式と公開鍵暗号方式
  • 第7章:障害対策と障害対応
  • 第8章:よく知られたセキュリティ攻撃
  • 第9章:インターネットの運用と発展をつかさどる組織や団体
  • 第10章:RFCの読み方と作られ方
  • 第11章:世界規模のインターネットサービス運営
  • 第12章:インフラエンジニアとして目指す方向

インフラエンジニアのプロセス管理

サーバ上で何か問題が発生したときに各プロセスの状態に着目すると,サーバ上で何が起こっているか把握でき,その対策を考えることができる.
ここではインフラエンジニアがプロセス管理で意識しておくべきことを紹介する.

  • シングルプロセスかつシングルスレッドの処理速度向上

シェルスクリプトなどのシングルプロセスかつシングルスレッド型のプログラムでは,プロセスが1つしかないので実行時にCPUコアが1つしか使われない.このような場合はクロック数が高いCPUに入れ替えると処理速度が向上する.

  • CPUコアを無駄なく使う

マルチコアCPUを搭載したサーバにおいて,実行状態のプロセス数が少なくて,CPU使用率が0%のCPUコアが目立つ場合はCPUリソースが余っている状態である.このような場合は,他のアプリケーションを起動するか,もしくはすでに起動しているマルチプロセス型のプログラムの設定変更を行ってさらにCPUコアを割り当てることでCPUリソースを無駄なく使うことができる.

頻繁にプロセスのコンテキストスイッチが発生する環境では応答速度が落ちる.実行可能状態のプロセスを減らすことでコンテキストスイッチの発生を可能な限り抑えることが好ましい.

  • 割り込み処理

OSに割り込みが発生した場合もCPUコアが使われる.割り込みはIRQ(Interupt ReQuest)とも呼ばれる.Linuxカーネルの場合は割り込みが発生すると常に0番目のCPUコアを使おうとするが,irqbalanceサービスを起動しておくと,他のCPUコアにも割り当てが分散されるようになる.

インフラエンジニアのメモリ管理

不適切なメモリ利用が原因でメモリ使用量が限界近くまで上がると突然パフォーマンスが低下したり,カーネルパニックが発生してOSが落ちたりすることがある.
ここではインフラエンジニアがメモリ管理をするうえで気をつけるべき点を紹介する.

スワップが発生する場合,「物理メモリが足りない」「物理メモリの断片化が進んでまとまったメモリを確保できない」の2つの原因が考えられる.
「物理メモリが足りない場合」は,物理メモリの増設を行うことで対応する.
「物理メモリの断片化が進んでまとまったメモリを確保できない場合」は,大量のメモリを確保しているプロセスを終了させることで割り当てられていた仮想メモリ空間を解放することが一番確実な方法である.下記の例のように,OSによってはスワップ領域自体を解放する方法もあるが,ディスクI/O負荷が非常に高く,かつとても時間のかかる処理なので実サービスで実施するのは危険.

# Linuxにおけるswap領域の操作コマンド
$ swapoff -a ← スワップ領域の解放と無効化
$ swapon -a ← スワップ領域の有効化
  • 空き物理メモリが足りなく見える

物理メモリ使用量が多くて空き物理メモリ量が足りなく見える場合でも,空き物理メモリのほとんどがキャッシュとして使われているだけで実質的には空き物理メモリとみなすことができる場合が多い.キャッシュには「ページキャッシュ」(ファイルのページ単位でのキャッシュ)と「バッファキャッシュ」(ディスクのブロック単位でのキャッシュ)がある.バッファキャッシュはOSで少しだけ使われるが,ページキャッシュはファイルにアクセスすればするほど肥大化していく.
下記の例をみると,32GBの物理メモリに対して,①空き物理メモリ量が1016MB(≒1GB程度)しか残っていないように見えるが,②実質的なメモリ使用量は21868MG(≒21.8GB程度)が使われていて,③キャッシュを除いた実質的な空きメモリ量は10226MB(≒10GB程度)となる.

$ free -m
              total          used           free        shared        buffers        cached
Mem:          32095         310781016             0            357          8852
-/+ buffers/cache:         ②2186810226
Swap:          4095            10           4085

③キャッシュを除いた実質的な空きメモリ量は以下のように算出される.

10.226(MB)≒ Mem free 1016(MB) + buffers 357(MB) + cached  8852(MB)
  • バックアップとページキャッシュ

バックアップを取るために一時的に巨大なファイルを操作すると,ページキャッシュに乗っていたデータが追い出されて,一時的にしか使われないバックアップデータに取って代わられてしまうことがある.日常から負荷が高いサーバの場合,ページキャッシュに乗っていたデータが追い出された瞬間からサーバ負荷が急増してパフォーマンスに影響を与えることがある.
この回避策として,以下のようにddコマンドのダイレクトI/Oオプションを用いてブロック単位でバックアップを取る方法がある.ただし,この方法はとても遅いので注意する.

↓NFSマウントした外部ストレージにバックアップを取る場合
$ dd if=/home/user/1GB.txt of=/mnt/extdisk iflag=direct

↓同じサーバにバックアップを取る場合
$ dd if=/home/user/1GB.txt of=/home/backup/1GB.txt iflag=direct oflag=direct

インフラエンジニアのファイル管理

ファイルの操作はOSを利用する上でもっとも基本的な機能だが,OSのファイル管理の仕組みを理解していないと一見よく分からない事象がまれに起こる.
ここではファイル管理において時折見かける現象を紹介する.

  • ファイルを消しても空き容量が増えない

プロセスがファイルをオープンしている場合は元のファイルを消しても実際は削除されない.この場合,ファイルをオープンしているプロセスを終了すればファイルが完全に削除されるLinuxなどにおいてファイルをオープンしているプロセスIDを特定したい場合にはlsofコマンドを使う.以下の例では,削除したアクセスログをWebサーバのプロセスがオープンしているので,空き容量を増やすにはWebサーバの再起動が必要である.

$ rm /var/log/httpd/access_log
$ lsof | grep deleted
COMMAND        PID          USER          FD          TYPE          DEVICE          SIZE/OFF          MODE          NAME
httpd       103317        apache          3r           REG           8.1               7              273722        /var/log/httpd/access_log(deleted)
  • ディスク容量に空きがあるのにファイルを生成できない

Linuxなどで容量に空きがあるのにファイルを生成しようとすると「No space left on device」というエラーが出てファイルが書き込めないことがある.これはinodeテーブルが枯渇している状態である.以下のようにしてinodeの枯渇を調べることができる.

$ df
Filesystem       1K-blocks            Used           Available      Use% Mounted on
/dev/sba1       262930508           71539660         191390848           28% /    ←空き容量に余裕があるように見える

$ df -i
Filesystem       Inodes                 IUsed       IFree       IUse% Mounted on
/dev/sda1       16703488             16703488           0               100% /    ←inodeが枯渇していることがわかる

ファイルかディレクトリを1個生成する度にinodeが1個消費される.通常の用途では十分な量のinodeが用意されているが,小さいファイルやディレクトリを大量に作るとinodeが枯渇してしまうことがある.この場合はファイルかディレクトリを削除するか,もしくはinodeに余裕がある他のファイルシステム上のディレクトリに移動することでinodeの使用率を下げる方法がある.もしくは動的にinodeテーブルサイズを変更できるファイルシステムを使っている場合はinodeテーブルを拡張するという方法もある.

  • ログ出力でディスク容量を使い果たした

ApacheTomcat,アプリケーションなどのログが肥大化してディスク容量を使い果たしてしまうことがある.このような場合はログを出力しているプロセスを停止してからログファイルを削除するか,もしくは他の大きいパーティションにログファイルを移動してからシンボリックリンクを貼ることで対処可能である.
しかし,どうしてもプロセスを停止できない場合は,以下のように既存のログファイルを他のパーテションにコピーして上書きすることでログファイルの中身を空にするという方法がある.

$ ls -lh /var/log/httpd/access_log
-rw-r--r-- 1 root root 480G Apr 1 19:27 access_log

$ cp /var/log/httpd/access_log /home/user/    ←他のディスクにデータを退避する
$ cp /dev/null /var/log/httpd/access_log      ←ログファイルの中身を空にする

$ ls -lh /var/log/httpd/access_log
-rw-r--r-- 1 root root 211 Apr 1 19:29 access_log  ←ログファイルが空になった後,新しいログが書き込まれている

このときによくある失敗としては,ログファイルをmvコマンドで他のパーテションに退避した後,同じファイル名でログファイルを作成してしまうというものである.この場合,プロセスがオープンしているログファイルのinode番号と新しく生成したログファイルのinode番号が異なるので,同じファイル名であったとしても,プロセスを再起動しない限りは新しいファイルにログが書き込まれることはない.そもそも古いファイルはまだ解放されていない状況なので空きディスク容量は増えない.

$ mv /var/log/httpd/access_log /home/user/           ←他のディスクにデータを移動する
$ echo "" > /var/log/httpd/access_log                ←同じファイル名でログファイルを新規作成する

...しばらくして...

$ ls -lh /var/log/httpd/access_log
-rw-r--r-- 1 root root 0 Mar 27 01:02 access_log     ←新しく作成したファイルにログが書き込まれない
  • 想定よりもハードディスクの空き容量が少ない

ext3/ext4などのファイルシステムでは,リザーブ領域もしくは予約領域と呼ばれるrootしか使えない専用領域がデフォルトで5%確保されている.そこでtune2fsコマンドでリザーブ領域を0%に変更すると,すべての領域が使えるようになる.

$ df -kh | grep -e /dev/sda3 -e Filesystem
Filesystem        Size        Used        Avail        Use% Mounted on
/dev/sda3         536G          0G         508G          0%    /home        ←使用率が0%なのに使える容量が実容量より少ない

$ tune2fs -l /dev/sda3 | grep -e "Block count" -e "Reserved block count"
Block count:             142540544
Reserved block count:    7127027     ←予約領域が5%分割り当てられている

$ tune2fs -m 0 /dev/sda3             ←予約領域を0%にする
tune2fs 1.41.12(17-May-2010)
Setting reserved blocks percentage to 0%(0 blocks)

$ tune2fs -l /dev/sda3 | grep -e "Block count" -e "Reserved block count"
Block count:             142540544
Reserved block count:    0           ←予約領域が0%になった

$ df -kh | grep -e /dev/sda3 -e Filesystem
Filesystem        Size        Used        Avail        Use% Mounted on
/dev/sda3         536G          0G         536G           0%    /home         ←使える容量が実容量と一致した

インフラエンジニアのネットワーク管理

ここではネットワークまわりでインフラエンジニアが関与すべきことを紹介する.

  • ネットワーク経由でのファイルコピーが遅い

ネットワーク経由でファイルコピーを行うとき,期待通りの速度が出ないことがある.この場合,以下のようにさまざまな要因が考えられる.

  1. 「ネットワークインタフェースの制限」
    1GbpsのNICを使っている場合,理論上は1Gbps / 8bit = 125MB/sが転送速度の上限となる.
  2. 「ネットワークスイッチのスイッチング性能不足」
    サーバとサーバの間にネットワークスイッチを挟んでいる場合,そのネットワークスイッチのスイッチング性能不足の場合がある.特に家庭用スイッチングハブを挟んでいる場合はそこがボトルネックになっている場合がある.この場合は業務用スイッチに置き換えるか,もしくはサーバ間をクロスケーブルを用いて直結することで転送速度が向上する.
  3. WindowsNICのオフロード機能を使っている」
    Windowsの場合,ネットワークアダプタのオフロード機能が有効になっていると,転送速度が落ちる場合がある.この場合,ネットワークアダプタのプロパティでオフロードという名前がついている設定項目をすべて無効にすると転送速度が向上する可能性がある.
  4. プロトコル
    扱うプロトコルによって桁違いの速度差が出ることがある.例えば,Windows Server 2003まで使われていたSMB1.0の場合,WAN間でのファイルコピーが特に遅いと言われている.

インフラエンジニアのデータベース管理

ここではデータベースまわりでインフラエンジニアが気をつけるべき点を紹介する.

万が一のデータベース障害に備えて正しい方法で定期的にバックアップを取っておくことが重要である.障害が発生したデータベースを障害発生直前の状態に復旧するためには,バックアップデータだけではなく,バックアップ取得時点から障害発生直前までに出力されたすべてのWAL(Write Ahead Logging)ログが必要であることに注意する.WALとは,書き込みや更新といった更新処理が走るとき,変更内容をログとして記録し,その後,実データに反映する仕組みである.バックアップデータを復元しただけだと,データベースはバックアップ取得時点の状態に戻るだけなので,障害発生直前の状態まで復旧するには,バックアップを復元した後WALログを流し込む必要がある.

  • RDBMS起因でない原因不明のパフォーマンス低下対応

原因不明のパフォーマンス低下が起こった場合,ハードウェアやOS側に問題がある場合がある.ハードウェア故障はOSのシステムログ(/var/log/syslogやイベントログなど)やRAIDコントローラーのログで確認できる.比較的よくみかけるのがハードディスクにエラーが発生していてパフォーマンスが低下するパターンである.ハードディスクは不良セクターが発生してもある程度は自己修復機能で修復され,エラーカウントが閾値を超えるとサーバはやっとハードディスク故障と認定する.しかし,実際はサーバが故障と認定する前の状態でも事実上,故障状態でパフォーマンス低下によるサービス影響が起きていることがある.このとき,ハードディスク内のエラーカウント値が急速に増えているものの,まだ閾値を下回っているため,OSのシステムログに現れてこず,普通に見ただけではハードウェア故障に気づくことができない.このような場合はRAIDコントローラーのログやステータス情報を見てハードディスクの劣化状態を知る以外に方法がない.MegaRAIDコントローラを使用している場合の例を以下に示す.

$ MegaCli -PDList -aall | grep -e Slot -e Count
Slot Number: 1
Media Error Count: 0
Other Error Count: 0
Predictive Failure Count: 0

Slot Number: 2
Media Error Count: 0
Other Error Count: 0
Predictive Failure Count: 0

↓ハードディスク故障の兆候が見える
Slot Number: 3
Media Error Count: 35
Other Error Count: 6
Predictive Failure Count: 2

Slot Number: 4
Media Error Count: 0
Other Error Count: 0
Predictive Failure Count: 0

誤植

以下のサイトに公開されている正誤表以外の誤植があったのでメモ程度にまとめておく.

www.c-r.com

  • Chapter3 P.105:特にdiscard値とunbindの発生は特に注意 -> 「特に」が2回出てきている
  • Chapter12 P.248:現在のバージョンの課題なそ -> 現在のバージョンの課題など
  • あとがき P.250:本があったいいのに -> 本があったらいいのに

まとめ

  • 「インフラエンジニアの教科書2」を読んだ
  • インフラエンジニアに欠かすことのできない知識が体系的にまとめられており,特に駆け出しインフラエンジニアにおすすめ
  • 記事ではインフラエンジニアが意識しておくべき点をまとめてみたが,これは一部分でありこれ以外にもまだ紹介されていた

WebアプリケーションでのSSHで不具合を出さないためにknown_hostsを理解する

PHPSSHを使ってファイルを転送する際にうまく転送できず,原因がknown_hosts周りだったので改めて調べてみた.うまくいかなかった事象・原因とその解決方法もまとめておく.なお今回はPHPを使用しているが,Rubyシェルスクリプトでも同様の問題は起きうる.

known_hostsとは

一般にクライアントが公開鍵認証のSSHを使ってサーバへログインする際,通信を暗号化するためにサーバが公開しているホスト鍵を使って通信を始める.ホスト鍵とは,SSHで公開鍵認証や暗号化通信を使用するためにサーバが用意している秘密鍵と公開鍵のペアである.この記事では公開鍵のことをホスト鍵と呼ぶことにする.
known_hostsにはSSHで繋いだサーバのホスト鍵もしくはホスト鍵のフィンガープリント」が登録されている.デフォルトの設定だと初めてサーバにSSHでアクセスする際以下のように聞かれると思う.これにyesと答えるとknown_hostsに接続先のサーバのホスト鍵もしくはフィンガープリントが登録される.

$ ssh user@192.168.100.200
The authenticity of host '192.168.100.200 (192.168.100.200)' can't be established.
ECDSA key fingerprint is SHA256:qKZyNM7JGgEGH7QWLANKBpNlFwM1XT7i45Z5oB7V61Y.
Are you sure you want to continue connecting (yes/no)? yes

二度目のアクセスからは「known_hostsに記録されているサーバのホスト鍵もしくはフィンガープリントと実際のホストが返すサーバのホスト鍵もしくはフィンガープリントが一致した場合にそのホストは認証済みである」と判断され確認メッセージは表示されない.ここでは割愛するがホスト公開鍵に対するホスト秘密鍵をサーバが所有していることを確認するには公開鍵暗号技術のチャレンジ&レスポンス方式を用いる.

known_hostsには「なりすまし」による第三者の介入を防ぐという重要な役割がある.二度目以降のSSHによるアクセスにおいてホスト鍵を検証することでホストを認証するので,誤って第三者が用意しているサーバに繋ぐことを防ぐ.
また,ワーム1のようなマルウェアの感染に対する緩和策としても有効である.要するに,初回のSSHに確認が求められるので,被害を受けたサーバから自動で違う新しいサーバへ接続できなくする効果があるというわけだ.

まとめるとknown_hostsとは「公開鍵認証のSSHでログインしたサーバのホスト鍵を記録し,次回からのホスト認証で利用されるファイル」である.

構成

今回はPHPのcmd関数を使って,SSHを用いたrsyncコマンドを実行してサーバAからサーバBへファイルを転送することを想定する.このプログラムはブラウザからApache経由でアクセスされ,プログラムの実行ユーザはwww-dataとする.
サーバAからサーバBへのSSHでは公開鍵認証を使用するため,サーバA上でsu -u www-data ssh-keygenのようにwww-dataとしてパスフレーズ無しの秘密鍵・公開鍵のペアを生成し,公開鍵をサーバBへ設定した.

f:id:a-mochan:20191220000318p:plain
構成図

起きた事象と原因

ブラウザでアクセスしてWebアプリケーションが動いてrsyncコマンドが実行されると,以下のようなメッセージがApacheのエラーログに出力された.

Host key verification failed.
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(235) [sender=3.1.2]

ホスト鍵の検証に失敗している.冒頭で述べたように,「サーバにSSHで初めてアクセスする場合,クライアントのknown_hostsにホスト鍵を登録しなければならない」ためである.

解決策1

1つめの解決策は事前にサーバBSSHでアクセスしておき,known_hostsにホスト鍵を登録しておく方法である.この方法ではサーバBのホスト鍵が何らかの理由で変わると,known_hostsで保存されているホスト鍵と一致しなくなりホスト鍵の検証に失敗するので注意する.また接続するサーバが増えた時も事前にSSHでアクセスしknown_hostsにホスト鍵を追加しておく必要があることに注意する.

解決策2

2つめの解決策は,クライアントのSSHの挙動を設定するファイルである/etc/ssh/ssh_configStrictHostKeyCheckingnoとする方法である.これでサーバBへの初回アクセス時に対話的に聞かれることなく自動でknown_hostsにホスト鍵が追加される.StrictHostKeyCheckingのパラメータは以下のようになっていてデフォルトはaskである.2

StrictHostKeyChecking 意味
ask 初回のSSHでサーバにアクセスする際にknown_hostsへサーバのホスト鍵を登録するか対話的に確認される
yes known_hostsへの新規追加はできず,既にknown_hostsで定義されているサーバへしかアクセスできない
no 初回のSSHでサーバにアクセスする際に自動でknown_hostsにホスト鍵を追加

StrictHostKeyCheckingnoにする時は,利便性とセキュリティ面を考慮して設定しよう.

まとめ

  • 公開鍵認証のSSHを使ったファイル転送を実現するのにknown_hosts周りを調べた
  • /etc/ssh/ssh_configはマニュアルページにまだまだたくさんの設定項目がまとめられているので困ったら参考にしようと思う
  • known_hostsに追加されているデータが何なのか,追加・削除にどんな意味があるのか等の概要を理解できた

EC2 Image Builderの「ロール」と「ビルド環境」は適切に設定しよう

先日のre:Inventで発表されたEC2 Image Builderを試してみた.EC2 Image Builderで簡単なAMIを作成して,つまづいた箇所があったのでメモとして残しておこうと思う.

EC2 Image Builder

EC2 Image Builderとは,OSイメージのレシピとイメージ作成後のテストをパイプラインでつないでAMIの作成を自動化してくれるサービスである.従来,AMIの作成を自動化するにはAWS Systems Manager(以下,SSM)やHashiCorp製品のPackerなどを組み合わせる利用が目立ったが,EC2 Image BuilderはAMI作成に関する一連の作業を一気に引き受けて自動化してくれる.仕組みとしてはEC2 Image Builderが裏でSSMを動かしてAMI作成作業を抽象化している.現状ではAmazon Linux 2Windows Server 2012R2, 2016, 2019をOSとしてサポートしており,すべてのリージョンで使用可能である.

今回試してみること

以下のブログを参考にEC2 Image BuilderでAMIを作成する.今回はPHP7.3がインストールされたAMIを作成し,そのAMIが正常にEC2で起動するかテストする.その中でつまづいたポイントを紹介していく.

aws.amazon.com

EC2 Image Builderを使用してAMIを作成

説明に入る前につまづいたポイントを列挙しておく.

  1. EC2 Image Builderで使うロールのポリシーにEC2InstanceProfileForImageBuilderAmazonSSMManagedInstanceCoreが必要
  2. AMIをビルドする環境がインターネットに出ることができないとダメ

それではEC2 Image BuilderでAMIを使っていく.まずはEC2 Image Builderのサービスページで「Create image pipeline」を押下する.すると全部で3ステップあるパイプラインの作成画面が出てくる.1ステップ目でレシピを設定し,2ステップ目でパイプラインを設定し,3ステップ目でオプションを設定する.それぞれ設定していく.

1ステップ目では,AMIのOSAMIに入れるソフトウェアテスト方法を設定する.
今回使うOSはAmazon Linux 2とし,新規でAMIを作成することとする.ちなみに既存のAMIを指定することも可能である.
次に,componentsを選択する.EC2 Image BuilderではAMIにインストールするパッケージやミドルウェアcomponentsとして管理しており,componentsを選択することでAMIに必要なソフトウェア群を入れることができる.componentsはカスタムして作成することもできるが,ここではAWS管理のPHP7.3のパッケージを指定する.設定は以下のようになる.

f:id:a-mochan:20191213224408p:plain
AMIにインストールされるOS

f:id:a-mochan:20191214000955p:plain
AMIにインストールするソフトウェア群とテスト方法

2ステップ目では,EC2 Image Builderを実行するロールビルドのスケジュールビルドする環境を設定する.
ロールの設定では1つめのつまづきポイントに気をつける必要がある.AWS管理のcomponentsのビルドとテスト実行に必要十分な権限を持つEC2InstanceProfileForImageBuilderポリシーとSSMを動かすためのAmazonSSMManagedInstanceCoreポリシーをロールに付与しなければ,ほとんどの場合EC2 Image Builderが正常に動かない.自分はAmazonSSMManagedInstanceCoreポリシーをつけ忘れたのでエラーが出た.エラーの詳細はSSMの「オートメーション」ページで確認することができる.このポリシーをつけ忘れると下図のようなタイムアウトが起こるので,もしこのエラーが出たらAmazonSSMManagedInstanceCoreポリシーの付与忘れを疑うとよいかもしれない.

f:id:a-mochan:20191213211441p:plain
SSMでのエラー

ビルドのスケジュールは手動で行うように設定しておき,ビルドする環境の設定に移る.ここでは2つめのつまづきポイントに気をつける.AMIを作成する際多くの場合インターネットを通して通信すると思うので,ビルドする環境はインターネットへ通信可能なところに置いておく必要がある.自分はビルド環境を特に何も指定していなかったので,インターネットに出ることができないデフォルトの環境でビルドしてエラーが出てしまった.このエラー画面はいくつかあるので割愛させていただくが,1つめのつまづきポイントを解消してなお失敗する場合はビルド環境を疑うとよいかもしれない.設定は以下のようになる.

f:id:a-mochan:20191213233737p:plain
ロールとビルドスケジュールの設定

f:id:a-mochan:20191213234355p:plain
ビルド環境の設定

3ステップ目は,ソフトウェアライセンスとAMIの関連付けAMIの名前・タグAMIの配布先指定の設定を行う.
こちらはオプションなので必要であれば設定する.

これでパイプラインの設定は終わりである.あとは作成したパイプラインを実行すれば,EC2上での起動テスト済みかつPHP7.3がインストールされたAMIの出来上がり.

まとめ

  • EC2 Image Builderを使って簡単なAMIを作成した
  • AMI作成に自動化をサクッと作れるのはいい
  • エラー内容から直接的な原因が分かりづらかった
  • AWS管理されていないソフトウェアをcomponentsに書いて管理するより,既存で管理しているGitHubやCodeCommitから適用したいができない
  • サポートしているOSが増えて欲しいなという気持ち
  • まだまだ出たばかりなので今後に期待

PHPerの学びの場「PHPカンファレンス2019」に参加した

自身としては今年で3回目の参加となるPHPカンファレンス2019に参加してきた.年に一度開催されるPHPカンファレンスは今年も盛況でとても学びのある面白いセッションが多かった!自分の参加したセッションのメモと感想を残そうと思う.

fortee.jp

MVCにおける「モデル」とはなにか

MVCにおけるモデルって何だっけ?という技術よりもむしろ思想や概念に近いお話をされた.

  • ドメインモデル
    • ドメインモデル=ドメイン(目的)を情報処理システム(手段)によってモデル化したもの
    • ドメインモデルは社会的なシステムとして個人を制約する
  • メンタルモデル

    • メンタルモデルとは頭の中にある「ああなったらこうなる」といった行動のイメージを表現したもの
    • 道具から適切なメンタルモデルを構築するにはフィードバックも重要.フィードバックに即時性がないとメンタルモデルを作り上げられない.
    • コンピュータのメンタルモデルを作るのは難しい
    • ユーザイリュージョンとは,ユーザに錯覚を起こさせることで本来なら理解し難い対象をあたかも理解したかのように思わせること.
    • スプレッドシートはユーザイリュージョンが働いているいい例
    • コンピュータのメンタルモデルにおいて,人は作業そのものに取り組んでいる状態がいい
  • パーソナルコンピュータ

    • システムと個人の関係を変える
    • 人がシステムをコントロールする.そのために,コンピュータの言語を理解する必要がある.
  • MVCとは何か

    • ユーザがドメインの情報(ドメインモデル)を直接みて操作する時,ユーザのメンタルモデルとドメインモデルには距離があるのでユーザイリュージョンが必要.
    • ユーザイリュージョンを起こすためにツールとモデリング言語が必要.これがMVCでいうVCの部分.

感想

MVCについてこんなに考えたことはなかったし,思想や概念の話がメインですごく頭を使った.理解が追いつかない部分もあったが,自分の中にあるふわっとした考えを深めて言語化できそうな情報源や言葉を知ることができたのでまた後で見直したい.

徳丸先生による徳丸試験例題解説とPHP7初級書籍贈呈キャンペーンと市場動向

ランチセッションは2019/12から始まるWebセキュリティ試験(通称徳丸試験)の例題を徳丸先生自ら解説された. www.phpexam.jp

大学院生の頃に「体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」(通称徳丸本)を読みふけっていたので出題された3問の例題に正解できた🎉出題範囲は徳丸本(第二版)からだそう(ただし7章 脆弱性診断入門は範囲外)なので受けみようかという気になってきた.

思想と理想の果てに -- クリーンアーキテクチャのWebフレームワークを作ろう

クリーンアーキテクチャフレームワークを作っているお話をされた.

質疑応答
Q ユースケースからのエンティティは直接操作してよい?
A ユースケースからのエンティティはインタフェースを介さないので直接操作してOK

感想

前半部分のクリーンアーキテクチャの解説がとても分かりやすく,それだけでもとても勉強になった.フレームワークを作成する話では試行錯誤の様子をたくさん話されていた.大事なポイントはアウトプットすると情報が集まってくるだと思う.新しいことにチャレンジするには,まずやってみてアウトプットして周りからの反応やアドバイスをもらいさらに改良していくことの重要さを改めて感じることができた!

Webサービスのトラブルの現場 ~ Webサービスの今と昔 ~

昔に経験した現場のトラブルを振り返り,今はどのようなことに気をつければよいかをお話された.

Webサーバが死ぬ原因

  • アプリケーションのバグ
    • テストを書こう
  • ハードウェア障害
  • アクセス過多
    • モニタリングしよう
    • trdsqlがめちゃくちゃ便利
  • おや?共有サーバの様子が...
    • 一緒にホスティングされている他のサービスがおかしいと巻き込まれることもある

バッチの突き抜け

サイトにアクセスできない

  • どこのレイヤーがダメなのか把握するためにしっかり切り分けする.

失敗から学ぶクラウドの正しい歩き方

  • データストアが死ぬと対応が難しい
  • 制御できるところできないところを知る
  • 自分たちの急所を知る

まとめ

  • 便利になるということは抽象化されている.トラブル時は抽象化されているものの仕組みを知っている必要がある.

質疑応答
Q 障害など何か問題が起きた時,自分の知らないところが原因だった場合はどうやって調べる?
A 知らないことはTwitterで調べたりして,知らない人から聞くことも1つの方法.

感想

全体としてインフラっぽい内容だった.身の周りで起きた障害を紹介しつつ,n+1構成やシステムの切り分けの話など基本的な部分の大事さをお話された.システムを作るにあたって基本は大事だしおろそかにしてはいけないと感じた.さらに,trdsqlといった便利なツールや可視化の方法などは実務で使えそうでとても有意義な時間だった.

オニギリペイのセキュリティ事故に学ぶ安全なサービスの構築法

オニギリペイという架空の決済システムのロールプレイを通してサービスの構築を学ぶ.

オニギリペイの8つの試練

  1. キャンペーンを実施したら,某筋からお叱りを受ける
  2. ログインIDを発番したのに不正ログインが多発
  3. 二段階認証を強制したのに不正ログインが止まらない
  4. ヘルプデスクが狙われる
  5. スマホアプリの脆弱性を指摘される
  6. スマホアプリのアップデートを広報したらアプリがリジェクトされる
  7. 「あの有名な脆弱性」で大変なことに
  8. WAFを導入したらかえって脆弱になる

オニギリペイから学ぶ今後のセキュリティ

  • 開発時のセキュリティも重要だが,上流からのセキュリティの検討も大事
  • リスクアセスメントの方法
    • ベースラインアプローチ:既存の標準や基準をもとにベースライン(自組織の対策基準)を策定し,チェックしていく方法.徳丸本を活用するのもよい.
    • 非形式的アプローチ:徳丸さんのような専門家に聞く.
    • 詳細リスク分析:情報資産に対し資産価値脅威脆弱性セキュリティ要件を識別し,リスクを評価していく.
    • 組み合わせアプローチ:よく用いられるのがベースラインアプローチ詳細リスク分析のいいとこ取り.
  • 業務フローでリスク分析
  • 発注がセキュリティを左右する.発注者が提案書の内容に関与できるのは提案依頼書を出すタイミングだけ.
  • 脅威分析はIPAが公開している「IoT開発におけるセキュリティ設計の手引き」がよくまとまっているので参考にすると良い

感想

業務で開発現場を離れインフラをメインでやるようになってからセキュリティについて考えることが増えてきた.そんな状況の中,今起こりうるシチュエーションでセキュリティのお話を聞けたのはすごくよかった.開発現場レベルのセキュリティももちろん大事だが,脅威分析やセキュリティアセスメント等の上流工程でのセキュリティも重要だということをしみじみ感じた良いセッションだった.

www.slideshare.net

LT

PHPカンファレンスはメインのセッションの後にLTが行われる.弊社からも3人の新卒が発表したので紹介させていただきたい.

1人目は新卒2年目の@yu12co_mmさん.1つのリポジトリで管理されていた2つのサービスを分割する際に使用したビジュアルリグレッションテストの話をされた.この言葉を聞いたことのない方はお読みいただければと思う.スライドのストーリーも面白い!

2人目もまた新卒2年目の@shimamizさん.PHP力を高めるためにPHP競技プログラミングをやった話をされた.PHPtという標準入力で便利なツールがあるらしい.

2年目エンジニアがスキルアップのためにPHPで競プロやってみた by 瑞 | トーク | PHP Conference Japan 2019 #phpcon - fortee.jp

3人目は新卒6年目の@_ohshigeさん.今まで実績のないクリーンアーキテクチャを採用してプロダクト作った時の話をされた.クリーンアーキテクチャの導入を検討しているそこのあなたにおすすめ.

全体の感想

毎度のことながら参加したすべてのセッションがとても面白かった.登壇者や関係者のみなさまにお礼を申し上げたい!
PHPにあまり関係のないセッションもあったりするが,それがとても勉強になったりして,いい意味で裏切られる感じが好きだ.
一応PHPカンファレンスなのでPHPの話もしておくと,次のPHP8ではJust-In-Time Compilerにより処理が早くなることいくつかの構文のシンプル化型の強化が見所だ.PHP7が出たと思ったのがすごく最近な気がするのにあっというまにPHP8がくる.開発が早く活発なのでこれからも楽しみだ.
あと,先輩や後輩が発表してたのを見て刺激をもらったので自分もどこかで発表したいなと思った.また来年も行くぞ!

GASを使ってALBのIPアドレス変更を検知しSlackへ通知する

世の中の数多くのサービスがAWSやAzure,GCPなどのクラウドにホストされることが普通になり,オンプレの時に比べてサーバや各種リソースのIPアドレスが動的に変わることが増えてきた.そんななか,業務でどうしてもクラウドで動作中のリソースに紐づいているIPアドレスの変更を検知して通知する仕組みが必要だったので,今日はその方法を紹介しようと思う.

前提

今回はAWSのロードバランサの1つであるApplication Load Balancer(以降ALB)を検知対象とする.ALBを立てると多くの場合,1つのDNS名に対して複数のIPアドレスを持つ状態となる,ここでは2つIPアドレスを持つ場合を考える.

やりたいこと

ALBが持つ2つのIPアドレスのうち,片方を事前に記録しておく.ALBのIPアドレスの変更により,その記録したIPアドレスがALBのIPアドレスと異なる状態になったとき通知を行う.

検知と通知方法

スプレッドシートGoogle Apps Script(以降GAS)を使い,Slackへ通知する方法で実現する. まずはシートに初期値として現状のALBのIPアドレスをメモしておく.また検知した日新しいIPアドレスを記録するように列を用意しておく.

f:id:a-mochan:20191128234922p:plain
ALBの初期IPアドレスをメモ

GASで実装する内容は主に以下の2つ.

  1. DNS over HTTPSを使ってクラウド上のALBのIPアドレスを取得
  2. もし今記録しているIPアドレスと違ったら変更履歴をシートに記録しSlackへ通知

まずはDNS over HTTPSを使って名前解決する.DNS over HTTPSとは,文字通りHTTPSを用いてDNSの通信を行う技術である.今回はGoogleが提供してくれているGoogle Public DNS over HTTPSという全世界の人が無料で使えるフルリゾルバを使用する.どんなものかというのは,以下のURLを叩くと雰囲気がわかると思う.

https://dns.google.com/resolve?type=A&name=example.com

次に,Slack通知を行う部分を書く.以下のブログが参考になった.

qiita.com

Incoming WebhookをSlackのチャンネルに設定して,hookするURLさえ分かれば通知できる.完成版のGASは以下のようになる.

function resolveName() {
  var ss = SpreadsheetApp.getActiveSheet();
  var apiUrl = 'https://dns.google.com/resolve'; // Google Pubic DNS API URL
  var type = 'A'; // Aレコードを指定
  var name = 'hogehoge.com'; // 検知するドメイン名

  // 名前解決
  var requestUrl = apiUrl + '?name=' + name + '&type=' + type;
  var response = UrlFetchApp.fetch(requestUrl);
  var responseText = response.getContentText();
  
  // レスポンスをパース
  var json = JSON.parse(responseText);
  var newIpList = json.Answer.map(function(ans) {
      return ans.data
    });
  
  // 登録IPアドレス取得
  var oldIp = ss.getRange(2,1).getValue();

  // IPが変更したか確認
  if (newIpList.indexOf(oldIp) !== -1) {
    return;
  }
  
  // 文字列としてパース
  newIpList.sort();
  var ipAddr = newIpList.join('\n');

  // 変更日時とIPアドレスを記録
  var date = new Date();
  Utilities.formatDate( date, 'Asia/Tokyo', 'yyyyMMdd: hhmm');
  var lastRow = ss.getLastRow();
  ss.getRange(lastRow + 1, 3).setValue(date);
  ss.getRange(lastRow + 1, 4).setValue(ipAddr);

  // Slackへ通知
  notifySlack(name,ipAddr);
}

function notifySlack(name,ip_addr) {
  var postUrl = 'https://hooks.slack.com/services/hoge/fuga/moge';
  var username = 'IP報告する男';
  var message = name + 'のIPアドレスが変わりました\n' + ip_addr;

  var jsonData =
  {
     "username" : username,
     "text" : message
  };
  var payload = JSON.stringify(jsonData);

  var options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  };

  UrlFetchApp.fetch(postUrl, options);
}

テストとしてGAS上のタブから「実行」を選んでresolveName関数を実行する.IPアドレスが変わっていればスプレッドシート検知した日新しいIPアドレスが記録され,Slackへ通知が届く.

f:id:a-mochan:20191128234446p:plain
変更履歴が追加されたシート

f:id:a-mochan:20191128233942p:plain
Slack通知画面

あとは,GAS上のタブから「編集」->「現在のプロジェクトのトリガー」をクリックし,このスクリプトを時間ベースで定期実行するようにしておけば,IPアドレスが変わったタイミングでSlackに通知が届く.Slackに気づいたら,シートの初期値として設定したIPアドレス通知された新しいIPアドレスに手動で書き換える運用をしている.もちろんGASで自動的に書き換えてもらってもよい.

f:id:a-mochan:20191129100746p:plain
定期実行設定画面

まとめ

  • GASを使ってALBのIPアドレス変更を検知してSlackへ通知した
  • GASからSlack通知も便利だが,Google Public DNS over HTTPSも便利だった
  • 今回の対象はALBだったが,他の対象にも使えそうな手段だと思う

証明書入れ替えでAkamaiにまつわるトラブルに遭遇した話

Webサービスに関わるインフラエンジニアをやっていると証明書を入れ替える時がしばしばあると思う.今日は業務中の証明書入れ替えで起こったトラブルについて書いていく.CDNサービスの1つであるAkamaiと関係のあるトラブルなので,Akamaiを使っている方は参考になるかもしれない.

構成

弊社はメディアサービスを運用しており,ページの表示速度向上のために,画像等の静的なファイルについてはAkamaiを利用してキャッシュを効かせている.まずは必要な部分のシステムと画像が表示されるまでのフローを図示する.

f:id:a-mochan:20191121234104p:plain
画像が表示されるまでのフロー

弊社では画像を置くためのサーバ(以降オリジンサーバ)を用意しており,それらの画像はAkamaiが管理するサーバ(以降エッジサーバ)にキャッシュとして存在している.画像が表示されるまでのフローは以下の通り.

  1. ユーザがWebサーバからコンテンツを取得
  2. そのコンテンツで表示される画像はエッジサーバから取得
  3. エッジサーバは画像がキャッシュに存在するならそれをユーザへ返し,存在しなければエッジサーバがオリジンサーバから画像を取得しユーザへ画像を返す

なお,社内のDNS上でimage.example.jpexample.jp.edgesuite.netへCNAMEとして向けられており,画像へのアクセスはhttps://image.example.jp/path/to/image.jpgのような形でアクセスすると思っていただけたらよい.

起きた現象

今回,証明書の入れ替えを行うのはWebサーバとオリジンサーバである.いつものように証明書を入れ替えて,入れ替え対象となるドメインを確認した.ざっと確認してOKだったので,今日も無事に完了かと思ったその時,現場の開発エンジニアから以下のようなスクショが飛んできた.

f:id:a-mochan:20191121220647p:plain
ブラウザのエラー画面

エッジサーバからan error occurred while processing your requestが返されるらしい.詳しく聞くとどうやらエッジサーバにキャッシュとして存在するhttps://image.example.jp/path/to/image.jpgのようなアクセスだと画像が表示されるが,エッジサーバにキャッシュとして存在しないhttps://image.example.jp/path/to/image.jpg?hogehogeのようなアクセスだと画像が表示されないらしい.オリジンサーバのアクセスログを見てもそれらしいアクセスはない.おそらくエッジサーバからオリジンサーバへのアクセスが怪しい.報告してくれたエンジニアの話を聞くとタイミング的に証明書を変えた時間からおかしくなったようなので,試しに元の証明書に戻したところ無事画像が表示された.

原因調査

すぐにAkamaiのサポートに問い合わせた.するとサポートから,「エッジサーバがオリジンサーバとSSL通信を実施する際に,Origin SSL Certificate Verificationの設定と一致しない証明書が使用されたため,エラーが発生した可能性がある」と返答がきた.これはAkamaiが信頼しているルート認証局がチェーンに入っていない証明書をオリジンサーバで使うとエラーになるということを表している.どうやらAkamaiの管理コンソールで,Akamaiが信頼するルート認証局を設定する項目があるらしく探してみるとそれらしき項目を発見.

f:id:a-mochan:20191121222159p:plain
Akamaiが信頼するルート認証局を設定する画面

信頼するルート認証局として1つ登録してある.ここで我々インフラは原因に気づいた.今回発行した証明書のルート認証局が変わっていたのである.つまり変更した新しいルート認証局Akamaiの信頼するルート認証局一覧になかったのでエッジサーバからオリジンサーバへのアクセスができなくなったというわけだ.

対応

原因がわかったので,Akamaiの信頼するルート認証局一覧に新しい認証局を追加しようかと考えた.しかし,頻繁に起こるわけではないにしてもルート認証局が変わるたびにAkamaiの設定をいじるのはあまりしたくない.そう思いながら設定項目を見ていると,我々が設定していた方法は認証局をカスタムで設定する方法であって,Akamaiが管理するルート認証局セットを使う方法もあることに気が付いた.仮にまた証明書のルート認証局が変わった場合,Akamaiの信頼するルート認証局に新しいルート認証局が登録されているかを確認しなければならないのはどちらの設定方法でも言えることだ.しかし,Akamaiが管理するルート認証局セットを使う方法では以下で図示しているように数多くのルート認証局が登録してあり,今後の運用で今回のような実害が発生しにくいのはこの方法だと思ったのでこちらを使うことにした.ただここで1つ罠なのが,以下の図で示すように,我々が新たに使うルート認証局の共通名がブランクになっていてわかりにくい点だ.この場合はフィンガープリントの方で判断するしかないので注意する.

f:id:a-mochan:20191121222342p:plain
Akamaiが管理するルート認証局一覧

Akamaiの信頼するルート認証局の設定を変更し,既存のWebサービスに影響が出ていないことを確かめ,改めて証明書を入れ替えると,エッジサーバにない画像はオリジンサーバから返却され正常に表示された.めでたしめでたし.

まとめ

  • 証明書入れ替えにより,Akamaiのエッジサーバからオリジンサーバへのアクセスがエラーになった問題を解決した
  • 原因はAkamaiが信頼しているルート認証局がチェーンに入っていない証明書をオリジンサーバで使ったこと
  • 今回のように証明書の更新でルート認証局が変わる可能性を考慮すると,Akamaiでの信頼するルート認証局の設定はAkamaiが管理するルート認証局セットを使う方法がベターかなと思う

HashiCorpの日本語Vaultハンズオンを実施した!

業務でHashiCorpのVaultを使うかもしれないのでハンズオンをやってみた.GitHubに日本語のハンズオン資料が公開されているのでそれをもとに進めた.

github.com

ハンズオンアジェンダ

GitHubで公開されている日本語のハンズオンアジェンダを以下に示す.この中にはハンズオンとしての内容がまだ公開されていないものも含まれている.

  • 初めてのVault
  • Secret Engine 1: Key Value
  • Secret Engine 2: Databases
  • 認証とポリシー
  • Auth Method 1: LDAP
  • Auth Method 2: AppRole
  • Auth Method 3: OIDC
  • Auth Method 4: GitHub
  • Response Rapping
  • Secret Engine 3: Public Cloud (AWS, Azure, GCP)
  • Secret Engine 4: PKI Engine
  • Secret Engine 5: Transit (Encryption as a Service)
  • Secret Engine 6: SSH
  • 運用系機能色々
  • CIツール連携(Concourse, Jenkins)
  • Kubernetes連携
  • Cloud Foundry連携
  • Enterprise機能の紹介

今回は初めてVaultに触るので初めてのVaultSecret Engine 1: Key ValueSecret Engine 2: Databases認証とポリシーの4つをやってみる.具体的な手順はハンズオンに任せるとして,この記事ではそれぞれのハンズオンで学んだことと,自分が思う重要な概念や押さえるべきポイントについてまとめてみようと思う.

1. 初めてのVault

ここではVaultをインストールしてVaultを立ち上げ,あとのハンズオンの準備をしていく.

このハンズオンで学んだこと

  • Vaultのインストール
  • Vaultサーバの立ち上げ(開発モード)
  • シークレットエンジン
    • シークレットの保存・取得
  • Vaultサーバの立ち上げ(本番モード)
    • Vaultの初期化処理

シークレットエンジン

シークレットエンジンとはkey=valueでデータを格納・生成・暗号化するVaultのコンポーネントである. AWSのIAMのキーやDatabaseへアクセスするシークレットなどさまざまな用途で使えるように,Vaultで専用のコンポーネントが用意されている.例えばkvシークレットエンジンを新規で使う時はvault secrets enable -path=kv kvのようにシークレットエンジンを有効にする.さらに,パスを指定してエンドポイントにみたてて,以後そのエンドポイントに操作を実行するのが特徴である.用途にあったシークレットエンジンを使うことでシークレットを適切に扱うことが可能となる.

Vaultの初期化処理

本番モードのVaultではセキュアな設計がされているので,起動直後はsealedという状態になっておりVaultへログインできない.ログインするにはinitunsealという初期化処理が必要になる.まずinit処理をする.

$ vault operator init
Unseal Key 1: JLYUBHrdwWu2dxwjCazqsCQ4OJPJtiMFsIZeO1osyJ1t
Unseal Key 2: nEjGt+rYSOomqmyTsuF7PnKPS+NE3yPEfuo6WDXm/QDR
Unseal Key 3: d5nTCzEGKIBPFGCC3ANzKf8gGgwoV8APr6V9KdDcNjOW
Unseal Key 4: sd7vzV1FQk/96xQmIuKRKhydy9tGEmORbFyozAKxFc4n
Unseal Key 5: JqLGxcvA3gLrwQHIliWvl1ytkMbuDGu/6p2KzGpvnCa9

Initial Root Token: s.Wi5WjPfPHqbFAcXjwsDCHQLd
~~~

initの処理をすると,VaultをunsealするためのUnseal KeyInitial Root Tokenが生成される.試しにこの状態でRoot Tokenを使ってログインしてみる.

$ vault login
Token (will be hidden):
Error authenticating: error looking up token: Error making API request.

URL: GET http://127.0.0.1:8200/v1/auth/token/lookup-self
Code: 503. Errors:

* error performing token check: Vault is sealed

エラーになる.Vaultではsealed状態になっているといかに強力な権限のあるトークンを使ったとしても操作は受け付けない.unsealの処理はUnseal Keyを使う.デフォルトだと5つのキーが生成され,そのうち3つのキーが集まるとunsealされる.このアルゴリズムシャミアの秘密鍵分散法と呼ばれる.3つのUnseal Keyを入れてみる.

$ vault operator unseal
Unseal Key (will be hidden):
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    1/3
Unseal Nonce       32d20912-88dd-16e2-28d3-87344abec5fc
Version            1.2.3
HA Enabled         false
$ vault operator unseal
Unseal Key (will be hidden):
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    2/3
Unseal Nonce       32d20912-88dd-16e2-28d3-87344abec5fc
Version            1.2.3
HA Enabled         false
$ vault operator unseal
Unseal Key (will be hidden):
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.2.3
Cluster Name    vault-cluster-7815e4aa
Cluster ID      12747d60-78d3-8fad-6082-102052ac8c74
HA Enabled      false

これでログインができる.

$ vault login
s.Wi5WjPfPHqbFAcXjwsDCHQLdToken (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                s.Wi5WjPfPHqbFAcXjwsDCHQLd
token_accessor       NX8h0laChNLO5oObu2nDtovT
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

やや面倒なこの仕組みの何が嬉しいのかということについては以下のブログが参考になった.

christina04.hatenablog.com

2. Secret Engine 1: Key Value

ここではシンプルなKeyValueStore型のシークレットエンジンを使ってデータのCRUDをするハンズオンを行う.

このハンズオンで学んだこと

  • Key Value Store型のシークレットエンジン
    • データのCRUD
    • データのバージョニング管理
    • 2つのデータ更新パターン

3. Secret Engine2: Databases

ここではMySQLとVaultを使い,Vaultで発行したシークレットを用いてMySQLにアクセスするハンズオンを行う.

このハンズオンで学んだこと

  • Databaseのシークレットエンジン
  • Vaultが対応しているDatabaseシークレットエンジン一覧
  • VaultがMySQLの操作を制限するようなロールを作成し,そのロールの内容に基づいてVaultがアプリケーションへシークレットを生成する流れ
  • 動的シークレットの破棄
  • Rootユーザのパスワードローテーション

MySQLへのアクセスの流れ

以下の図のような流れでアプリケーションはMySQLへのアクセスを実現する.

f:id:a-mochan:20191117084705p:plain
MySQLへのアクセスの流れ

それぞれのステップをみていく.

① Vaultに特権ユーザのクレデンシャルとデータベースの接続先を登録する.MySQLrootユーザ(特権ユーザ)を指定してコネクションの設定をする.②で作成するロールもここで指定しておく必要がある.また,ロールは複数指定できる.

$ vault write database/config/mysql-handson-db \
  plugin_name=mysql-legacy-database-plugin \
  connection_url="{{username}}:{{password}}@tcp(127.0.0.1:3306)/" \
  allowed_roles="role-handson" \
  username="root" \
  password="rooooot"

MySQLへの権限やシークレットのTTLを記述したロールを定義する

$ vault write database/roles/role-handson \
  db_name=mysql-handson-db \
  creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';" \
  default_ttl="1h" \
  max_ttl="24h"

③ クライアントからVaultに対してシークレットの発行を依頼する

$ vault read database/creds/role-handson
Key                Value
---                -----
lease_id           database/creds/role-handson/G3tJu3gN8nGoL8Z8MlVkvD2g
lease_duration     1h
lease_renewable    true
password           A1a-qIpUomJUoXOiHlQh
username           v-role-PaKXo1BO1

④ 取得したシークレットでMySQLにアクセス

$ mysql -u v-role-PaKXo1BO1 -p -h 127.0.0.1
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 5.7.22 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

$ mysql>

⑤ ロールによりSelectはできるがInsertはできないことを確認する.

$ mysql> select * from products;
+------+-------------+-------+
| id   | name        | price |
+------+-------------+-------+
|    1 | Nice hoodie | 1580  |
+------+-------------+-------+
1 row in set (0.00 sec)

$ mysql> insert into products (id, name, price) values (2, "hoge", 1600);
ERROR 1142 (42000): INSERT command denied to user 'v-role-PaKXo1BO1'@'127.0.0.1' for table 'products'

動的シークレットの破棄

ロールにシークレットのTTLを設定できる.TTLを過ぎれば当該シークレットではMySQLにログインできなくなる.またrevoke処理で発行したシークレットを明示的に無効にできる.再びMySQLにログインしたい場合は,再度シークレットを発行するか,シークレットのTTLが切れる前にrenew処理と呼ばれるTTLを延長する処理のどちらかを実行すればよい.

Rootユーザのパスワードローテーション

VaultにMySQLの特権を持たせているためそのパスワードの扱いが非常にセンシティブである.Vaultにはコンフィグレーションとして登録したデータベースのパスワードをローテーションさせるAPIがある.これを使ってこまめにRootのパスワードをリフレッシュできる.ただし,MySQLのRootユーザがVaultに登録してあるもの1つだけだと,Rootユーザのパスワードのローテーションを行った後はRootのパスワードはVaultしか扱うことができない.そのため通常別の特権ユーザをMySQLに準備してから行う

4. 認証とポリシー

ここではエンドポイントごとにポリシーを設定してトークンを発行し,アクセスコントロールを試してみるハンズオンを行う.

このハンズオンで学んだこと.

  • ポリシーの利用方法

ロールとポリシーの違い

ポリシーの作成方法や使い方はハンズオンで十分学ぶことができる.個人的に思うここでの重要なポイントは1つ前のSecret Engine2: Databasesで学んだロールと今回のポリシーの違いだと思う.利用するアプリケーションのアクセスをコントロールをしているという点においては同じだが,ロールとポリシーではコントロールする対象領域が異なる.1つ前のハンズオンを例にとると,ロールの場合コントロールする対象領域はMySQLだった.つまり,MySQLへアクセスするシークレットが発行された上で,そのシークレットでMySQLにログインするアプリケーションにどんな制限を持たせるかというのを定義するのがロールである.一方,ポリシーはVaultのエンドポイントを対象領域としている.例えば,「kvタイプのとあるエンドポイントにはread,writeを持たせるけど,databaseタイプのエンドポイントにはアクセスさせない」というようなポリシーを定義をする.もしこの例が適用されたトークンが発行されると,たとえロールの設定にすべてのテーブルへのアクセス許可を書いていたとしても,そもそもdatabaseタイプのエンドポイントにアクセスができないのでシークレットが発行されず,MySQLにログインすることすらできない.まとめると,ポリシーはエンドポイント単位でのアクセスコントロールを制御し,ロールはエンドポイントに対応しているSecret Engineの中でもっと細かいアクセスコントロールを制御するものだと思う.

まとめ

  • HashiCorpのVaultハンズオンを一部試してみた
  • Vaultの日本語ハンズオンはとても分かりやすかった.Vaultが何か分からない方におすすめ!
  • 認証を絡めたハンズオンもいくつかあるのでやっていこうと思う.また,この記事を書いている間にもハンズオンが変更されたり増えていたりしたのでまた試してみようと思う.