LinuxユーザーがイジるはじめてのAzure

LinuxユーザーがAzureを使いこなせるように応援するブログです

ドメインの異なるアカウント同士で vNET間を結ぶ

長くエンタープライズ顧客を相手にしていると単純なLift&Shift(オンプレからの移行)もあるけど、サブスクリプション間で移行したいといった要件も出てきますね。慣れている人は「Azure ADテナントが違って」とか「サブスクリプション移行をしたくて」といった言葉が出てきただけで、変な汗が出てくること間違いないw(私だけ?汗)

 

それもそのはずで、契約形態や移行したいシステムリソースによって対応方法もいくつかあり、簡単なものから複雑なもの、リスク込みのものや金かけてでもダウンタイム最小限にしたいものまであるから、といった実情があるからだろう。

 

例えばこんなパターンが考えられる。

1)EA契約のサブスクリプションからCSP契約のサブスクリプションへ移行したい

2)EA契約同士、もしくはCSP契約同士でサブスクリプションを移行したい

3)無償利用で作ったものをEA契約もしくはCSP契約へ移行したい

4)同じ契約内で異なるサブスクリプションへ移行したい

 

1~3はそもそもAzure AD テナントが変わる前提で、4)だけ変わらない。あと2のEA契約同士というのは、本社とグループ会社で、というのが多いが、CSP間で、とかはそもそもビジネスの主管を変えるって話になるので技術の前に話し合うべきことが多いことも押さえておこう。まぁ平たく言えば、hogehoge.comで管理されている環境で作ったシステムリソースをfugafuga.comで管理されているシステムリソースへ移行したいのか、hogehoge.comで管理されている環境は変えずに別のサブスクリプションへ移行したいのかって違いがあるということ。

 

なーんだ、システムリソースはあるんだから契約情報だけ差し替えてくれればオッケーっす!マイクロソフトさんよろ!なーんて考えていると甘い。マイクロソフトはセキュリティの会社と言われているぐらいなので、締結された契約をそんな安易に企業間の都合だけで対応できるようになんて仕組みにしていない。たぶんw

 

とはいえ技術的な側面ではしっかりとお互いの認証を行い双方での疎通が取れる状態にすることはできるので、今回はその1つである「異なるAzure ADテナントの異なるサブスクリプション間のvNET同士をピアリングし、プライベート間で通信できるようにする」ということを試した結果を記事にしておく。

 

やりたいことは簡単。koiketmp1@outlook.jp(マイクロソフトアカウント)のVMからkoiketmp1@outlook.com(マイクロソフトアカウント)のVMへプライベート接続でデータを送りたい。つまりoutlook.jpとoutlook.comの仮想ネットワーク(vNET)間をプライベートピアリングして接続したい、ということ。

※社員用サブスクは制限かかっているようなのでできまへん。ちーん。

 

ちなみに以下のオフィシャルサイトを参考にしてほしい。

https://docs.microsoft.com/ja-jp/azure/virtual-network/create-peering-different-subscriptions 

なんだか小難しい手順が書かれているが、簡単に言うと以下となる。

 

1)Azure ADで「ゲストユーザー招待」を完了させておけ

2)お互いのvNETで相手側のアカウントが操作できるよう

  「ネットワーク共同作成者」をRBAC(IAM)で相互で設定しておけ

3)ピアリング設定をする際は「リソースID指定」で認証してやれ

 

ということでやってみよう。

 

まず「outlook.jp」側のリソースがこれ。

f:id:akazure:20200615071738j:plain

「cent-vnet」という名前のvNETに「cent0001」という仮想マシンが 172.16.0.4 というプライベート用のNiCが付いた状態でいる。

 

もう片方は「outlook.com」側のリソース。

f:id:akazure:20200615081704j:plain

「cent-vnet2」という名前のvNETに「cent0003」という仮想マシンが 192.168.0.4 というプライベート用のNiCが付いた状態でいる。

 

とりあえずAzure Portalの左ペインからAzure Active Directoryを選択。

f:id:akazure:20200615082022j:plain

 

「ユーザー」から「+新しいゲストユーザー」を選択し、以下メールアドレスを入力して「招待する」を押す。

 

f:id:akazure:20200615082327j:plain

 

 

しばらくすると上で入力されたメールアドレスへ以下のようなゲストユーザー招待のメールが届く。

 

f:id:akazure:20200615082708j:plain

 

メール内にある「招待の承諾」を押すことブラウザから認証が走るので、招待された側は自分(outlook.jp側)のアカウントとパスワードを入力して招待を受ける。

 

この作業はoutlook.jp側からoutlook.com側への招待も同じく実施する。つまりそれぞれ1回ずつ招待し計2回やる。

 

反映に少し時間がかかるかもしれないが、以下のような画面がAzure Active Directoryのユーザー一覧に出力されることを確認しよう。

 

f:id:akazure:20200615083124j:plain

※これはoutlook.com側のアカウントでログインした時の画面。koiketmp1@outlook.jpのアカウントがゲストユーザーとして反映されていることが確認できる。

 

これでとりあえずAzure AD側では双方のアカウントが認識された状態となる。次はそれぞれのvNET(cent-vnetとcent-vnet2)にて、RBAC(IAM)を操作し、お互い「ネットワーク共同作成者」のロールを付与する。つまりお互いがお互いのvNETに対して作成や更新ができる状態にしよう。

 

outlook.jpとoutlook.com両方のvNETに、以下のようにアクセス制御(IAM)の設定を入れて保存する。

f:id:akazure:20200615084606j:plain

保存が正常に終わると「アクセス制御(IAM)」の「ロールの割り当て」画面に、指定したアカウントとロールが一覧に表示されるので確認しておこう。ちなみにRBAC(あーるばっく)とはRole-Based Access Contolの略で、この「アクセス制御(IAM)」を指します。

 

双方(outlook.jpとoutlook.comの両方)でのRBACの設定が終われば最後はピアリング。

 

ここまでで、以下の1)と2)が終わった状態。

1)Azure ADで「ゲストユーザー招待」を完了させておけ

2)お互いのvNETで相手側のアカウントが操作できるよう

  「ネットワーク共同作成者」をRBAC(IAM)で相互で設定しておけ

3)ピアリング設定をする際は「リソースID指定」で認証してやれ

 

最後に3)を行うが、先に許可を許す側のvNETの「プロパティ」を選択し、「リソースID」をコピっておこう。

f:id:akazure:20200615084151j:plain

※2画面で別々のアカウントでログインしてやっていると、サインアウトしないといけない部分などがタイミングによってあるので、ここからは1画面でやるのがよい。

 

お互いのリソースIDをメモ帳などにコピーしたら、vNETの「ピアリング」から「追加」を選ぶ。

f:id:akazure:20200615090043j:plain

 

Peeringの設定ではピアリングの名前は適当(任意)でOK。リソースIDを知っている、というチェックボックスにチェックを入れる。

f:id:akazure:20200615090326j:plain

先ほどメモ帳でコピーしたリソースIDを、べたっと張り付けるとディレクトリがプルダウンで選択できるようになるため、該当のっディレクトリを選択して「認証」を押す。

上の画面ではoutlook.jp側のアカウントでログインしoutlook.jp側のvNET(cent-vnet)のピアリングを追加する画面。なのでoutlook.com側のリソースIDとディレクトリを指定してあげて認証する。認証が終われば1番下のOKボタンを押す。

 

以下のように設定したピアリング項目がvNETのピアリング画面に表示されればOK。

f:id:akazure:20200615090746j:plain

※ピアリング状態が「開始済み」なので、まだ片方向でしか設定が完了していないことを示す。双方向でピアリングが確立していると「接続済み」というステータスになるため、現時点ではまだvNET Peeringは終わっていない。

 

ということで今度はもう片方側でログインして同じようにピアリングの設定を行おう。

f:id:akazure:20200615091450j:plain

 

こんな感じで「接続済み」ステータスになっていれば万事OK!!!

 

ちなみにこんなエラーが出てピアリングがうまく行かない時がある。

f:id:akazure:20200615100458j:plain

 

この場合はピアリング先のアカウントがログイン状態になっている、とか、Azure AD側の反映が遅れている、などの原因が考えられるため、1日おいて片方のアカウントだけでログインしてもう一度設定することをお薦めする。

 

では最後に仮想マシン間でチェックしよう。

f:id:akazure:20200615092449j:plain

f:id:akazure:20200615092503j:plain

 

outlook.jp側もoutlook.com側も、どちらのVMからも双方向でpingチェックの通信が通ることが確認できた。ってことでファイルをコピーしてみよう。

 

172.16.0.4 のVMから192.168.0.4 のVMへ scp でファイルコピー。

f:id:akazure:20200615095856j:plain

f:id:akazure:20200615095808j:plain

 

すばらしい。

 

ってことで、異なるアカウント同士で異なるネットワークセグメント間のVM同士を仮想ネットワーク単位でピアリングすることができました。VMイメージのvhdをエクスポートしてインポートしたり、VMを新規作成してデータだけblobへ配置して吸い上げる、Azure Filesで双方でマウントする、などなどいろんな移行方法はあるけど、サーバー間を繋げることでプライベート網を使った接続が簡単にできるので、今後の移行方法の1つとして考えてもらえればと。

 

ストレージ診断ログのblobアクセスログを解析する

あー、ついに禁断の地へw

 

パブリッククラウド全般に言える共通点ですが、データが外からクラウド

入ってくる分には無料だけど、クラウド内の同一リージョンもしくは同一

データセンターから外に行く分(主にダウンロードやリスト表示等)には

課金される、という法則は一般的ですね。

※ベンダーから見ると簡単に無料でデータが他社に引っ越されてしまうのは

 いかんぜよ、という昔からの戦略というか怖い意志がありますからねw

 

もちろんAzureでも同様なのですが、よくある話としては以下のような

ケースです。EAポータル等から課金レポート(csv)をダウンロードして

見たり、Azure Cost Managementを使ってリソースタグの集計結果グラフ

などを見ていると、ある日を境に突然blobのデータ転送料がドエライこと

になっててビビる!というものです。特にこれは複数の部門やチームに

跨ってAzureをご利用頂いている時に起きますかね。

 

そしてエンタープライズオーナーやサブスクリプションオーナーが

ぎゃあぁ!なんだこれ!今月分くっそ高いぞ!てか誰だよこんな使い方

してるやつはぁ!っと目くじら立て原因の特定に入っていきます。

 

さて、ぼちぼち本題にはいっていきますが、もちろんストレージ

アカウントのストレージ診断をONにしていないと細かい原因がわかり

ません。以下Azure Portalの設定個所です。オンにしておきましょう。

 

f:id:akazure:20200430081503j:plain

 

ストレージ診断をONにしておくと、このストレージアカウント

に自動的に$logs というコンテナが作成されます。先頭が$で始

まっているので、隠しディレクトリという扱いのようです。

 

この $logs 配下の階層は、/YYYY/MM/DD/HHmm/000000.log の

ようになっていますが、アクセスがないと何も作成されません。

※mmの部分は固定で00になるので「分」までは厳密なディレ

クトリ管理はしていません。

※000000.log は時間をおいてアクセスすると000001.log のように

 インクリメントされたファイル名として複数に分けられます。

 

この000000.logファイルのフォーマットは以下に記載があります。

https://docs.microsoft.com/ja-jp/rest/api/storageservices/storage-analytics-log-format

 

つまりこの000000.logのテキストファイル(アクセスログ)を

解析すれば何か原因がつかめそうですが、いかんせん、とても、

や、、ややこしいw

 

ということでLinuxな人ならではの解析方法をいくつか紹介しておきます。

とりあえずblobfuseでblob/$logsをマウントし、コマンドが実行できる

状態にしましょう。

 

以下を参考にマウントします。(rpmでパッケージをダウンロードする

際のOSのバージョンに注意してください)

https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-how-to-mount-container-linux

 

※注意

 CentOS7でこの手順を行うと yum install -y blobfuse を実行した時に

 以下のエラーが出る場合があります。

 

 ---> Package fuse-libs.x86_64 0:2.9.2-11.el7 will be installed
--> Finished Dependency Resolution
Error: Package: blobfuse-1.0.2-1.x86_64 (packages-microsoft-com-prod)
           Requires: libgnutls.so.26(GNUTLS_1_4)(64bit)
Error: Package: blobfuse-1.0.2-1.x86_64 (packages-microsoft-com-prod)
           Requires: libgnutls.so.26(GNUTLS_2_10)(64bit)
Error: Package: blobfuse-1.0.2-1.x86_64 (packages-microsoft-com-prod)
           Requires: libgnutls.so.26()(64bit)
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest

 

 この場合は libgnutls.so.26 という共有オブジェクトがねぇ!って

 言われてますが、yumのキャッシュが邪魔している時があるので

 yum upgradeした後なんかは以下の手順でクリアにするとblobfuseが

 インストールできます。

 

# yum remove packages-microsoft-prod-1.0-1.el7.noarch
# yum clean all
# rm -rf /var/cache/yum
# rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
# yum install blobfuse fuse -y

 

 あとストレージアカウントの設定ファイルとして用意する

 fuse_connection.cfg のコンテナ名は、普通に $logs と入れます。

 こんな感じで。(ストレージアカウントのキーはマスクしてます)

f:id:akazure:20200430091844j:plain

 

無事 $logs をマウントできれば

以下のように操作できる状態になっていると思います。

f:id:akazure:20200430092047j:plain

 

ここまでで取り合えず解析する準備ができました。いよいよ本ちゃんです。

なにやらデリミタ(区切り文字)は「;」セミコロンになっているようです。

 

●blobへのアクセスが集中している時間帯を特定しよう

# cd /${mount_point}/${blobdir}/blob/YYYY/MM

# awk -F";" '{ print $2 }' ./*/*/*.log | cut -f1 -d":" | sort | uniq -c | sort -nr

 出力結果サンプル

  167 2020-04-29T21
     42 2020-04-29T23
     1 2020-04-29T03

 ⇒ 4/29の21時が1番アクセスが多い時間だとわかります。

   4/29の21時はUTCです。+9時間のJSTに読み替えましょう。

   あと診断ログの日付フォーマットはISO8601になってます。

   わかりやすい日付フォーマットにする場合は、python

   phpでやったほうが楽かと。

 

●4/29 21時のログから何のリクエストが多いのかを特定しよう

# cd /${mount_point}/${blobdir}/blob/YYYY/MM/DD/2100

# awk -F";" '{ print $3,$4 }' ./*.log | sort | uniq -c | sort -nr

 出力結果サンプル

     89 ListBlobs Success
     46 GetBlobProperties BlobNotFound
     14 GetBlobProperties Success
      6 BlobPreflightRequest AnonymousSuccess
      3 GetBlob Success
      2 ListBlobs SASSuccess
      2 GetBlobProperties SASSuccess
      1 ListContainers Success
      1 ListBlobs OAuthAuthorizationError
      1 GetContainerServiceMetadata Success
      1 GetContainerProperties Success
      1 GetBlobServiceProperties OAuthAuthorizationError

 ⇒ ListBlobsの成功(Success)が 89 回あったことがわかります。

 

●どのURL(blob object)へのアクセスが多いかを特定しよう

# awk -F"\"|&" '{ print $2 }' ./*.log | sort | uniq -c | sort -nr

 出力サンプル

    170 https://share001.blob.core.windows.net:443/$logs?comp=list
     13 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2100/000002.log
      9 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2100/000003.log
      9 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2100/000001.log
      9 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2100/000000.log
      8 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2100/.hoge.swp
      6 https://share001.blob.core.windows.net:443/url?restype=container
      6 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/30
      4 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2300/000001.log
      4 https://share001.blob.core.windows.net:443/$logs/blob/2020/04/29/2300/000000.log

 ⇒ $logs のリスト表示が多いのがわかるが、今操作しているblobfuseの

   ログも出力されるので、grep -v "\$logs" を追加すると良いかも。

   あとURL抽出だとクエリ内で使われる「&」が大量に出るため

   awkのデリミタには「&」も追加して検索。

 

●どこのソースIPからのアクセスが多いかを特定しよう

# awk 'BEGIN {FS="\"";OFS="\""};{gsub(";", "",$2);print}' ./*.log | awk -F";" '{ print $16 }' | cut -f1 -d":" | sort | uniq -c | sort -nr

 出力サンプル

  266 172.19.0.4
    12 106.73.2.64
      3 10.81.128.71

 ⇒ 172.19.0.4 からのアクセスが266回と1番多いことがわかる。

   いきなりawk文がややこしくなったが、デリミタとしていた「;」

   がダブルクォート内のURLにも利用されるためこのようにした。

 

●特定の時間にGetBlobされたデータ容量の合計を出そう

grep "2020-04-30T00:" *.log | grep ";GetBlob;" | awk 'BEGIN {FS="\"";OFS="\""};{gsub(";", "",$2);print}' | awk -F";" '{ sum += $21 } END { print sum }'

 出力サンプル

345161

 ⇒ 2020年4月30日の09時(JST)に、約345kbyteのデータ量が

   GetBlobのリクエストで取得された総データ量。

 

●おまけ:利用しているblobのAPIバージョンを全て特定する

 # awk 'BEGIN {FS="\"";OFS="\""};{gsub(";", "",$2);print}' ./*.log | awk -F";" '{ print $17 }' | sort | uniq -c | sort -nr

  出力サンプル

  266 2017-04-17
      6 2019-10-10
      6 2015-02-21
      3 2019-07-07

  ⇒ 2017-04-17のAPIバージョンが1番利用されていることがわかる。

 

とまぁsedでやると正規表現コテコテになるのでawkでやってみたけど

awkでも結構難解になりましたね(汗

 

もちろん診断ログはLog Analyticsへ飛ばすこともできるので

お金かけてkustoで検索した方が楽だと言う方はそっちをお薦めします。

 

今回はこんなところで。

Azure DB for PostgreSQL の バックアップ・リストアを試す

タイトルにあることを考えると、大抵以下のサイトに行きつくと思う。

https://docs.microsoft.com/ja-jp/azure/postgresql/concepts-backup

 

が、なんだかよく読んでいると???になってくることもw

 

フルバックアップは毎週で差分バックアップは1日に2回行ってて、でもリストアする時は日時だけじゃなく時分秒まで設定を入れてポイントインタイムリストアできると。。はてww 復元されたデータのタイミングはどこの静止点になるんだ?と。

 

オンプレでは主にpg_dumpやpg_dumpallをcronに登録しておいて、決まった日時や曜日、1日に1回、などなどデータベースのダンプバックアップファイルを別のNASへ配置しておき、何か想定外のことが起きればNASにあるdumpファイルから空のPostgreSQLの環境へスキーマ毎復元、みたいなことをやると思います。

 

が、Azure DB for PostgreSQLでは、「決めらている静止点」での復元になるのか、「選択可能な静止点」での復元なのかが、モヤっとしますね。

 

ということで試しましょ。

 

 まずはDB for PostgreSQLをデプロイ。

f:id:akazure:20200323151202j:plain

※USリージョン使っているのに特に意味はないです。

 

つづいてバックアップ設定。

f:id:akazure:20200323151405j:plain

※デフォではバックアップの保有期間が7日になっていたので、14日に変更。

※今回は同一リージョン内での復元をするのでローカル冗長。

 

接続元は特定のVMからにしたいのでDB for PostgreSQLに付いているファイアウォール機能を使い、SSL接続は無効に。(手っ取り早く疎通取りたかっただけ)

f:id:akazure:20200323151600j:plain

 

で、とりあえずDB側の設定は終わりで、続いてクライアント側からは定期的にデータをぶち込むスクリプトを用意。

※パスワードは環境変数でPGPASSWORDに入れてある。

デバッグ用のechoは無視してOK)

 

#!/bin/sh

COUNT=1
while :
do
        DATENOW=`date`
        WORD="insert into test values (${COUNT}, '${DATENOW}');"
        psql --host=postgresqltest001.postgres.database.azure.com --port=5432 --username=azure01@postgresqltest001 --dbname=postgres -c "${WORD}"
        echo "${COUNT} , ${DATENOW}"
        echo "${WORD}"
        COUNT=`expr ${COUNT} + 1`
sleep 300
done

 

ちなみに事前にpostgreSQL 11はインストールしてあるのでpsqlのパスは通ってる前提。

yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

 

yum -y install postgresql11-server postgresql11-contrib

 

で、DB for PostgreSQLへ接続して放置してあったデータの一覧を見るとこんな感じで見える。

f:id:akazure:20200323152409j:plain

※最初の1,2,1,2,3は何度かリランした残骸なので無視してw

 

こんな感じのデータが約8日間、ずっと5分毎にinsertされまくってる状態。

ということで、2020年3月20日の15:00の時点で復元を試みてみよう。

 

リストアは簡単。postgreSQLのOverviewにある上段の「復元」タブを押して日付と復元した後の新しいpostgresサーバー名を入力するだけ。

f:id:akazure:20200323154229j:plain

 

復元が終わるとこんな感じでDB for PostgreSQLのリソース2つ目ができます。

f:id:akazure:20200323155132j:plain

 

おしおし、ってことで早速DBに登録されている最新レコードが何時何分のものなのか見たくなるのですが、復元した直後の状態はファイアウォール設定がコピーされない状態で復元されるので、再度設定してあげます。

不便じゃーって思う人も多いかもしれませんが、仮にSQLインジェクションが発見されて落ちてしまったDB for PostgreSQLを復元させると、復元と共にまた脆弱性をつつかれる、みたいなことにならないようネットワークレイヤの設定とDB設定は別にされているのかもしれませんね。

 

f:id:akazure:20200323161136j:plain

 

おおう、すばらしい。直前まで書き込んでいたデータがちゃんと反映されてますね。あ、ちなみにpostgres側のユーザーやパスワードはリセットされませんので、そのまま今までのものが引き継がれます。

 

いや、でもたまたま15:00にバックアップ取られていただけでタイミングよかったからじゃね?という小悪魔な自分が囁き始めたので、今度は3月18日の16:50での復元を試してみました。結果は以下。

f:id:akazure:20200323170735j:plain

 

すんばらしい、よくできている。

てことで、バックアップは設定した直後からバックアップはされますが、保持期間の間であれば任意な時間で復元が可能ということがわかります。 

Azure Firewall の Inbound 制御について

Azure Firewall をアウトバウンド制御するFWとして利用する記事は割と多めですが、インバウンドだって制御しまっせ、という記事を少し記載しておきますかね。

 

Azure Firewall はvNET(仮想ネットワーク)に所属させますので、デプロイするとグローバルアドレスとプライベートアドレスの代表をそれぞれ1つずつ持ちます。

 

f:id:akazure:20200323111157j:plain

 

今回の構成としてはAzure Firewallを上段に、下段にはILBとしてApplication Gateway(AppGW) を構え、AppGW の下段にVMを配置してあります。VMはnginxのページが表示されるよう80 port/tcpでlistenしてある状態です。

 

ではAzure Firewall のルールを見てみます。

f:id:akazure:20200323112310j:plain

 

ルールでは「NATルールコレクション」「ネットワークルールコレクション」「アプリケーションルールコレクション」とありますが、ここではNATルールコレクションを選択します。

 

f:id:akazure:20200323112517j:plain

このような設定をしています。

1つ目のルール名natruleSGTは、オンプレから外に出る際に利用されるグローバルアドレス(167.220.233.219)をソースIPとする80port/tcpのリクエストは、Azure Firewallのグローバルアドレス(52.156.53.53)からAppGWのプライベートアドレス(172.19.3.254)へ変換して通信を促せ、というルールです。いわゆるグローバルアドレスをプライベートアドレスと通信させたいDNATとしてよく利用される機能ですね。

 

2つ目のルール名natruleOtherは、上記以外をソースIPとする80port/tcpの通信は、Azure Firewallのグローバルアドレス(52.156.53.53)から、別vNETに配置したVMのPIP、グローバルアドレス(104.41.185.108)へ通信を促せ、というルールです。

 

f:id:akazure:20200323113253j:plain

Public to Public へNATした場合は上記のnginxのページが表示されていますね。

f:id:akazure:20200323114119j:plain

一応VM側から見たソースIPを確認するとAzure Firewallのグローバルアドレスとして持つ「52.156.53.53」から来ていると見えます。

 

一方AppGWのILBへDNATした場合は、こんな感じで見えます。

f:id:akazure:20200323115254j:plain

VM側から見るとILBとして立てたAppGWから来ていると見えます。

f:id:akazure:20200323115316j:plain

 

といった感じで、外部から来たリクエストをAzure Firewallから内部、外部へ促すことができます。(IPv4レベルですが)

 

 

また、以下のようにネットワークルールコレクションで設定すると、特定の外部ソースIPから来たリクエストは拒否する、といったこともできます。

f:id:akazure:20200323120419p:plain

 

以上、Azure Firewallでインバウンド制御する場合の内容でした。

Azure Linux VMでのsudo脆弱性対応(CVE-2019-14287)

なんだかsudoに脆弱性が発覚し騒がれつつありますね。

 

それもそのはず、CVSSのスコアが「7.0」と警告レベルを超えて

重要だぜ、というスコアになっているのと、一部分だけを読むと

rootユーザー権限が乗っ取られる、と解釈できる部分があるから

でしょうね。

参考:https://access.redhat.com/security/cve/cve-2019-14287

 

いや、そもそもCVSSってなんぞ?スコアって?という方は

以下を参考に。

https://www.ipa.go.jp/security/vuln/CVSS.html

 

まぁいわゆる脆弱性レベルって一概にいっても様々な軸と捉え方が

あるわけで、そこを包括的にオープンな情報として各機関が軸や

考え方をまとめたスコアですよ、ということ。つまりこのスコアを

本人や企業ががどう扱おうと自由なわけですが、エンジニアとしては

今やるべきなの?後回しでもいいの?スルーしていい?というモノサシ

がほしいですよね。

 

そういう意味では今回のは重要レベルのスコアなので踊らされてしまう

のも無理はないですが、ちゃんと中身を読むとsudoをuser毎に厳密に

権限管理されている場合、root権限を与えていないuserでもroot権限で

実行できてしまうセキュリティホールが見つかったよ、というもの。

 

言い方を変えるとsudo = root という扱いでしか利用されていない

Linux環境であれば何の問題もないわけです。具体的にはこんな感じ。

 

通常利用

$ sudo -u#${uid} command

 ⇒ uidを使ってcommandを実行

 

問題となった利用方法

$sudo -u#-1 command

もしくは

$sudo -u#4294967295 command

 ⇒ -1も4294967295も存在しないuidだが、バグで0と解釈されて

   しまい、かつ存在しないuidのためパスワードデータベース

   への参照もされずPAMも制御外となる、そしてuid=0はroot

   のuidのためroot実行されちゃう、というw

 

で、じゃあどうすんだよ、という話に移る。

今回問題があるsudoのバージョンは1.8.28より前のバージョンが該当

するとのこと。

参考:https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-14287

 

で、Azure VM(やっとここでAzureな話w)のRhelCentOSyum upgrade

/update で対応できるかなー、っと思ったけどまだrepositoryに展開されて

いない模様。

 

# yum list sudo
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * epel: ftp.riken.jp
Installed Packages
sudo.x86_64                                        1.8.23-4.el7                                         @base
#

 ※あ、epel-releaseもインストール済でつ。

 

ということでsudo本家の以下サイトから1.8.28をtar.gzでダウンロードしま

しょ。

https://www.sudo.ws/stable.html

 

ソースからの展開なのでパッケージ展開と比べてダルいですがコンパイラ

必要なのでgccあたりはあらかじめyum installしておいてください。

tar.gzを展開するとこんな感じ。

 

f:id:akazure:20191017132019j:plain

 

./configure、make、make installして反映しましょ。

※rootアカウントが利用するsudoはデフォで/bin/sudoになってます。

 このソースからmake installで展開されるのは/usr/bin/sudoのs-linkと

 /usr/local/bin/sudoの実態なので、混乱しないように。

 自分は混乱するので/bin/sudoを/usr/local/bin/sudoの実態を参照する

 ようにS-linkはっておきましたが。

 

で、例の問題となった脆弱性をつつくコマンドを叩いた結果が以下。

f:id:akazure:20191017132330j:plain

 

いいね!ちゃんとエラーとなって解釈できているようでつ。

対応方法と対応した後の確認までの記事があまり見つからなかったので

敢えてこの記事を書きましたが、もう一度言っておくとsudo = rootと

いうシンプルな扱いをされている環境では対応の至急性はないです。

repositoryに反映されたら定期的にyum upgradeかけてるし問題なす!

な話です。

 

ちなみに今回はCentOS(ディストリビューションRHELと同様)で

試しましたが、JPCERT CCが各ディストリビューション毎の記事を

まとめられていたので、ご参考まで。

 

https://www.jpcert.or.jp/newsflash/2019101601.html

 

 

いじょ

特定のログからWebシナリオテストを自動実行する

前回の記事内容の派生形となります。

 

 

実現イメージは以下ですが、前回との変更点は赤文字/赤線の部分です。

 

f:id:akazure:20190327112808j:plain

 

変更点の補足です。

④ Azure Automation Runbook Python2 から、事前に用意しておいた

  Blob ファイルを取得しています。ファイルの中身は以下です。

 

https://www.google.com/
https://www.microsoft.com/ja-jp
https://hogehogekoike.com
https://aws.amazon.com/jp/

 

 ここでは正常にHTTPの応答を返すか否かの対象となるサイトURLを

 1行ずつ記載しています。

 (3行目のhogehogekoike.comは存在しないURL)

 

⑤Blob ファイルに記載されたURLに対して1行ずつHTTPのスポンスを

 取りに行っています。

 

⑥各URLのHTTPレスポンスの結果をメールの本文に入れてメール通知します。

 

ある程度想像できた方も多いと思いますが、24/365の稼働を提供している

自社のWebサービスにおいて、裏側で構成されているWebのシステムから

致命的なログが確認されたとき、多くのエンジニアは監視オペレータから

の呼び出しや緊急連絡網からの確認依頼、障害対処依頼がきますよね。

 

担当エンジニアは原因の究明や一次対処、サービス正常性確認などを

実施されていると思いますが、これをAzure PaaSを使ってすべて自動化

してしまおう、という一例です。営業時間内のオペレーションであれば

特に問題はないのですが、夜間や休日にサービス影響がない監視連絡ほど

出たくない電話はないですからね(汗

 

では本編に入ります。

 

 

前回の記事も試されて、かつ、せっかちな方(笑)は、以下の

pythonコードをAzure Automation Automation Runbook (python2) に

べたっと張り付けましょう。

(標準ライブラリのみで動作するように記載しています)

 

https://github.com/akkoike/sample/blob/master/automationpython.py

※{}の個所のみ、ご自分の環境に合わせた文字列を入れてください。

例:

storage_resource_group = "{RESOURCE_GROUP_NAME}"

⇒ storage_resource_group = "hogehoge-rg"

 

47行目まではAuto Credentialの部分ですので変更不要です。

49行目から68行目まではblob storageの 定義とインスタンス化です。

コメントアウト部分はデバッグ用で残しておきました。

 

69行目から79行目までがURLを1行ずつ持ってきてHTTP(GET)を

発行し、その結果を変数に代入している部分です。

単純なBasic認証ならrequest.post(url,auth=('user','passwd'))と

指定すればいけそうですね。

 

80行目から最後の101行目まではSendGridへメール送信する部分

です。サブミッションポート(587)のTLS通信でメール送信しています。

 

では試してみましょう。

 

まずLog Analytics(Azure Monitor Logs)では、以下のクエリを

発動条件にしています。

 

f:id:akazure:20190327131730j:plain

Syslog
| where Computer == "centlog002"
| where SyslogMessage contains "critical error"

 

Runbookの発動をAzure MonitorのAlert ruleで指定します。

f:id:akazure:20190327131856j:plain

 

閾値は0件「より大きい」(つまり1以上)にしています。

 

では対象VMのcentlog002にて、「critical error」をrsyslogdへ飛ばしてみます。

 

f:id:akazure:20190327150038j:plain

logger -p authpriv.crit "web service critical error , please check"

 

しばらく待つとLog AnalyticsのクエリログにHITし、Azure Monitorの

Alert Ruleに引っかかり、runbook pythonが処理され、以下のような

メールが飛んできます。

f:id:akazure:20190327150305j:plain

 

各URLのレスポンス結果がメール本文に入っていますね。

もちろんすべて200 OKならメールを通知させる必要もないので

その場合はPythonの中身を自由にカスタマイズしてください。

 

最後に、補足です。

Webのシナリオテストを実行する、というだけなら他にもよさげな

ものがいくつかあると思います。以下はご参考まで。

 

1、Azure DevOpsのCI/CD機能を使う

  今回は試していませんが、gitへnull commitするトリガーで

  いくつかのサイトへのHTTPレスポンスを計測する、といったことも

  できると思います。

 

2、Logic Appsを使う

  以下のように組み立ててれば、簡易版のシナリオテストも

  実行できました。

 

f:id:akazure:20190327151014j:plain

f:id:akazure:20190327151027j:plain

 

 ※ただ、確認したいURLが数百あると、1つ1つアクションを追加する

  のも面倒ですし、blobに置いたURLリストをJSON形式に変換する必要

  がありそうだったため、今回はこちらを使いませんでした。

 

3、Azure functionsで実装する

 今回行ったAzure Automation Runbook pythonは、functionsで実装しても

 大差ありません。お好みで選んでよいと思います。

 

4、SeleniumなどのOSSをOS内で実行する

 Seleniumによる自動化のよいところは、細かいブラウザ内の制御を

 自作できる部分です。

 例えば以下のような記載になります。(簡単なサンプルです)

 

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
 
driver = webdriver.Chrome(executable_path='C:\\Users\\akkoike\\AppData\\Local\\Programs\\Python\\Python37-32\\Scripts\\chromedriver.exe')
# chrome browser
driver.get('https://www.google.co.jp/')
# input keyword to textarea
driver.find_element_by_name("q").send_keys("まいくろそふと")
# search return
driver.find_element_by_name("q").send_keys(Keys.ENTER)
# click link by classname
driver.find_element_by_class_name("LC20lb").click()
driver.back()

 

 ※事前にChromeドライバのダウンロードと配置、pipでselenium

  インストール(pythonの場合)しておく必要があります。

  ここではchromeブラウザを起動し、google検索フィールドに

  「まいくろそふと」と入力しリターン。検索ヒットした1つ目の

  リンク先をクリックし、最後に「戻る」を押す、といった簡単な

  ものです。

 

ポップアップ認証サイトや、Basic認証以外の認証を経由した

サイトへのアクセスも含めてシナリオテストへ反映したい、という

場合なんかはSeleniumでやった方が楽な部分も多いですね。

(といっても座標指定など結構、力業な部分が多いですがw)

ただ、PythonユーザーだとMicrosoft Edgeブラウザはドライバが

提供されていませんので注意が必要です。(C#Javaが提供中)

 

今回はこんなところで。

Log Analytics(Azure Monitor Logs) + Azure Monitor Alert Rule + Runbook PowerShell + SendGrid

タイトルで、ある程度何やろーとしているか想像できる方は

きっともうAzureの初心者ではなさそうですね。

そしてやろうとしたけどなんだかうまくいかねぇーって泣きそうな

顔してる方もいらっしゃるかと思うので疎通とった記事をまとめて

おきますかね。

 

今回の記事でやりたいことは以下です。

f:id:akazure:20190308171511j:plain


 

やりたいのは、特定のログを定期的にチェックし、閾値を超えて検知

された場合は、一次処理を行った上でメールで終わったよ通知したい、

というものです。

 

①、ここは純粋にLog Analytics(Azure Monitor Logs)のワークスペースVM

  入っているomsagentからのログを飛ばすだけ。

②、Log Analyticsのワークスペースで検索したクエリ(Kusto)から、特定の

  ログを抽出し、その件数を閾値にアラートルールを作成。

③、アラートルールではAzure Automationアカウントに登録してある

  Powershell Runbookを実行するように指定。

④、Runbook内に登録したPowershellでは、特定のAzure VMのステータスを

  確認。

⑤、VMステータスをメール本文に入れてSendGridへ送信。

⑥、管理者(ここでは私)がメールの通知を受け取ってハッピー。

 

この①~⑥を自動実行(定期実行)したいわけですね。

ということでAzure上で必要なコンポーネントを並べると以下です。

 

● Azure Log Analytics (Azure Monitor Logs)

● Azure Monitor

● Azure Automation

Powershell スクリプト

● SendGrid

 

じゃ早速いってみましょー。

 

今回疎結合された単体テストができるよう進めているので、以下の

2系統で進めます。

 

1系統:Automation powershell runbookの動作(④~⑥)

2系統:Log Analytics (Azure Monitor Logs) とAzure Monitorの設定(①~③)

 

まず最初に準備するのはAzure MarketPlaceにあるSendGridです。

ここは単純にFreeプラン(無償)でアカウント作ってパスワード設定

するだけなので割愛します。

smtp serverと587 submissionポート接続用にユーザー名とパスワード

 はどこかにメモっておいてね。(SMTP認証するんで)

 

次にAzure Automationを用意しておきましょ。Azutomationアカウント名

は任意で作成しておいてください。

 

最初にやるべきは「Azure モジュールの更新」!!

これをやっておかないと、1.2.1といった超絶古いバージョンのpowershell

モジュールを使った処理がAuzre Automation側で実行されるので

1.2.1の記載をしなければ動かないです。また、ローカルPCで動いてる

hogehoge.ps1が5.7.1とかのバージョンで動作していると、動かないん

だけどー?って事態になります。(結構落とし穴w)

 

f:id:akazure:20190308095521j:plain

更新するとこんな感じです。(10分ぐらいで終わります)

最新すぎるだろ!って突っ込みはスルーして進めますw

 

続いてRunbookを開いてPowershellをコネコネしていきます。

f:id:akazure:20190308095804j:plain

Runbookの作成、から、powershellを選びます。

Python2やグラフィックpowershell ワークフローなんかも選べます。

※Python3マダー

 

で、ぶちこむPowerShellはこんな感じ。

powershell/automationpowershell.ps1 at master · akkoike/powershell · GitHub

 

入れる変数の説明だけ少し。

$rg_name = ""
$target_vm_name = ""

   ポンチ絵でいう④の部分です。対象となるVM名とRG名を入れましょう。

# Setting for E-mail
$SmtpServer = 'smtp.sendgrid.net'
$Port = 587
$UserName = ''
$Password = ''
$MailFrom = ''
$MailTo = ''
$Encode = 'UTF8'
$Subject = 'テストメール from runbook'

  メールの設定です。MailFromは確実にエラーメールが届くように

  存在するメールアドレスを入れないとRFC的にしばかれます。

 

じゃテストウィンドウからテスト実行しましょ。

f:id:akazure:20190308102213j:plain

ロードして実行されて宛先のメールアドレスにTest Mailという件名のメールが

届いたでしょうかね。本文には対象としたVMの稼働状態を示す文字列が1行だけ

記載されているはず。

※メールこないんだがー、って時は、runbookが正常に終わったのかどうかを

 確認してください。正常に終わっててこない場合は、メールの問題か

 宛先アドレス側で設定されているスパムフィルタの問題などが考えられる

 かな。SendGridポータルで確認してください。

 

ここまでうまくできれば、1系統は終わり。

最後にAutomationのrunbookの画面で「公開」を押しておきます。

※「開始」ではない点注意です。開始は単体で実行してみる、という意味合い

 です。

 

続いて2系統目に入ります。

 

リソース追加からLog Analyticsと検索し、WorkSpaceを1つ作成します。

対象VMのログ設定がまだされていない場合は以下のように設定します。

f:id:akazure:20190308132820j:plain

 

ここではazure01とcentlog001というVM名の2つがLog Analytics WorkSpaceへ

ログを取り込む設定がされていることがわかります。

※しばらくほっておくと左ペインの「ログ」からHeatbeatなどのログが飛んで

 きていることがわかります。また、対象VMを選択した後、Azure Portalから

 該当のVMへ自動的にエージェントをインストールしてくれます。

 LinuxVMならomsagentというプロセスが上がります。

 

ではログ検索にいきましょう。

ここでは単純に「今から10分前までのデータでHeartbeatで出力されたレコード」

を一覧で出しています。

f:id:akazure:20190308133624j:plain

 

Splunkでもそうですが、Log AnalyticsでもKusto(くすと)と呼ばれる

専門の言語をつかって検索クエリをかける必要があります。

 

Heartbeat

| where TimeGenerated > ago(10m)

 

※Heartbeatテーブルの中でTimeGeneratedの時間が10分前のもの、という

 指定です。

 

Linuxのログだとsyslogやカスタムログでも構造的にシンプルな構造に

入っていますが、Windows ServerのAuditlogsやActivityLogsなんかは

ツリー形式で登録されていたりします。

その場合は、mv-expandやprojectという関数を使って整形するとよいです。

 

AuditLogs
| where OperationName == "Add group"
| where TimeGenerated > ago(365d)
| extend localtime = TimeGenerated+9h
| mv-expand TargetResources
| mv-expand InitiatedBy
| project DisplayName = TargetResources.displayName , InitiatedBy.user.userPrincipalName

※AuditLogsテーブルの中で、OperationNameがAdd Groupになっている

 レコードを対象に、JSTで365日前までのデータを対象にする。

 ツリー構造になっているレコード(>TargetResourcesみたいな表示)に

 なっているものは、TargetResourcesとInitiatedByの2つをmv-expandで

 1行に整形し、project関数を使って表示したいdisplaynameとuserPrincipal

 Nameの項目だけを出力する、という命令。(わけわからんよねw)

 

ま、今回はここは難しいクエリにせずにHearbeatの件数を閾値

アラートルールを設定しましょ。

 

f:id:akazure:20190308163721j:plain

 ログ検索した画面の右上にNew alert ruleというボタンがあるので、そこを

クエリ結果が出た状態のままクリックします。

 

f:id:akazure:20190308164248j:plain

 

「条件」を押すと、先ほどかけたクエリがそのまま適用された状態に

右側へブレードが移ります。

基準では、このクエリが出力された結果の”行数”が1以上の場合、っと設定

しています。

最後の評価基準では、このクエリをかける対象範囲を今から30分前までの

データを対象とし、5分間隔で定期実行する、という設定の意味です。

 

完了を押して、次のアクショングループの設定にいきます。

 

f:id:akazure:20190308165827j:plain

 

ここでは1系統目で用意していたRunbook powershellを指定していきます。

特に迷うところはない、かな。

 

最後に「アラートの詳細」で、「アラートルール名」と「説明」を

適当に入力して、「最後にアラートルールの作成」ボタンを押します。

 

以上です。5分間隔で以下のようなメールが飛び続けますw

 

f:id:akazure:20190308170218j:plain

停止するときは以下のようにLog Analytics WorkSpaceの画面で

「警告」>「アラートルールを管理します」「無効化」で停止しましょう。

f:id:akazure:20190308170355j:plain

 

今回はテスト的に疎通を取ることに注力しましたが、いくらでも応用はできます。

VMのステータスを見て、VMを起動、停止するもよし、VM以外のリソースを

コントロールする処理をPowerShellで記載すれば、とあるログの結果から一次処理

を実行する、といったことができます。

また、今回はAutomation Runbook Powershellで行いましたが、アクショングループ

を設定する際にAzure Functionを呼び出すこともLogic Appsを呼び出すことも

できます。このあたりは好みで。

あとLog AnalyticsではHeartbeatテーブルを対象にしましたが、Linuxであれば

rsylogd経由のFacilityに対して独特のログを飛ばし、その独特のログの件数を

閾値に処理を行う、といったことや、Azure VM OS内の個別のログファイルを

対象(カスタムログ)にLog Analytics WorkSpaceへ飛ばすこともできます。

 

「ログから、とある処理を自動実行したいんだけど」

といった状況があれば、上記は1つの解決策ですね。べんりやね~~♪