20200414のAWSに関する記事は18件です。

AWS ClientVPNのおかげで、3日で3000名分のリモートワーク環境を構築できた話

はじめに

表題の件で、備忘録としてダラダラと書き連ねます。
※Qiita初投稿です。実際にやってみるとアウトプットって難しいですね(今までお世話になったQiita記事の投稿者の皆様に改めて感謝です…)

背景

  • 新型コロナウイルス感染症にかかる緊急事態宣言を受けて、4/8(水)から弊社でも在宅勤務がスタート
  • 社員数4000名以上の弊社で準備していた既存VPNは、同時接続数がわずか1000
  • 在宅勤務開始直後から「VPN接続できない!」というクレームの嵐
  • NWベンダに確認したところ、現VPNは同時接続数1000ではなく、同時セッション数1000だったと判明し絶望
  • 4/9(木) 対応策検討開始。お上から「週明けには解決しろ!」
  • 「AWS ClientVPN」を発見。万年人手不足の情シスに嬉しいマネージドのVPN?激アツじゃないですか

ゴール

VPN接続したユーザが、DX経由で社内リソースやインターネット/各種Saasへアクセスできる状態を目指します
goal.png
※「全通信、社内経由とかイケてないww」「この機会にゼロトラスト進めろよw」と思われるでしょう。社内理解を得る難しさと、リードタイムなど考慮すると、選択肢が限られていたのです…

0. 前提条件

0-1. お金の確保

今回の構成において、課金対象となるのは下記のリソースです(2020年4月時点@東京リージョン)

課金対象リソース 料金 課金単位 補足
AWS ClinetVPN endpoint association $0.15 1時間毎 エンドポイントとサブネットの関連付に対して課金
AWS ClinetVPN endpoint connection $0.05 1時間毎 アクティブなVPN接続に対して課金
AD Connector(Large) $0.24 1時間毎 ディレクトリ認証を利用するため
DirectConnect(Outbound) $0.041 1GB毎 DX→DCのデータ転送

3000ユーザ※が毎日7.5時間、20営業日利用するケースを想定し、ざっくり費用を算出しました。
計算すれば分かりますが、決して安い金額にはなりません。しかし、初期費用不要/週明けには展開可ということで、緊急対応策として承認もらえました。
※社員数4000名以上なのに3000?…諸事情で全員が在宅勤務できるわけではないため一旦3000でよいとのこと

0-2. AWS環境(DirectConnectあり)

技術的には、インターネット経由で社内リソースへアクセスさせることも可能ですが、セキュリティ意識の高いお上の了承を得るのが難しい可能性があります。幸い、弊社ではDirectConnectでDCと専用線接続している AWS環境が整っていました。

0-3. 制限緩和

  • 他のAWSサービス同様、ClientVPNにも各種制限があります。 弊社の場合3000ユーザ想定のため、「エンドポイントあたりの同時クライアント接続数:2000」の制限緩和をリクエストしました。複数のエンドポイントで構成するなら緩和不要ですが、エンドポイント毎に接続先URLが異なると、エンドユーザへの展開が複雑になります。
  • 通常、制限緩和は「Service Limit Increase」の方からリクエストしますが、現時点では当該項目が見当たらなかったため「Technical Support」から起票 image.png
  • 申請の理由や背景、過去のClientVPN利用実績、なぜ複数のエンドポイント構成だと厳しいのか等、詳しい説明が必要です。最終的にはUS側の判断となるため返信を待ちます。24時頃に起票し、翌日夕方には制限緩和承認の連絡を受領しました

1. 構築編

1-1. AD Connectorの作成

ClientVPNはディレクトリ認証/相互認証を利用できますが、今回の構成はどちらも利用します。前者のため、AD Connectorを作成します。

作成手順は長くなるのでコチラをご覧ください

image.png

1-2. ClientVPNエンドポイント用セキュリティグループの作成

ClinetVPNエンドポイントを作成すると、関連付けされたサブネット上にENIが自動的に作られます。このENIに適用するセキュリティグループを作成します。
AWSコンソールにて「VPC」→「セキュリティグループ」→「セキュリティグループの作成」
image.png
名前と説明を入力、VPCを指定し、「作成」をクリック
image.png

作成完了
image.png

1-3. CloudWatchロググループ作成

クライアント接続の記録を保管するためのロググループを作成します。
AWSコンソール→「CloudWatch」→「ロググループ」で「ロググループの作成」をクリック
image.png
ロググループ名を入力し、「作成」をクリック
image.png
作成完了
image.png

1-4. サーバ/クライアント証明書作成、ACMへの証明書登録

相互認証のため、公式ドキュメントに従って証明書を作成し、AWS Certificate Manager(ACM)へアップロードします。

↓コンソールにて、アップロード完了していることを確認
image.png

1-5. ClientVPNエンドポイントの作成

いよいよ本題です。AWSコンソール→「VPC」→「クライアント VPN エンドポイント」に進み、「クライアント VPN エンドポイントの作成」をクリック
image.png

必要項目を記入します。

  • 「クライアント IPv4 CIDR」→接続するクライアントに割り当てるIP(/22以上必須)。社内/AWSリソースのIPと重複しないように注意です。※作成後変更不可
    image.png

  • 「サーバ証明書ARN」「クライアント証明書ARN」では、1-4で登録した証明書を選択

  • 「認証オプション」は全て選択

  • 「ディレクトリID」は1-1で作成したAD ConnectorのIDを選択
    image.png

  • 「接続ログ記録」は「はい」を選択し、「ロググループ名」で1-3で作成したロググループを指定

    image.png

  • 「DNSサーバ~」に、社内DNSサーバのIPを入力

  • 「セキュリティグループID」に、1-2で作成したセキュリティグループを選択

  • 「作成」をクリック
    image.png

作成完了
image.png

1-6. ClientVPNエンドポイントについて、サブネット関連付

作成したエンドポイントを選択し、「関連付け」タブの「関連付け」をクリック
image.png

対象のVPCと、関連付けるプライベートサブネットを指定し、「関連付け」をクリック
image.png

完了
image.png

しばらく待って、状態が「関連付け済み」になることを確認
image.png

※可用性確保のため、複数のサブネットを関連付けする場合は1-6を再度実施

1-7. 認証ルールの追加

誰がどこにアクセスするかを制御する、つまり認可の制御を担うのが「認証ルール」となります。
まず、作成したエンドポイントを選択し、「受信の承認」タブの「受信の承認」をクリック
image.png

要件に応じて記入し「認証ルールの追加」をクリック
- 弊社の場合、要件は「取り急ぎ社員は全てアクセス許可でよいが、社外ベンダは指定のリソースのみに限定したい」
- ユーザはADグループで管理しているため、「アクセスを付与する対象」として「特定の~」を選び、ADグループのSIDを指定
- 「送信先ネット」欄にアクセスできるIPアドレス範囲を記入
image.png

完了(しばらく待って、状態が「アクティブ」になることを確認)
image.png

1-8. クライアント設定のダウンロードと編集

あと少しです。エンドポイントを選択→「クライアントの設定のダウンロード」→「ダウンロード」をクリック
image.png

ダウンロードしたovpnファイルを開き、ファイル末尾にクライアント証明書と秘密鍵の内容をコピペします(※パス指定してもOK)
image.png

1-9. 確認

  • OpenVPNをインストール(AWS公式のクライアントもありますが、動作不安定と聞いたのでパス)
  • OpenVPNのconfigフォルダ(C:\Program Files\OpenVPN\config)に、1-8で作成したovpnファイルを格納
  • OpenVPN起動
  • タスクトレイ→「OpenVPN」メニュ→「接続」 image.png
  • ディレクトリ認証画面にて、ADユーザ名・パスワード入力し「OK」 image.png

接続成功!(社内リソースへのアクセスや、IP縛りしてるSaasへのログインも成功)
image.png

2. 検証編

上記の通り、手順はシンプルなので半日で構築完了(マネージドって素晴らしいですね…!)。しかし、動作検証の中で下記問題が発生し、土曜日がつぶれました…。

2-1. 問題:Outlookの認証失敗

時折、Outookクライアントにおいて下記エラーが表示され、全く送受信できなくなるという現象が発生。
image.png

2-2. 原因/対応策

  • 該当エラーに関する記事がありました→「インターネット接続性チェックが失敗する環境で Outlook 2013/2016 による先進認証が失敗する
  • Win10 にはNetwork Connection Status Indicator(NCSI)という機能があり、ネットワークの接続が変わったタイミング(無線LANにつなぐとか,VPNにつなぐとか)で、特定のURLに接続し、インターネット接続を確認するそうです(初めて知りました…)。Outlookで先進認証をする際も、NCSIで接続性を確認しており、インターネット接続がないと判断されると、Outlook でエラーが出るそうです
  • 調べているうちに、OpenVPNで接続確立後のルートテーブルを見ると、デフォルトルート 0.0.0.0 に対して、VPNクライアントのインターフェースのメトリックの値が大きい=優先度が低くなっていることに気づきます。「ルート追加時にメトリックを指定する方法」を参考に、ovpnファイルにroute 0.0.0.0 0.0.0.0 vpn_gateway 0を追記することで、デフォルトゲートウェイを追加でき、エラーは解消しました。

3. 運用編

各種エラーをつぶし、まともに使えるレベルになりました。日曜日一日かけて、下記を実施しました。

3-1. ユーザへの提供準備

  • テストユーザ増やして、動作検証
  • OpenVPNインストーラ/ovpnファイルをユーザに配信できるよう、IT資産管理ソフト設定
  • ユーザ向けインストール/初期設定マニュアルの作成

3-2. 監視環境の整備

3-2-1. 課金状況の監視

「使いすぎは避けて」「Web会議するときはVPN切断を」と案内したものの、(情シスの方なら分かっていただけると思いますが)ユーザがどんな使い方をするか分かりません。Cost Explorerで課金対象項目を指定したレポートを作成しました

image.png

3-2-2. 利用状況の監視

ClientVPNエンドポイントについて、CloudWatchで下記のメトリクスを取得可能です(参考
- 送信バイト
- 送信パケット
- 受信バイト
- 受信パケット
- 認証の失敗 (カウント)
- アクティブ接続 (カウント)

当初、CloudWatchダッシュボードをサクッと作る予定でしたが、Grafanaでダッシュボードを作成し、社内全員に公開することにしました。(逐一状況を把握したいお上の要望に応えると同時に、ユーザにも状況をシェアすることで、皆に考えながら使ってもらいたいよねという意見があったため)
image.png

※初めてGrafana触りましたが、便利ですね。参考にした記事は↓
- Grafanaでかっけぇダッシュボード作るよ!(構築・設定編)
- 今回作成したClientVPNエンドポイントと同じ、プライベートサブネット上でEC2(t2.micro)で動いてます。メトリクス取得するため、VPCエンドポイント作成も必要

終わりに

週明けからユーザへの提供開始。ユーザからは次々と好評の声が寄せられてますが、利用ユーザ数が増えるとどうなるか未知のため、戦々恐々と監視画面を見つめています。
一息ついたところで、上司がポロリ「でも、上からは『こんなに簡単にできるなら、なぜもっと早くやってなかったんだ』と怒られるかもな…」
…情シスの悲しい現実ですね。
管理面での課題もあるため、今後随時改善していく予定です。最後まで読んで頂きありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【試験合格記】AWS 認定 機械学習 – 専門知識(MLS-C01)

お疲れさまです。
表題の試験に合格したため記録として残したいと思います。

結果

合格(902点)

分野 名称 割合
1 データエンジニアリング 20%
2 探索的データ解析 24%
3 モデリング 36%
4 機械学習の実装と運用 20%

所有資格

資格名 取得年月日
AWS Certified Solutions Architect - Associate (SAA) 2018-06-14
AWS Certified SysOps Administrator - Associate (SOA) 2018-06-22
AWS Certified Developer - Associate (DVA) 2018-06-25
AWS Certified Solutions Architect - Professional (SAP) 2018-07-19
AWS Certified DevOps Engineer - Professional (DOP) 2020-02-19
AWS Certified Big Data - Speciality (BDS) 2020-03-23
AWS Certified Security - Speciality (SCS) 2020-04-01
AWS Certified Machine Learning - Speciality (MLS) 2020-04-13

事前知識

機械学習にガッツリ取り組んだ経験はありません。
GCPのデータエンジニア試験を受けた際に機械学習に関する一般的な知識は重点的に学んだ経験があります。

所感

他の方の合格記事を見られた方はなんとなく理解されてると思いますが、出題範囲はほぼ機械学習モデルとSageMakerに集中しています。
ですので、この領域が完全に未経験の場合は回帰や分類、教師ありなし学習などがどのような意味か、というところから書籍などで理解する必要があります。
一部、データレイクやETL周りのアーキテクチャも出題されますが、これはSAPやDOPを取得していれば理屈で答えられると思います。

受験者に向けたアドバイス

  • 予測や分類、アルゴリズムやハイパーパラメータなどは幅広く固有名詞とどういうパターンに当てはまるか整理する
  • 正規化、非正規化、トレーニングセットやテストセットの精度に異常がある場合の改善方法を理解する
  • SageMakerと親和性の高いAWSサービスの理解と組み合わせ方を学ぶ
  • ノートブックインスタンスの特徴を把握しておく
  • Transcribe、Comprehend、Translate、Polly、Lexなどの機械学習関連サービスの特徴を理解する
  • KinesisとGlueの使い方を理解する

まとめ

これで専門知識資格を3つ取得できました。
月内にネットワークを受けられればいいなと思いながら地道に試験対策を進めていこうと思います。
あとはこの試験について学習したことで機械学習の具体的な使い方やワークフローについて理解できたので社内データをなんとか活用したいというモチベーションが上がったことが収穫でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS 課金回避方法

AWS無料期間について

AWSのアカウントを作成してから12ヶ月間、以下のアプリケーションは無料で利用できます。

※12ヶ月が過ぎてしまうと課金が始まってしまうので注意が必要。

Amazon EC2

750時間/月 (t2.microインスタンスの使用もこれに含む)まで

Amazon S3:


5GBの標準ストレージ、20,000件のGETリクエスト、2,000件のPUTリクエスト

Elastic IPアドレス


実行中のインスタンスに関連づけられたElastic IPアドレスを1つだけ
インスタンスに紐付いていないElastic IPアドレスは全て課金対象

IAM


IAMユーザーの一時的なセキュリティ認証情報を使用して他のAWSサービスにアクセスするときのみ料金が発生

2つ目以降のインスタンスを作成する際の課金回避法

インスタンスを作成し直す際や、別のアプリケーションをデプロイするためには別インスタンスを立ち上げることになります。以下の手順を踏めば課金は解除できるでしょう。

手順

1.Elastic IPアドレスを解放する
2.(すでに作成済みで、これ以上必要の無い場合)紐付いているS3バケットを削除する
3.インスタンスを削除する

1.Elastic IPアドレスを解放する

1.AWSアカウントのルートユーザーにログインする
※IMAユーザーの場合、以下のようなエラーが出ると思います。
a.png

2.Amazon EC2 コンソールを開く
3.Elastic IP アドレスを選択し、[Actions]、[Release addresses] の順に選択します。
a.png

※もし、解放ができない場合は、アドレスの関連付けの解除を押してからアドレスの解放を押してください。
以上で、1.Elastic IPアドレスを解放は完了です。

2.(すでに作成済みで、これ以上必要の無い場合)紐付いているS3バケットを削除する

1.AWS マネジメントコンソールにサインインし、Amazon S3 コンソールを開きます。
2.[バケット名] リストで、削除するバケットの名前の横にあるバケットアイコンを選択し、[バケットを削除する] を選択します。

※バケットにオブジェクトが含まれている場合は、[This bucket is not empty (このバケットは空ではありません)] というエラーアラートの [バケットを空にする設定] リンクを選択し、[バケットを空にする] ページの指示に従って、バケットを空にしてから削除します。次に、[バケットを削除する] ページに戻り、バケットを削除します。
以下のような状態になれば2.に戻って削除に入ります。
a.png
3.[バケットを削除する] ダイアログボックスで、削除するバケットの名前を確認のために入力し、[確認] を選択します。

3.インスタンスを削除する

インスタンスを停止するとEIastic IPアドレスは関連づけられなくなるので、あらかじめEIastic IPアドレスを解放し課金されないように気をつけましょう。
1.ナビゲーションペインで [Instances] を選択し、インスタンスを選択します。


2.[Actions] を選択して [Instance State] を選択し、[Stop] を選択します。
[Stop] が無効になっている場合は、インスタンスが既に停止しているか、またはルートボリュームがインスタンスストアボリュームです。(インスタンスを停止すると、インスタンスストアボリューム上のデータは消去されます。インスタンスストアボリュームのデータを保持するには、このデータを永続的ストレージに必ずバックアップしてください。)

停止、終了など選択できると思うので削除したい場合は終了を押してください
停止の場合はEIastic IP(住所のようなもの)を削除していないと課金が発生してしまいます。

3.確認ダイアログボックスで [Yes, Stop] を選択します。
※インスタンスが停止するまで、数分かかる場合があります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【GitHub Actions】俺はactions/cacheをやめるぞ!S3ィィィィィッ!!

はじめに

GitHub Actions公式のキャッシュ機能であるactions/cacheは、

  • Pull Requestでコケた時にRe-run jobsするとactions/cacheアクションが正常に動作しない
  • actions/cacheアクションは時折キャッシュの取得に失敗することがある

などの問題を抱えているようです。

詳しくは、こちらのブログ記事を参照ください。

加えて、キャッシュの容量はリポジトリごとに5GBとのことなのですが、明らかに容量オーバーしていないのに、いつのまにかキャッシュが追い出されていることがあるような気がします(検証はして無いので、私の思い込みかもしれません)。

そこで、actions/cacheを使わず、S3に独自にキャッシュさせることにしました。

今のところ、快適に動いているので、今回その内容を紹介させていただきます。

S3に独自にキャッシュするためにやったこと

  1. S3バケットの作成
  2. IAMユーザーの作成
  3. GitHubリポジトリへのSecretsの設定
  4. ワークフローの修正とシェルスクリプトの作成

1. S3バケットの作成

まず、S3バケットを作成してください。

パブリックアクセスをすべてブロックをオンにするなど、標準の設定で問題ありません。

s3.png

GitHub Actionsの実行環境は、Azureの米国のリージョンにあるそうなので、S3バケットは近そうなus-east-1に作ることにしました。

WindowsとUbuntuのランナーはAzureでホストされ、IPアドレスの範囲がAzureデータセンターと同じになります。 現在、すべてのWindows及びUbuntuのGitHubホストランナーは、以下のAzureリージョン内にあります。

米国東部(eastus)
米国東部2 (eastus2)
米国西部2 (westus2)
米国中部 (centralus)
米国中南部(southcentralus)

https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners#ip-addresses-of-github-hosted-runners

なお、適当な期間を過ぎたらキャッシュが削除されるよう、ライフサイクルルールも作っておくと良いと思います。

lifecycle.png

2. IAMユーザーの作成

S3とキャッシュをやり取りするためのIAMユーザーを作成します。

S3に対して、以下のような操作を許可するようポリシーを与えます。

IAMユーザーのポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::aws-us-east-1-xxxxxxxxxxxx-github-actions-caches/*",
                "arn:aws:s3:::aws-us-east-1-xxxxxxxxxxxx-github-actions-caches"
            ]
        }
    ]
}

また、アクセスキーを作成し、アクセスキーIDとシークレットアクセスキーを控えてください。

後で、GitHubのSecretsに設定します。

3. GitHubリポジトリのSecretsへの設定

先ほどのアクセスキーIDとシークレットアクセスキーをそれぞれGitHubのリポジトリのSecretsに設定します。

secrets

4. ワークフローの修正とシェルスクリプトの作成

コメントアウトしている箇所は、actions/cacheを使っている場合のワークフローです。

4.1. composerの例

.github/workflows/build_test.yml
#      - name: cache composer
#        id: cache-composer
#        uses: actions/cache@v1.1.2
#        with:
#          path: laravel/vendor
#          key: composer-v1-${{ hashFiles('laravel/composer.lock') }}
#      - name: composer install
#        if: steps.cache-composer.outputs.cache-hit != 'true'
#        run: composer install -n --prefer-dist
#        working-directory: laravel
      - name: composer install
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_CACHE_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CACHE_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-1 # aws cliを使う上でのデフォルトリージョン
          AWS_S3_BUCKET: aws-us-east-1-847754820862-github-actions-caches
          CACHE_KEY: composer-v1-${{ hashFiles('laravel/composer.lock') }}
          PATHS: vendor # EXEC_DIRを起点としたパス
          BUILD_COMMAND: composer install -n --prefer-dist
          EXEC_DIR: laravel # zipやBUILD_COMMANDを実行するディレクトリ
        run: .github/workflows/cache.sh

4.2. npmの例

.github/workflows/build_test.yml
#      - name: cache npm
#        id: cache-npm
#        uses: actions/cache@v1.1.2
#        with:
#          path: laravel/node_modules
#          key: npm-v1-${{ hashFiles('laravel/package-lock.json') }}
#      - name: npm ci if there are no node_modules
#        if: steps.cache-npm.outputs.cache-hit != 'true'
#        run: npm ci
#        working-directory: laravel
      - name: npm ci
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_CACHE_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CACHE_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-1 # aws cliを使う上でのデフォルトリージョン
          AWS_S3_BUCKET: aws-us-east-1-xxxxxxxxxx-github-actions-caches
          CACHE_KEY: npm-v1-${{ hashFiles('laravel/package-lock.json') }}
          PATHS: node_modules # EXEC_DIRを起点としたパス
          BUILD_COMMAND: npm ci
          EXEC_DIR: laravel # zipやBUILD_COMMANDを実行するディレクトリ
        run: .github/workflows/cache.sh

なお、キャッシュとして保存したいディレクトリやファイルが複数ある場合は、PATHSにスペース区切りで記述してください。

4.3. シェルスクリプト

ひとつのシェル内で、

  • キャッシュがS3にあればダウンロード
  • キャッシュが無ければ、パッケージをインストールしてS3にキャッシュとしてアップロード

を行います。

.github/workflows/cache.sh
#!/bin/bash

set -eu

cd ${GITHUB_WORKSPACE}/${EXEC_DIR}

FILE=${CACHE_KEY}.zip

CACHE_HIT=`aws s3 ls s3://${AWS_S3_BUCKET}/${FILE}` || true

if [ -n "$CACHE_HIT" ]; then
  echo "Found a cache for key: ${FILE}"
  aws s3 cp s3://${AWS_S3_BUCKET}/${FILE} ./
  unzip -n $FILE
  echo "Done is unzip -n ${FILE}"
  rm -f $FILE
  echo "Done is rm -f ${FILE}"
else
  echo "No cache is found for key: ${FILE}"
  $BUILD_COMMAND
  zip -ryq $FILE $PATHS
  echo "Done is zip -ryq ${FILE} ${PATHS}"
  aws s3 cp $FILE s3://${AWS_S3_BUCKET}/
  echo "Stored cache to ${FILE}"
fi

実際に使ってみて

正確に測っていませんが、速度的にはactions/cacheと遜色無い気がします。

何より、キャッシュがそこにあるはずなのにヒットしない、という状況にもやもやすることが無いのは快適でした。

また、actions/cacheは、

  • キャッシュをリストアしたいタイミングに使用するステップを記述するのみとなり、保存のステップは記述しない。キャッシュの保存は、actions/cacheのステップが存在するジョブの最後にPost + actions/cacheのステップ名というステップ名で実行される。

という仕様となっており、ジョブが正常終了しなかった場合は、キャッシュを保存するステップは実行されません。

今回の自家製シェルはパッケージのインストール後に必ずキャッシュを保存するので、その点で無駄が少ないのでは、と思っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【GitHub Actions】「俺はactions/cacheをやめるぞ!」「S3ィィィィィッ!!」

はじめに

GitHub Actions公式のキャッシュ機能であるactions/cacheは、

  • Pull Requestでコケた時にRe-run jobsするとactions/cacheアクションが正常に動作しない
  • actions/cacheアクションは時折キャッシュの取得に失敗することがある

などの問題を抱えているようです。

詳しくは、こちらのブログ記事を参照ください。

加えて、キャッシュの容量はリポジトリごとに5GBとのことなのですが、明らかに容量オーバーしていないのに、いつのまにかキャッシュが追い出されていることがあるような気がします(検証はして無いので、私の思い込みかもしれません)。

そこで、actions/cacheを使わず、S3に独自にキャッシュさせることにしました。

今のところ、快適に動いているので、今回その内容を紹介させていただきます。

S3に独自にキャッシュするためにやったこと

  1. S3バケットの作成
  2. IAMユーザーの作成
  3. GitHubリポジトリへのSecretsの設定
  4. ワークフローの修正とシェルスクリプトの作成

1. S3バケットの作成

まず、S3バケットを作成してください。

パブリックアクセスをすべてブロックをオンにするなど、標準の設定で問題ありません。

s3.png

GitHub Actionsの実行環境は、Azureの米国のリージョンにあるそうなので、S3バケットは近そうなus-east-1に作ることにしました。

WindowsとUbuntuのランナーはAzureでホストされ、IPアドレスの範囲がAzureデータセンターと同じになります。 現在、すべてのWindows及びUbuntuのGitHubホストランナーは、以下のAzureリージョン内にあります。

米国東部(eastus)
米国東部2 (eastus2)
米国西部2 (westus2)
米国中部 (centralus)
米国中南部(southcentralus)

https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners#ip-addresses-of-github-hosted-runners

なお、適当な期間を過ぎたらキャッシュが削除されるよう、ライフサイクルルールも作っておくと良いと思います。

lifecycle.png

2. IAMユーザーの作成

S3とキャッシュをやり取りするためのIAMユーザーを作成します。

S3に対して、以下のような操作を許可するようポリシーを与えます。

IAMユーザーのポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::aws-us-east-1-xxxxxxxxxxxx-github-actions-caches/*",
                "arn:aws:s3:::aws-us-east-1-xxxxxxxxxxxx-github-actions-caches"
            ]
        }
    ]
}

また、アクセスキーを作成し、アクセスキーIDとシークレットアクセスキーを控えてください。

後で、GitHubのSecretsに設定します。

3. GitHubリポジトリのSecretsへの設定

先ほどのアクセスキーIDとシークレットアクセスキーをそれぞれGitHubのリポジトリのSecretsに設定します。

secrets

4. ワークフローの修正とシェルスクリプトの作成

コメントアウトしている箇所は、actions/cacheを使っている場合のワークフローです。

4.1. composerの例

.github/workflows/build_test.yml
#      - name: cache composer
#        id: cache-composer
#        uses: actions/cache@v1.1.2
#        with:
#          path: laravel/vendor
#          key: composer-v1-${{ hashFiles('laravel/composer.lock') }}
#      - name: composer install
#        if: steps.cache-composer.outputs.cache-hit != 'true'
#        run: composer install -n --prefer-dist
#        working-directory: laravel
      - name: composer install
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_CACHE_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CACHE_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-1 # aws cliを使う上でのデフォルトリージョン
          AWS_S3_BUCKET: aws-us-east-1-847754820862-github-actions-caches
          CACHE_KEY: composer-v1-${{ hashFiles('laravel/composer.lock') }}
          PATHS: vendor # EXEC_DIRを起点としたパス
          BUILD_COMMAND: composer install -n --prefer-dist
          EXEC_DIR: laravel # zipやBUILD_COMMANDを実行するディレクトリ
        run: .github/workflows/cache.sh

4.2. npmの例

.github/workflows/build_test.yml
#      - name: cache npm
#        id: cache-npm
#        uses: actions/cache@v1.1.2
#        with:
#          path: laravel/node_modules
#          key: npm-v1-${{ hashFiles('laravel/package-lock.json') }}
#      - name: npm ci if there are no node_modules
#        if: steps.cache-npm.outputs.cache-hit != 'true'
#        run: npm ci
#        working-directory: laravel
      - name: npm ci
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_CACHE_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CACHE_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ap-northeast-1 # aws cliを使う上でのデフォルトリージョン
          AWS_S3_BUCKET: aws-us-east-1-xxxxxxxxxx-github-actions-caches
          CACHE_KEY: npm-v1-${{ hashFiles('laravel/package-lock.json') }}
          PATHS: node_modules # EXEC_DIRを起点としたパス
          BUILD_COMMAND: npm ci
          EXEC_DIR: laravel # zipやBUILD_COMMANDを実行するディレクトリ
        run: .github/workflows/cache.sh

なお、キャッシュとして保存したいディレクトリやファイルが複数ある場合は、PATHSにスペース区切りで記述してください。

4.3. シェルスクリプト

ひとつのシェル内で、

  • キャッシュがS3にあればダウンロード
  • キャッシュが無ければ、パッケージをインストールしてS3にキャッシュとしてアップロード

を行います。

.github/workflows/cache.sh
#!/bin/bash

set -eu

cd ${GITHUB_WORKSPACE}/${EXEC_DIR}

FILE=${CACHE_KEY}.zip

CACHE_HIT=`aws s3 ls s3://${AWS_S3_BUCKET}/${FILE}` || true

if [ -n "$CACHE_HIT" ]; then
  echo "Found a cache for key: ${FILE}"
  aws s3 cp s3://${AWS_S3_BUCKET}/${FILE} ./
  unzip -n $FILE
  echo "Done is unzip -n ${FILE}"
  rm -f $FILE
  echo "Done is rm -f ${FILE}"
else
  echo "No cache is found for key: ${FILE}"
  $BUILD_COMMAND
  zip -ryq $FILE $PATHS
  echo "Done is zip -ryq ${FILE} ${PATHS}"
  aws s3 cp $FILE s3://${AWS_S3_BUCKET}/
  echo "Stored cache to ${FILE}"
fi

実際に使ってみて

正確に測っていませんが、速度的にはactions/cacheと遜色無い気がします。

何より、キャッシュがそこにあるはずなのにヒットしない、という状況にもやもやすることが無いのは快適でした。

また、actions/cacheは、

  • キャッシュをリストアしたいタイミングに使用するステップを記述するのみとなり、保存のステップは記述しない。キャッシュの保存は、actions/cacheのステップが存在するジョブの最後にPost + actions/cacheというステップ名で実行される。

という仕様となっており、ジョブが正常終了しなかった場合は、キャッシュを保存するステップは実行されません。

今回の自家製シェルはパッケージのインストール後に必ずキャッシュを保存するので、その点で無駄が少ないのでは、と思っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2 EBS ボリューム拡張

手順メモ

EC2に接続されてるボリュームをAWS管理画面上から拡張

管理画面で実行
今回は8GB -> 30GB に拡張

現状をdf

# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        992M   56K  992M   1% /dev
tmpfs          1001M     0 1001M   0% /dev/shm
/dev/xvda1      7.8G  7.5G  173M  98% /

lsblk で確認

# lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  30G  0 disk
└─xvda1 202:1    0   8G  0 part /

growpart実行

growpart - extend a partition in a partition table to fill available space

growpart manページ

# growpart /dev/xvda 1
# growpart /dev/xvda 1
CHANGED: disk=/dev/xvda partition=1: start=4096 old: size=16773086,end=16777182 new: size=62910430,end=62914526

resize2fs で拡張実行

# resize2fs /dev/xvda1
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/xvda1 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 2
The filesystem on /dev/xvda1 is now 7863803 (4k) blocks long.

dfで確認

# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        992M   56K  992M   1% /dev
tmpfs          1001M     0 1001M   0% /dev/shm
/dev/xvda1       30G  7.5G   22G  26% /

完了

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(編集中)API Gateway

  • API Gatewayとは、Web API(HTTP/HTTPSベースで実現するAPI)の一種。
  • リージョンレベルでデプロイされるサービス(S3と同じ)
  • サーバレスアーキテクチャ(インフラ管理は不要)
  • 呼び出し回数によって料金が発生

image.png

そもそもRESTとは

 RESTとはRepresentational State Transferの略称で、下記のRESTの考え方に従って実装されたAPIをRESTful API(またはREST API)と呼ぶ。

  • HTTPのメソッド(命令)でデータ操作種別(CRUD)を表す
    • POSTメソッドであれば作成(Create)、GETメソッドであれば参照(Reference)、PUT/PATCHメソッドであれば更新(Update)、DELETEメソッドであれば削除(Delete)を表す ステートレスにする
  • 前回のAPIコール結果にかかわらず同じ値を戻す。例えば合計1000件のデータが存在し、1回目のAPIコールで100件まで取得したとしても、その状態は考慮せず、2回目のAPIコールでも同じ100件を戻すという挙動だ
  • URIで操作対象のリソースを判別可能にする
    • RESTでは、APIを介して操作する対象を「リソース」と呼ぶ。例えば「ユーザー」というリソースを表すために「users」というリソース名を付与することがある。ユーザーIDが「1000」のユーザー情報を取得したい場合、下記のようなURIフォーマットでURIを生成し、操作可能とする
  • レスポンスとしてXMLもしくはJSONで操作結果を戻す
    • 適切にデータ操作できた場合、データ記述言語のXMLもしくはJSONでデータ操作結果を記述し、HTTPのレスポンスボディーに含め、APIコール元に戻す

 Web APIが爆発的に広がるきっかけになったのがスマートフォンアプリではないかと思います。スマートフォンアプリはそれ単体で使われるものは多くなく、何らかの形でインターネット上のサーバとデータの送受信を行っています。そうした時にサーバ側はWeb APIを用意し、アプリからはWeb APIを呼び出します。一般的に外部の開発者に向けて公開されているものがWeb APIと思われますが、こういったそのアプリ向けだけの専用かつ非公開なWeb APIはとても数多く存在します。

image.png

image.png

image.png

image.png

image.png

開発基盤と問わずに実行

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Translate、AWS Lambda を使った自動翻訳(後編)

はじめに

前回の記事でご紹介した、ドキュメント翻訳の自動化の実践の続きです。

batch-translation-6.gif

コンテンツを外国の言語に翻訳するには、バッチ翻訳ジョブを実行することで、容易かつコスト効率よく解決することができます。
しかし、一連のドキュメントを集めたり、そのドキュメントに対して定期的に非同期のバッチ API を呼び出したりすると時間がかかります。
ドキュメントが用意された時点で、すぐに翻訳処理を開始できたほうがいいですよね。

AWS Lambda と リアルタイム翻訳を使用する進んだアプローチ

イベント駆動型アーキテクチャを利用することで、簡単に、自然な翻訳を行えるようになります。

特定の S3 バケットにドキュメントがアップロードされたときに、そのバケットから AWS Lambda に通知が送信されるように設定を構成します。

実践の流れ

S3 バケットの通知により、AWS Lambda では、次のようなイベントのシーケンスのためのコードが実行されます。

1. ドキュメントを読み出し S3 バケットにアップロードします。
2. リアルタイム翻訳 API に渡せるように、要素をドキュメントから抽出します。
3. リアルタイム翻訳 API にこの要素を渡します。
  さらに、リアルタイム翻訳 API からの出力を使って翻訳済みドキュメントを再構成します。
4. 指定したバケットに翻訳したドキュメントを保存します。

ステップ1.  CloudFormation を使ったアプリケーションの起動

コンソールを使い、 AWS CloudFormation スタックを起動します。

AWS CloudFormation コンソールで、新しくリソース (標準) を設定し、[Create stack]をクリックします。
2020-04-13_15h21_47.png

[Amazon S3 URL] を選択し、Amazon S3 URL の項目に
https://s3.amazonaws.com/aws-ml-blog/artifacts/serverless-document-translation/translate-lambda-cfn-stack.yml
をコピーします。次に [Next] をクリックします。
image.png

ステップ2. S3 バケットを作成して指定する

[Stack name] には、automated-document-translation のように、アカウント内で一意のスタック名を入力します。
[IAMRoleName] には、TranslationLambdaExecRole のように、アカウント内で一意の IAM ロール名を入力します。
2020-04-13_15h30_21.png

[LambdaFunctionName] には、trigger-translation のように、一意の AWS Lambda 関数名を入力します。
[InputBucketName] には、スタックが作成した Amazon S3 バケットのための、一意の名前を入力します。
入力のドキュメントは、翻訳処理前に、このバケットにアップロードされます。

新規の S3 バケットが作成されるため、既存バケットの名前は使わないようにします。
※ 名前が同じものがあると、スタックの構築が失敗します。

[OutputBucketName] には、出力バケットのための一意の名前を入力します。
翻訳済みの出力ドキュメントが、このバケットに保存されます。
入力バケットの場合と同様に一意の名前をつけてください。

[SourceLanguageCode] では、翻訳前のドキュメントの言語を、コードで入力します。
たとえば、英語を指定する場合は en とし、主として使われている言語を検出させる場合は auto とします。

[TargetLanguageCode] には、翻訳後のドキュメントで使う言語を、コードで入力します。
日本語を指定する場合には ja とします。
(使用可能な言語コードの詳細についてはこちら
[Next] を選択します。
2020-04-13_15h30_53.png
[Configure Stack Options] ページで、スタック用のタグなど、追加のオプションパラメータを選択します。
(特に必要なければいじらなくてOK)
[Next] を選択します。
[I acknowledge that AWS CloudFormation might create IAM resources with custom names.] のチェックボックスをオンにします。
2020-04-13_16h07_18.png
[Create Stack] をクリックします。
スタックの作成が完了するまで、最大 1 分間ほどかかります。

ロール(権限)の内訳

Lambda 関数では、このロールが必要な Amazon S3 と Amazon Translate API にアクセスできることを想定しています。
この IAM ロールには、 2 つのポリシーがアタッチされています。

1 、入力および出力の S3 バケットに対する、読み出し/書き込みのアクセス権限
(GetObject と PutObject) を与えるカスタムポリシー。
2 、AWS が管理しているポリシー TranslateReadOnly
Amazon Translate への API を呼び出すためのもの。

ステップ3. アプリケーションの実行

AWS CloudFormation スタックの作成が終わると、このソリューションを利用開始できます。
デザインを表示させるとこんな構成が出来上がります。視覚的にわかりやすいですね。
2020-04-13_16h44_24.png
2020-04-14_15h03_19.png

入力側の S3 バケットに、翻訳対象のテキストファイルをアップロードします。
これによりワークフローが起動され、処理が完了すると、翻訳済みドキュメントが出力側の S3 バケットに自動的に保存されます。

翻訳済みドキュメントは、出力 S3 バケット内の次のようなパスに保存されます。
<TargetLanguageCode>/<original path of the source file>.

たとえば、入力ドキュメントのタイトルが test.txt となっていて、入力 S3 バケット内の in202040413 という名前の S3 フォルダーに保存されているとします。
2020-04-14_04h13_59.png

日本語に翻訳されたドキュメントの保存先は、出力バケット内の 「ja/out20200413/test.txt」 となります。
2020-04-14_04h14_29.png

出力 S3 バケットでドキュメントが見つからない場合は、Amazon CloudWatch Logs で対応する Lambda 関数をチェックし、失敗の原因となっている可能性のあるエラーを探します。

ここでは、UTF-8 形式のテキストドキュメントのみを処理しています。
Lambda 関数での最大実行時間 (timeout) に関する制限があります。

結果

今回は一つのファイルに英語の文章をまとめてから、アップロードしました。
2020-04-14_15h08_04.png
今回も違和感のないキレイな日本語になりました!

所感

1. 収集したドキュメントに、非同期のバッチ翻訳を行うシンプルな翻訳
2. AWS Lambda と Amazon のリアルタイム翻訳を使い、ドキュメントを入手する度に同期的に翻訳を行う、
より進んだ手法
今回この2つの手法を使ったドキュメント翻訳を行いましたが、1だとバケットを一度作ってしまえば Translate で操作して簡単に行なえますし
2を使えば S3 バケットを前もって作らなくても、自動的な翻訳が一度に一つのコンソール画面で処理できるので、面倒が少なくて済みます。
作業効率もあがりますし、文の長さを気にせず、質の高い翻訳が実現できるので積極的に使っていきたいです!

公式サイトリンク

AWS サービスブログ
Amazon Translate

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

浜松市版新型コロナウイルス対策サイトをAWSで運用する方法

はじめに

浜松市 新型コロナウィルス感染症対策サイトのお手伝いをしています。
浜松市では市内で 2020/3/29 2020/3/28 に初の感染者が確認されてから、市内外のITコミュニティ有志により東京都の新型コロナウィルス感染症対策サイトをForkして4/1から公開を始めました。

浜松市 新型コロナウィルス感染症対策サイト

東京都のサイトはデザインや中に使用されている技術のみならずデプロイまでの運用についてもしっかり出来上がっている素晴らしいものなので地方で立ち上げる場合も非常に短時間で公開できました。
【静岡新聞】 コロナ対策で新サイト 浜松市と市内IT関連有志

これから同じように立ち上げようとする方のために主にデプロイに関しての解説をしたいと思います。

派生版を作成するには

派生版を作成する場合のノウハウについては主に下記のサイトにナレッジがまとまっていますのでこちらを参考にたちあげます。
「東京都 新型コロナ対策サイト」地域展開に向けて情報共有のナレッジ

特に派生版を作るときにまず、やることリストには技術的な記事以外にも行政とつながるための連絡方法なども書いてあるので一読することをお勧めします。

基本的なデプロイの仕組み

下表の左列に記載されたブランチが更新されると、ブランチとWebサイトの更新が自動的におこなわれます。
この自動化にはGutHub Actionsの機能が使用されています。

image.png

すでに本家サイトに用意されたワークフローにより、下記の表にあるように
developmentブランチにpushされるとdev-pages
stagingブランチにpushされるとgh-pages
masterブランチにpushされるとproduction
にHTMLがpushされます。

ブランチ HTML類がbuildされ、更新されるブランチ 目的
development dev-pages 開発用サイト
staging gh-pages ステージングサイト
master production 本番サイト

buildで出来上がったファイルは以下のような構成になりますので本番公開するにはこのフォルダが サイトのトップディレクトリ になるように配置すればWEB公開されます。
https://github.com/tokyo-metropolitan-gov/covid19/tree/production

GitHub Pagesで公開する方法

GitHub Pagesの公開機能を使用してそのままのドメインで公開しようとするとサブディレクトリになってしまいますので中で使用される相対パスがずれてしまい画像やJSが参照できず404になってしまいます。
https://tokyo-metropolitan-gov.github.io/covid19/
試してはいませんが、このフォルダがトップディレクトリになるようにカスタムドメインを設定すればGitHub Pagesでも公開できると思います。

GitHub Pagesで公開する場合は次の制限があります 「100GB bandwidth/month」「10 builds/hour」

自サイトやさくらインターネットVPSなどで運用する場合

上記で出来上がったproductionブランチのルート以下のフォルダを丸ごとWEBサーバーの公開フォルダにコピーします。
このフォルダがサイトのトップディレクトリ になるようにする必要があります。(試していないですが...多分)

さくらインターネットでは新型コロナ情報まとめサイト向けのサーバーを無料提供のサポートを行っています。

Netlifyで公開

東京サイトをはじめとして、多くの派生サイト(2020/4/13現在の浜松版も)ではNetlifyを使用して公開されていることが多いです。
Netlifyの無料枠は「100GB bandwidth/month」「300 build minutes/month」になります。
また、COVID-19のサポートとしてこちらから申請することで2020/3より6か月間上記制限なしで利用することができます。

Netlifyを利用したデプロイはすでに以下のような記事が出ていますのでそちらを参考にするとよいと思います。
- 東京都新型コロナウイルス感染症対策サイトをforkしてnetlifyでdev環境を立ち上げる手順
- (東京都 新型コロナウイルス感染症対策サイトで)Netlifyでデプロイプレビューを設定するとレビューが捗るよ

Amazon S3 で公開

浜松版サイトではAWSからのサポートの提供の申し出があったため、現在のNetlifyでの公開を近日中にAWSを使用した公開に切り替える予定です。

そこで、どのようにしてAWSにデプロイするかを記載します。
S3バケットの作成と、そのバケットの公開設定、バケットにアクセスするためのIAMユーザーの作成は済んでいる前提です。
初めての方は下記のサイトを参考にS3バケットを用意してください。

IAMユーザーには作成したバケットに対して読み書きができるような権限を与えておきます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::[バケット名]/*",
                "arn:aws:s3:::[バケット名]"
            ]
        }
    ]
}

作成したバケット名リージョンおよび、作成したIAMユーザーの Access key IDSecret_Access_Key を後述するスクリプトで使用するため控えておいてください

GitHub Actionsの設定

GitHub ActionsではGitHubレポジトリに対しての何らかのイベントをトリガーとして何らかのアクションを実行します。

GitHub Actionsのワークフローは .github/workflowsに入っており、主にCOVID-19サイトのデプロイに関するワークフローは以下のものが用意されています。

  • developブランチにプッシュしたときにdev-pagesへデプロイするワークフローはdevelop.yml
  • stagingブランチにプッシュしたときにgh-pagesへデプロイするワークフローはstaging.yml
  • masterブランチにプッシュしたときにproductionへデプロイするワークフローはdeploy.yml

実行時には先ほど作成したIAMユーザーのACCESS_KEY_ID,SECRET_ACCESS_KEY が必要になります。
Forkしたブランチの[Settings]-[Secrets]にスクリプトに使用しているキーで保存しておきます
image.png

今回は、staging.ymlを修正してS3にデプロイする処理を加えます。(他のものも適宜ブランチ名、バケット名など変更すればOK)

.github/workflows/staging.yml
name: staging deploy

on:
  push:
    branches:
      - staging

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: '10.x'

      - name: Cache dependencies
        uses: actions/cache@v1
        with:
          path: ~/.cache/yarn
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-
      - run: yarn install --frozen-lockfile
      - run: yarn run test
      - run: yarn run generate:dev --fail-on-page-error
      - run: "echo \"User-agent: *\nDisallow: /\" > ./dist/robots.txt"

      - name: deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist

      # S3デプロイのために今回追加する箇所
      - name: deploy to s3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl public-read --follow-symlinks --delete
        env:
          AWS_S3_BUCKET: '用意したバケット名'
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'バケットがあるリージョン'   # 東日本ならば ap-northeast-1
          SOURCE_DIR: './dist'

完成イメージ

上記のワークフローファイルをコミットしてstagingブランチにプッシュすると、コミットしたワークフローが走り出し gh-pages と S3にデプロイされます。

image.png


最後に

浜松市版新型コロナウイルス対策サイトの運営は東京サイトに比べて人数も少なく、特定の人にタスクが集中してしまう状況にあります。是非興味がありましたら気軽に下記のリンクから一緒に参加してみませんか?

浜松市 新型コロナウイルス対策サイト↓
https://stopcovid19-hamamatsu.netlify.com/
GitHubリポジトリ
https://github.com/code-for-hamamatsu/covid19
Slack
https://join.slack.com/t/jaws-ug-hamamatsu/shared_invite/zt-dc5cgs87-cNw8QOxXeqhJnB8YB8A6Uw

リンク

浜松市 新型コロナウイルス対策サイトのコントリビューター @w2or3w さんの記事
静岡県オープンデータカタログ(csv)からCOVID-19対策サイトのデータ(json)に変換して取得するAPI
https://qiita.com/w2or3w/items/55ff3159aea0c7a9e096

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 7 - セキュリティ対策

記事一覧

概要

EC2上に構築したサーバを運用するうえで必要となるセキュリティ対策です。

前回までの記事でサーバ構築が完了していることが前提です。

セキュリティグループ

AWSのセキュリティグループがファイアウォールの役割を果たします。

外部公開が必要となるポートだけをインバウンドルールに追加します(不要なポートを開けないように)。

インバウンドルール

タイプ プロトコル ポート範囲 ソース
SSH TCP 22 0.0.0.0/0
HTTP TCP 80 0.0.0.0/0
HTTPS TCP 443 0.0.0.0/0
SMTP TCP 25 0.0.0.0/0
SMTPS TCP 465 0.0.0.0/0
IMAPS TCP 993 0.0.0.0/0
POP3S TCP 995 0.0.0.0/0

SMTPSが有効でも、25/tcpを許可しないとメール送受信ができないので注意してください。

Fail2ban

/var/logにある様々なログを監視し、アクセス失敗(多くは不正アクセス試行)を独自に管理しBan/Unbanします。

以下の設定内容はこのような内容になります。

  • 10分間で5回アクセス失敗したIPアドレスを10分間Banする
  • 24時間に5回BanされたIPアドレスを7日間Banする(再犯対策)
  • サーバ自身と管理者PC(例では123.456.78.90)は監視の対象外とする

/etc/fail2ban/jail.local

[DEFAULT]
backend  = systemd
ignoreip = 127.0.0.0/8 10.0.0.0/16 123.456.78.90

destemail = root@example1.com
sender    = fail2ban@example1.com

banaction = firewallcmd-ipset

bantime  = 600
findtime = 600
maxretry = 5

[sshd]
enabled = true

[postfix-sasl]
enabled = true

[dovecot]
enabled = true

[recidive]
enabled  = true
bantime  = 604800
findtime = 86400
maxretry = 5

Logwatch対応

LogwatchがFail2banのログを拾ってくれない問題に対処します。

/usr/share/logwatch/scripts/services/fail2ban

    } elsif ( my ($Service,$Action,$Host) = ($ThisLine =~ m/WARNING:?\s\[?(.*?)[]:]?\s(Ban|Unban)[^\.]* (\S+)/)) {

↓ このように変更します。

    } elsif ( my ($Service,$Action,$Host) = ($ThisLine =~ m/(?:WARNING|NOTICE):?\s+\[?(.*?)[]:]?\s(Ban|Unban)[^\.]* (\S+)/)) {"

具体的にはWARNING:?\s(?:WARNING|NOTICE):?\s+に変更しています。

Fail2ban起動

systemctl start fail2ban

ステータス確認

fail2ban-client status sshd

以下のように表示されます。

Status for the jail: sshd
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     2118
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 4
   |- Total banned:     59
   `- Banned IP list:   27.78.14.83 70.37.84.164 193.142.146.21 116.105.216.179

Logwatchの例

Logwatchからのメールでは以下のように記載されます。

--------------------- fail2ban-messages Begin ------------------------ 

 Banned services with Fail2Ban:              Bans:Unbans
    sshd:                                       [ 12:6  ]

---------------------- fail2ban-messages End -------------------------

サーバでの対応

以下はここまでの記事で設定済みです。

  • 定期的にパッケージのアップデートを行う(yum update
  • 必要のないポートを外部公開しない(セキュリティグループ)
  • SSHへのパスワードアクセスを禁止(公開鍵認証のみ)
  • FTPサーバを起動しない(Git over SSHやSFTPで代用可能)※
  • MySQL (3306/tcp) を外部に公開しない
  • 使えるところはすべてSSL通信を行う(SSH, Nginx, Postfix, Dovecot)
  • SSL/TLSの古いプロトコルや暗号スイートを使用しない(Nginx)
  • 非公開領域にはBASIC認証やアプリケーションでの認証を設定(Nginx)
  • 古いバージョンのPHPを使用しない(新規構築なら最低でもPHP 7.3系)
  • SPAM対策を行う(Postfix / SMTP認証、RBLデータベース参照)
  • 不正アクセスを対策する(Fail2ban)
  • ログに目を通す(Logwatch)

※あるクライアントが自社サーバの脆弱性診断を業者に依頼したところ、FTP関連の脆弱性が多くを占めていました。

さらなる対策

  • ファイルの改竄検知を行う(AuditやTripwire等)
  • ウィルス検知を行う(ClamAV等)
  • SPAM検知を強化する(SpamAssassin等)
  • 独自に設定したアラートをSlack等へ飛ばす
  • Webアプリケーションの脆弱性に気を配る(WordPress等)

他にもできることは多いですが、割愛(もしくは今後記事化)します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 6 - PostfixとDovecot (SSL対応)

概要

PostfixとDovecotでメールサーバを構築します。SSL化とSMTP認証を取り入れます。

前回までの作業が完了している前提です。

Postfix

メインドメインがexample1.comで、こちらでSSL証明書を取得します。

メールクライアントのサーバ設定では、~@example2.comであっても受信/送信サーバ共にmail.example1.comを指定します。

SMTP認証にはDovecotを使います。

/etc/postfix/main.cf

myhostname = mail.example1.com
mydomain = example1.com
myorigin = $mydomain

inet_interfaces = all
inet_protocols = ipv4

home_mailbox = Maildir/

smtpd_banner = $myhostname ESMTP unknown

# virtual_alias
#
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_alias_domains = example2.com

# spam block - 2020.04.07
#
disable_vrfy_command = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes

smtpd_helo_restrictions =
    permit_mynetworks,
    reject_non_fqdn_hostname,
    reject_invalid_hostname,
    permit

smtpd_error_sleep_time = 1s
smtpd_soft_error_limit = 5
smtpd_hard_error_limit = 10

smtpd_client_message_rate_limit = 6
smtpd_client_recipient_rate_limit = 6

# smtp-auth
#
smtpd_sasl_auth_enable = yes 
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_recipient_restrictions =
    permit_sasl_authenticated
    reject_invalid_hostname,
    reject_non_fqdn_hostname,
    reject_non_fqdn_sender,
    reject_non_fqdn_recipient,
    reject_unknown_sender_domain,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    reject_rbl_client all.rbl.jp,
    reject_rbl_client bl.spamcop.net,
    reject_rbl_client zen.spamhaus.org,
    permit
broken_sasl_auth_clients = yes

# tls/ssl
#
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example1.com/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/mail.example1.com/privkey.pem
smtp_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache
smtpd_tls_session_cache_timeout = 3600s

TLS有効化

SMTPSを使うための設定を行います。必要な部分をコメントインします。

/etc/postfix/master.cf

smtps     inet  n       -       n       -       -       smtpd
#  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_reject_unlisted_recipient=no

SSL証明書取得

メールサーバmail.example1.com用にLet's Ecnryptの証明書を取得します。Nginxと同様の手順です。

80/tcpで待ち受ける必要があるため、Nginxの設定で443/tcpにリダイレクトする設定があればコメントアウトしておきます。

certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d mail.example1.com -m root@example1.com

エイリアス設定

root宛のメールを別ユーザーへ転送します。

/etc/alises

root:           example1

上記設定を反映させるためには以下作業が必要です。

newaliases

バーチャルドメイン設定

メインドメインであるexample1.comはバーチャルドメインにできません。

/etc/postfix/virtual (作成)

example2.com            anything
info@example2.com       example2

上記ファイルを更新したらpostmapが必要です。

postmap virtual

Dovecot

使用プロトコルとポートを指定します。実際使うのはIMAPSとPOP3Sですが、ここではimap pop3と書きます。

/etc/dovecot/dovecot.conf

#protocols = imap pop3 lmtp
protocols = imap pop3

#listen = *, ::
listen = *

認証関連

標準的なメーラーの初期設定、自動認識に対応させます。

/etc/dovecot/conf.d/10-auth.conf

#disable_plaintext_auth = yes
disable_plaintext_auth = no

# auth_mechanisms = plain
auth_mechanisms = plain login

メールボックス

mbox形式からMaildir形式に変更します。

/etc/dovecot/conf.d/10-mail.conf

#mail_location = 
mail_location = maildir:~/Maildir

プロトコルとポート

IMAPとPOP3は使わず、SSL経由のIMAPSとPOP3Sを使うよう設定します。

PostfixもSMTPSを使うので、これでメール関連はすべてSSL通信に対応できます。

/etc/dovecot/conf.d/10-master.conf

service imap-login {
  inet_listener imap {
    #port = 143
    port = 0
  }
  inet_listener imaps {
    #port = 993
    port = 993
    #ssl = yes
    ssl = yes
  }

service pop3-login {
  inet_listener pop3 {
    #port = 110
    port = 0
  }
  inet_listener pop3s {
    #port = 995
    port = 995
    #ssl = yes
    ssl = yes
  }

  # Postfix smtp-auth
  #unix_listener /var/spool/postfix/private/auth {
  #  mode = 0666
  #}
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

SSL設定

Let's Ecnryptで取得したメールサーバ用の証明書を指定します。

/etc/dovecot/conf.d/10-ssl.conf

#ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
#ssl_key = </etc/pki/dovecot/private/dovecot.pem
ssl_cert = </etc/letsencrypt/live/mail.example1.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example1.com/privkey.pem

PostfixとDovecotを起動します。

systemctl start postfix dovecot
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 6 - PostfixとDovecot (SSL対応とSPAM対策)

記事一覧

概要

PostfixとDovecotでメールサーバを構築します。SSL化とSMTP認証、SPAM対策を取り入れます。

Postfix

メインドメインがexample1.comで、こちらでSSL証明書を取得します。

メールクライアントの送受信サーバ設定では、example2.comのメールアカウントであってもmail.example1.comを指定します。

SMTP認証にはDovecotを使います。SPAM対策のためRBLを参照するよう設定します。

/etc/postfix/main.cf

myhostname = mail.example1.com
mydomain = example1.com
myorigin = $mydomain

inet_interfaces = all
inet_protocols = ipv4

home_mailbox = Maildir/

smtpd_banner = $myhostname ESMTP unknown

# virtual_alias
#
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_alias_domains = example2.com

# spam block - 2020.04.07
#
disable_vrfy_command = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes

smtpd_helo_restrictions =
    permit_mynetworks,
    reject_non_fqdn_hostname,
    reject_invalid_hostname,
    permit

smtpd_error_sleep_time = 1s
smtpd_soft_error_limit = 5
smtpd_hard_error_limit = 10

smtpd_client_message_rate_limit = 6
smtpd_client_recipient_rate_limit = 6

# smtp-auth
#
smtpd_sasl_auth_enable = yes 
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_recipient_restrictions =
    permit_sasl_authenticated
    reject_invalid_hostname,
    reject_non_fqdn_hostname,
    reject_non_fqdn_sender,
    reject_non_fqdn_recipient,
    reject_unknown_sender_domain,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    reject_rbl_client all.rbl.jp,
    reject_rbl_client bl.spamcop.net,
    reject_rbl_client zen.spamhaus.org,
    permit
broken_sasl_auth_clients = yes

# tls/ssl
#
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example1.com/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/mail.example1.com/privkey.pem
smtp_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache
smtpd_tls_session_cache_timeout = 3600s

TLS有効化

SMTPSを使うための設定を行います。必要な部分をコメントインします。

/etc/postfix/master.cf

smtps     inet  n       -       n       -       -       smtpd
#  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_reject_unlisted_recipient=no

SSL証明書取得

メールサーバmail.example1.com用にLet's Ecnryptの証明書を取得します。Nginxと同様の手順です。

80/tcpで待ち受ける必要があるため、Nginxの設定で443/tcpにリダイレクトする設定があればコメントアウトしておきます。

certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d mail.example1.com -m root@example1.com

エイリアス設定

root宛のメールを別ユーザーへ転送します。

/etc/alises

root:           example1

上記設定を反映させるためには以下作業が必要です。

newaliases

バーチャルドメイン設定

メインドメインであるexample1.comはバーチャルドメインにできません。

/etc/postfix/virtual (作成)

example2.com            anything
info@example2.com       example2

上記ファイルを更新したらpostmapが必要です。

postmap virtual

Dovecot

使用プロトコルとポートを指定します。実際使うのはIMAPSとPOP3Sですが、ここではimap pop3と書きます。

/etc/dovecot/dovecot.conf

#protocols = imap pop3 lmtp
protocols = imap pop3

#listen = *, ::
listen = *

認証関連

標準的なメーラーの初期設定、自動認識に対応させます。

/etc/dovecot/conf.d/10-auth.conf

#disable_plaintext_auth = yes
disable_plaintext_auth = no

# auth_mechanisms = plain
auth_mechanisms = plain login

メールボックス

mbox形式からMaildir形式に変更します。

/etc/dovecot/conf.d/10-mail.conf

#mail_location = 
mail_location = maildir:~/Maildir

プロトコルとポート

IMAPとPOP3は使わず、SSL経由のIMAPSとPOP3Sを使うよう設定します。

PostfixもSMTPSを使うので、これでメール関連はすべてSSL通信に対応できます。

/etc/dovecot/conf.d/10-master.conf

service imap-login {
  inet_listener imap {
    #port = 143
    port = 0
  }
  inet_listener imaps {
    #port = 993
    port = 993
    #ssl = yes
    ssl = yes
  }

service pop3-login {
  inet_listener pop3 {
    #port = 110
    port = 0
  }
  inet_listener pop3s {
    #port = 995
    port = 995
    #ssl = yes
    ssl = yes
  }

  # Postfix smtp-auth
  #unix_listener /var/spool/postfix/private/auth {
  #  mode = 0666
  #}
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

SSL設定

Let's Ecnryptで取得したメールサーバ用の証明書を指定します。

/etc/dovecot/conf.d/10-ssl.conf

#ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
#ssl_key = </etc/pki/dovecot/private/dovecot.pem
ssl_cert = </etc/letsencrypt/live/mail.example1.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example1.com/privkey.pem

PostfixとDovecotを起動します。

systemctl start postfix dovecot
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 5 - MySQL + phpMyAdmin

記事一覧

概要

Amazon Linux 2を含むRHEL (RedHat Enterprise Linux) 系のOSでは、MySQLから派生したMariaDBがデフォルトで採用されています。これについての経緯は省きますが、Oracleによる買収後も従来通りに利用可能であるため、互換性重視でMySQLを採用します。

MySQLはサーバリソースを食うため、アクセス数や負荷が増えたらRDSへのリプレースも検討した方が良いでしょう。

MySQL

デフォルトのキャラセットを変更します。

今回、特にパフォーマンスチューニングは行いません。

/etc/my.cnf

character-set-server = utf8

以下は最初に行う儀式的なコマンドです。root@localhostのパスワードを変更します。

mysql_secure_installation

MySQLを停止します。

systemctl stop mysqld

デフォルトでは難解なパスワードしか認められずエラーになるのでこれを回避する設定です。

mysql -u root -p

> set global validate_password_policy=LOW;
> quit

改めてMySQLを起動します。

systemctl start mysqld

phpMyAdmin

設定ファイルのサンプルをコピーして設定ファイルを作成します。

cd /var/www/html/www.example1.com/public_html/phpmyadmin
cp config.sample.inc.php config.inc.php

/var/www/html/dev.netartz.com/public_html/config.inc.php

phpMyAdminはすぐにセッションが切れてしまい再ログインが手間なので、セッションが24時間持続するようにします。

$cfg['blowfish_secret'] = '{記号を含む長いランダムな文字列}'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */

$cfg['LoginCookieValidity'] = 86400;

phpMyAdminにログインしてphpmyadminデータベースを作成し、SQL文をインポートします。

mysql -uroot -p phpmyadmin < ./sql/create_tables.sql
mkdir tmp
chmod 777 tmp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 4 - Nginx + Let's Ecnrypt + PHP-FPM編 (脆弱性診断「A+」を狙う)

記事一覧

概要

Nginx、SSL (Let's Ecnrypt)、PHP-FPMの設定を行います。また脆弱性診断で最高の「A+」を狙います。

Apacheを設定済みの場合は削除するか、systemctl disable httpdしておいてください。削除を推奨します。

脆弱性診断結果

結論から書いてしまいますが、以下の設定でSSL Labsの脆弱性診断を行ったところ最高の「A+」となりました。

20200413_131134.png

当初は「A」でしたが、設定を見直して「A+」を達成できました。以下の通りの設定で「執筆時点では」「A+」になるはずです。

Nginx

エラー画面でバージョンを吐かないようにし、SSLに関するデフォルト設定を書いておきます。

ここが脆弱性診断「A+」のキモとなります。

注意:以下はserverではなくhttpディレクティブに追加してください。

/etc/nginx/nginx.conf

    server_tokens       off;

    ssl_protocols TLSv1.2; 
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout  1d;
    ssl_ciphers HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA:!3DES:!RC4:!DH;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

Amazon Linux 2のOpenSSLが1.0.2kなので、OpenSSL 1.1.1以降が必要となるTLSv1.3は設定できません。

バーチャルホスト

バーチャルホスト用に公開ディレクトリを作成しておきます。

Apacheとの互換を維持するため、var/wwwで始まりpublic_htmlで終わるようにしています。

mkdir -p /var/www/html/www.example1.com/public_html
mkdir -p /var/www/html/www.example2.com/public_html
chown -R nginx. /var/www/html

普段は常時SSL設定で良いのですが、Let's Ecnryptのcertbotが80/tcpへの接続を求めるためreturnの行はコメントアウトで切り替えて使います。

301リダイレクトしている80/tcpのディレクティブにもroot行があるのはそのためです。

また設定ファイル内でドメイン正規化 (wwwありに統一) を行います。Apacheの.htaccessが使えないためです。

/etc/nginx/conf.d/www.example1.com.conf

server {
    listen      80;
    listen      443 ssl http2;
    server_name example1.com;
    return 301  https://www.$host$request_uri;

   ssl_certificate     "/etc/letsencrypt/live/example1.com/fullchain.pem";
   ssl_certificate_key "/etc/letsencrypt/live/example1.com/privkey.pem";

   include /etc/nginx/default.d/*.conf;
}

server {
    listen      80;
    server_name www.example1.com;
    root        /var/www/html/www.example1.com/public_html;
    return 301  https://$host$request_uri;
}

server {
   listen       443 ssl http2;
   server_name  www.example1.com;
   root         /var/www/html/www.example1.com/public_html;

   ssl_certificate     "/etc/letsencrypt/live/www.example1.com/fullchain.pem";
   ssl_certificate_key "/etc/letsencrypt/live/www.example1.com/privkey.pem";

   include /etc/nginx/default.d/*.conf;

   location / {
   }

   error_page 404 /404.html;
       location = /40x.html {
   }

   error_page 500 502 503 504 /50x.html;
       location = /50x.html {
   }
}

Aapcheの例と同じく、www.example2.comはBASIC認証を設定します。

生成サービスを利用して暗号化パスワードを生成しておきます。

/var/www/html/www.example2.com/.htpasswd

{ユーザー名}:{暗号化されたパスワード}

Apacheと異なり.htaccessが使えないため、設定ファイル内にBASIC認証の設定を記述します。

/etc/nginx/conf.d/www.example2.com.conf

server {
   listen      80;
   server_name www.example2.com;
   root        /var/www/html/www.example2.com/public_html;
   return 301  https://$host$request_uri;
}

server {
  listen       443 ssl http2;
  server_name  www.example2.com;
  root         /var/www/html/www.example2.com/public_html;

  ssl_certificate     "/etc/letsencrypt/live/www.example2.com/fullchain.pem";
  ssl_certificate_key "/etc/letsencrypt/live/www.example2.com/privkey.pem";

  include /etc/nginx/default.d/*.conf;

  location / {
      auth_basic "Enter your ID and Password";
      auth_basic_user_file /var/www/html/www.example2.com/.htpasswd;
  }

  error_page 404 /404.html;
      location = /40x.html {
  }

  error_page 500 502 503 504 /50x.html;
      location = /50x.html {
  }
}

SSL証明書取得

Let's Ecnryptの証明書を取得します。

certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d www.example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example2.com/public_html/ -d www.example2.com -m root@example1.com

証明書の自動更新

月曜と木曜の4時0分に自動更新されるようにします。

Let's Ecnrypt証明書の期限は3ヶ月です。更新は週に5回までの制限があります。

crontab -e
0 4 * * 1,4  certbot renew --post-hook "systemctl restart nginx php-fpm postfix dovecot"

CAAレコード追加

脆弱性診断への対策のため、DNSサーバ (ネームサーバ) でCAAレコードを追加します。

できない場合やわからない場合は読み飛ばしてください。ただし診断結果に「若干」影響する可能性があります。

example1.com. 3600 IN CAA 0 issue "letsencrypt.org"
example2com. 3600 IN CAA 0 issue "letsencrypt.org"

PHP-FPM

まずはPHPそのものの設定を行います。環境によって調整してください。

/etc/php.ini

;expose_php = On
expose_php = Off

; max_execution_time = 30
max_execution_time = 120

; memory_limit = 128M
memory_limit = 256M

; post_max_size = 8M
post_max_size = 128M

; upload_max_filesize = 2M
upload_max_filesize = 128M

;date.timezone =
date.timezone = Asia/Tokyo

; session.gc_maxlifetime = 1440
session.gc_maxlifetime = 86400

チューニング

実行ユーザー/グループの設定とパフォーマンスチューニングの設定を行います。環境によって調整してください。

/etc/php-fpm.d/www.conf

; user = apache
user = nginx

; group = apache
group = nginx

;listen.owner = nobody
;listen.group = nobody
;listen.mode = 0660
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

; pm.max_children = 50
pm.max_children = 30

; pm.start_servers = 5
pm.start_servers = 10

; pm.min_spare_servers = 5
pm.min_spare_servers = 10

; pm.max_spare_servers = 35
pm.max_spare_servers = 25

;pm.max_requests = 500
pm.max_requests = 30

NginxとPHP-FPMを再起動します。これは設定を変更するたびに行います。

systemctl restart nginx php-fpm

Composer

Laravel等で使用するcomposerをインストールしておきます。

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

トラブルシューティング

/var/lib/php内のセッションやキャッシュ用ディレクトリの所有グループがapacheになっている場合の対処法です。

chgrp nginx /var/lib/php/opcache
chgrp nginx /var/lib/php/session
chgrp nginx /var/lib/php/wsdlcache

セッションについては中のファイルがapache:apacheな状態になっている場合があるので修正。

chown nginx. /var/lib/php/sesion/*
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 3 - Apache + Let's Ecnrypt + PHP-FPM編 (Nginxに変更)

記事一覧

概要

ウェブサーバをApacheからNginxに変更したので本作業は不要です。
Apacheで構築したい場合を除き、本記事は読み飛ばしてください。

Apache (Event MPM) とSSL (Let's Ecnrypt)、PHP-FPMを設定します。

Apahce

管理者メールアドレス等、お決まりの項目は除いて説明します。以下は気をつける部分です。

/etc/httpd/conf/httpd.conf

<Directory "/var/www/html">
...

#Options Indexes FollowSymLinks
Options FollowSymLinks ExecCGI MultiViews

#AllowOverride None
AllowOverride All
#AddHandler cgi-script .cgi
AddHandler cgi-script .cgi

バーチャルホスト

バーチャルホスト用に公開ディレクトリを作成しておきます。

mkdir -p /var/www/html/www.example1.com/public_html
mkdir -p /var/www/html/www.example2.com/public_html
chown -R apache. /var/www/html

Let's Ecnryptが80/tcpを使用するため、それを待ち受ける仮設定をしておきます。

/etc/httpd/conf.d/vhosts.conf

<VirtualHost *:80>
  ServerName  www.example1.com
  ServerAlias example1.com
  DocumentRoot /var/www/www.example1.com/public_html
  CustomLog logs/www.example1.com.access_log combined
  ErrorLog  logs/www.example1.com.error_log
</VirtualHost>

<VirtualHost *:80>
  ServerName  www.example2.com
  DocumentRoot /var/www/www.example2.com/public_html
  CustomLog logs/www.example2.com.access_log combined
  ErrorLog  logs/www.example2.com.error_log
</VirtualHost>

Apacheを起動しておきます。

systemctl start httpd

SSL証明書取得

Let's Ecnryptの証明書を取得します。

certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example1.com/public_html/ -d www.example1.com -m root@example1.com
certbot certonly --webroot -w /var/www/html/www.example2.com/public_html/ -d www.example2.com -m root@example2.com

証明書の自動更新

月曜と木曜の4時0分に自動更新されるようにします。

Let's Ecnrypt証明書の期限は3ヶ月です。更新は週に5回までの制限があります。

crontab -e
0 4 * * 1,4  certbot renew --post-hook "systemctl restart httpd php-fpm postfix dovecot"

バーチャルホスト (SSL対応)

証明書取得後のバーチャルホスト設定を行います。常時SSL化も行います。

ドメイン正規化 (wwwあり/なし) は.htaccessで行う想定です。

/etc/httpd/conf.d/vhosts.conf

<VirtualHost *:80>
  ServerName  www.example1.com
  ServerAlias example1.com
  Redirect permanent / https://www.example1.com/
</VirtualHost>

<VirtualHost *:80>
  ServerName  www.example2.com
  DocumentRoot /var/www/html/www.example2.com/public_html
  # Redirect permanent / https://www.example2.com/
</VirtualHost>

<VirtualHost *:443>
  ServerName  www.example1.com
  ServerAlias example1.com
  DocumentRoot /var/www/www.example1.com/public_html
  CustomLog logs/www.example1.com.access_log combined
  ErrorLog  logs/www.example1.com.error_log
  SSLEngine on
  SSLCertificateFile    /etc/letsencrypt/live/www.example1.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.example1.com/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/www.example1.com/chain.pem
</VirtualHost>

<VirtualHost *:443>
  ServerName  www.example2.com
  DocumentRoot /var/www/html/www.example2.com/public_html
  CustomLog logs/www.example2.com.access_log combined
  ErrorLog  logs/www.example2.com.error_log
  SSLEngine on
  SSLCertificateFile    /etc/letsencrypt/live/www.example2.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.example2.com/privkey.pem
  SSLCertificateChainFile /etc/letsencrypt/live/www.example2.com/chain.pem
</VirtualHost>

CAAレコード追加

脆弱性診断への対策のため、DNSサーバ (ネームサーバ) でCAAレコードを追加します。

できない場合やわからない場合は読み飛ばしてください。ただし診断結果に「若干」影響する可能性があります。

example1.com. 3600 IN CAA 0 issue "letsencrypt.org"
example2com. 3600 IN CAA 0 issue "letsencrypt.org"

MPM変更

ApacheのMPMを古いPreforkからEventに切り替えます。

# LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

LoadModule mpm_event_module modules/mod_mpm_event.so

チューニング

パフォーマンスチューニング設定です。環境に合わせて調整してください。

/etc/httpd/conf.d/performance.conf

# HostnameLookups off

<IfModule mpm_event_module>
  StartServers 2
  MinSpareThreads 2
  MaxSpareThreads 12
  ThreadsPerChild 2
  MaxRequestWorkers 36
  MaxRequestsPerChild 0
</IfModule>

モジュール版PHP廃止

PHP-FPM (FastCGI) を動かすため、モジュール版を使用しないよう設定します。

/etc/httpd/conf.d/php.conf

    # <FilesMatch \.(php|phar)$>
        # SetHandler application/x-httpd-php
    # </FilesMatch>

BASIC認証

www.example2.com全体をBASIC認証で保護する想定です。

生成サービスを利用してパスワードファイルを作成しておきます。

/var/www/html/www.example2.com/.htpasswd

{ユーザー名}:{暗号化されたパスワード}

.htaccessでBASIC認証を有効にします。

/var/www/html/www.example2.com/public_html/.htaccess

AuthType Basic
AuthName "Enter your ID and password"
AuthUserFile /var/www/html/www.example2.com/.htpasswd
require valid-user
chown -R dev. /var/www/html/www.example2.com

PHP-FPM

まずはPHPそのものの設定を行います。環境によって調整してください。

/etc/php.ini

;expose_php = On
expose_php = Off

; max_execution_time = 30
max_execution_time = 120

; memory_limit = 128M
memory_limit = 256M

; post_max_size = 8M
post_max_size = 128M

; upload_max_filesize = 2M
upload_max_filesize = 128M

;date.timezone =
date.timezone = Asia/Tokyo

; session.gc_maxlifetime = 1440
session.gc_maxlifetime = 86400

チューニング

パフォーマンスチューニングの設定を行います。環境によって調整してください。

/etc/php-fpm.d/www.conf

; pm.max_children = 50
pm.max_children = 30

; pm.start_servers = 5
pm.start_servers = 10

; pm.min_spare_servers = 5
pm.min_spare_servers = 10

; pm.max_spare_servers = 35
pm.max_spare_servers = 25

;pm.max_requests = 500
pm.max_requests = 30

ApacheとPHP-FPMを再起動します。

systemctl restart httpd php-fpm

Composer

Laravel等で使用するcomposerをインストールしておきます。

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 2 - 各種インストール編

記事一覧

概要

サーバアプリケーションを中心に各種インストールを行います。

設定は次回以降の記事で行います。

ApahceとSSL

ウェブサーバをApacheからNginxに変更したためインストール不要です。飛ばしてください。

SSL証明書取得用にLet's Ecnryptのcertbotもインストールします。

yum install httpd mod_ssl certbot
systemctl enable httpd

NginxとSSL

EPELにもNginxはありますが、AWSなのでamazon-linux-extraからインストールします。

SSL証明書取得用にLet's Ecnryptのcertbotもインストールします。

amazon-linux-extras install nginx1
yum install certbot
systemctl enable nginx

PHP-FPMとモジュール

Apache Event MPM Nginxで動かすためモジュール版PHPは使いません。

amazon-linux-extras install php7.3
yum install php-opcache php-apcu php-mbstring php-xml php-xmlrpc php-gd php-pecl-mcrypt php-pecl-imagick-devel php-pecl-imagick php-pecl-zip ImageMagick-devel
systemctl enable php-fpm

MySQL

RHEL系でデフォルトのMariaDBは使いません。コミュニティのリポジトリからMySQLをインストールします。

yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
yum-config-manager --disable mysql80-community
yum-config-manager --enable mysql57-community
yum install mysql-community-server
systemctl enable mysqld

phpMyAdmin

EPELからのyumインストールは依存関係で失敗するため公式サイトのソースからインストールします。

cd /var/www/html/dev.netartz.com/public_html
wget https://files.phpmyadmin.net/phpMyAdmin/5.0.2/phpMyAdmin-5.0.2-all-languages.tar.gz
tar zxf phpMyAdmin-5.0.2-all-languages.tar.gz
rm -f phpMyAdmin-5.0.2-all-languages.tar.gz
mv phpMyAdmin-5.0.2-all-languages phpmyadmin

Postfix

インストールしなくても最初から入っています。有効化しておきます。

systemctl enable postfix

Dovecot

インストールして有効化します。

yum install dovecot
systemctl enable dovecot

Node.js

EPELのNode.jsは6系と古いので、NodeSourceからLTSな12系をインストールします。

curl -sL https://rpm.nodesource.com/setup_12.x | bash -
yum install nodejs

有効化して使用する場合は以下を実行しておきます。

systemctl enable nodejs
systemctl start nodejs

Docker

ローカルで使用することが多いですが、自分の場合はサーバ側にも必要だったため一応インストールおきます。

不要な場合はインストールしなくてOKです。インストールだけして有効化しない手もあります。

yum install docker

有効化し使用する場合は以下も実行しておきます。

systemctl enable docker
systemctl start docker

Fail2ban / Logwatch

ログ監視とセキュリティ対策のためにインストールします。

yum install fail2ban logwatch
systemctl enable fail2ban
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 - Amazon Linux 2 準備編

概要

本記事ではAmazon Linux 2の初期設定を行います。

AWS EC2インスタンス作成までが完了している前提です。

ユーザーec2-userでSSHログインして作業します (ダウンロードした鍵ファイルを使用)。

文字コードとキーボード

日本の環境に合わせます。

sudo timedatectl set-timezone Asia/Tokyo
sudo localectl set-locale LANG=ja_JP.UTF-8
sudo localectl set-keymap jp106

設定反映のため一旦切断して再接続します。

反映を確認

設定が正しく反映されているか確認します。

date

以下のように日本時間が日本語で表示されていればOKです。

2020年  4月 14日 火曜日 10:03:39 JST

初回アップデート

パッケージのアップデートと再起動を行っておきます。

sudo yum update
sudo reboot

ログイン関連

rootユーザーでSSHログインしたいので鍵ファイルの用意をします。

/root/.ssh/authorized_key

ssh rsaより前の部分が不要なので削除してから保存します。

sshdの設定

パスワード認証を排除します。

/etc/ssh/sshd_config

PermitRootLogin without-password

sshdを再起動します。

systemctl restart sshd

ec2-user切断後、rootでSSHログインします (ec2-userと同じ鍵ファイルを使用)。

ユーザー関連

新規ユーザー作成時のデフォルトを設定します。

useradd -D -s /sbin/nologin
mkdir -p /etc/skel/Maildir/{new,cur,tmp}
chmod -R 700 /etc/skel/Maildir/

シェルとVimの設定

  • デフォルトのエディタをVimにする
  • プロンプトのカラーを変更する
  • cpmvでワイルドカード*を使う際、ドットファイルがスルーされないようにする
  • ターミナルで [Ctrl + S] した際に応答しなくなる問題に対処する

~/.bashrc

alias vi='vim'

PS1='\[\033[32m\][\u@\h \W]\$\[\033[0m\] '

export EDITOR=vim

shopt -s dotglob
stty stop undef

設定の反映を行います。

source ~/.bashrc

EPELリポジトリ追加

標準で用意されていないパッケージをインストールするために、EPELリポジトリを使うよう設定します。

amazon-linux-extras install epel

ツール類インストール

今後の作業に必要となる各種ツール類をインストールします。

yum install git curl wget nmap finger htop jwhois

Vim環境設定(例)

個人の好み通りに設定すればOKです。わからなければ以下をコピペして使ってください。

ターミナル内でのコピペが不便なので、行番号は表示していません。:set nu:set nonuで切り替えられます。

~/.vimrc

set nocompatible

set encoding=utf-8
set fileencodings=iso-2022-jp,euc-jp,sjis,utf-8
set fileformats=unix,dos,mac

set runtimepath+=~/.vim/dein/repos/github.com/Shougo/dein.vim

call dein#begin(expand('~/.vim/dein'))

call dein#add('Shougo/dein.vim')
call dein#add('scrooloose/nerdcommenter')
call dein#add('chriskempson/vim-tomorrow-theme')

call dein#end()

if dein#check_install()
  call dein#install()
endif

filetype plugin indent on

syntax on
autocmd BufRead,BufNewFile /etc/php.ini set syntax=dosini
autocmd BufRead,BufNewFile /etc/php-fpm.conf set syntax=dosini
autocmd BufRead,BufNewFile /etc/php-fpm.d/*.conf set syntax=dosini

if dein#check_install()
  call dein#install()
endif

set t_Co=256

colorscheme Tomorrow-Night-Bright

set autoindent
" set number
set nobackup
set paste

set pastetoggle=<C-e>

let NERDSpaceDelims = 1
nmap ,, <Plug>NERDCommenterToggle
vmap ,, <Plug>NERDCommenterToggle

プラグイン管理ツールのdeinをインストールします。

mkdir -p ~/.vim/dein/repos/github.com/Shougo/dein.vim
git clone https://github.com/Shougo/dein.vim.git ~/.vim/dein/repos/github.com/Shougo/dein.vim

次回Vim起動時にdeinによるプラグインのインストールが行われます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でサーバを構築 1 - Amazon Linux 2 準備編

記事一覧

概要

本記事ではAmazon Linux 2の初期設定を行います。

ユーザーec2-userでSSHログインして作業します (ダウンロードした鍵ファイルを使用)。

文字コードとキーボード

日本の環境に合わせます。

sudo timedatectl set-timezone Asia/Tokyo
sudo localectl set-locale LANG=ja_JP.UTF-8
sudo localectl set-keymap jp106

設定反映のため一旦切断して再接続します。

反映を確認

設定が正しく反映されているか確認します。

date

以下のように日本時間が日本語で表示されていればOKです。

2020年  4月 14日 火曜日 10:03:39 JST

初回アップデート

パッケージのアップデートと再起動を行っておきます。

sudo yum update
sudo reboot

ログイン関連

rootユーザーでSSHログインしたいので鍵ファイルの用意をします。

/root/.ssh/authorized_key

ssh rsaより前の部分が不要なので削除してから保存します。

sshdの設定

パスワード認証を排除します。

/etc/ssh/sshd_config

PermitRootLogin without-password

sshdを再起動します。

systemctl restart sshd

ec2-user切断後、rootでSSHログインします (ec2-userと同じ鍵ファイルを使用)。

ユーザー関連

新規ユーザー作成時のデフォルトを設定します。

useradd -D -s /sbin/nologin
mkdir -p /etc/skel/Maildir/{new,cur,tmp}
chmod -R 700 /etc/skel/Maildir/

シェルとVimの設定

  • デフォルトのエディタをVimにする
  • プロンプトのカラーを変更する
  • cpmvでワイルドカード*を使う際、ドットファイルがスルーされないようにする
  • ターミナルで [Ctrl + S] した際に応答しなくなる問題に対処する

~/.bashrc

alias vi='vim'

PS1='\[\033[32m\][\u@\h \W]\$\[\033[0m\] '

export EDITOR=vim

shopt -s dotglob
stty stop undef

設定の反映を行います。

source ~/.bashrc

EPELリポジトリ追加

標準で用意されていないパッケージをインストールするために、EPELリポジトリを使うよう設定します。

amazon-linux-extras install epel

ツール類インストール

今後の作業に必要となる各種ツール類をインストールします。

yum install git curl wget nmap finger htop jwhois

Vim環境設定(例)

個人の好み通りに設定すればOKです。わからなければ以下をコピペして使ってください。

ターミナル内でのコピペが不便なので、行番号は表示していません。:set nu:set nonuで切り替えられます。

~/.vimrc

set nocompatible

set encoding=utf-8
set fileencodings=iso-2022-jp,euc-jp,sjis,utf-8
set fileformats=unix,dos,mac

set runtimepath+=~/.vim/dein/repos/github.com/Shougo/dein.vim

call dein#begin(expand('~/.vim/dein'))

call dein#add('Shougo/dein.vim')
call dein#add('scrooloose/nerdcommenter')
call dein#add('chriskempson/vim-tomorrow-theme')

call dein#end()

if dein#check_install()
  call dein#install()
endif

filetype plugin indent on

syntax on
autocmd BufRead,BufNewFile /etc/php.ini set syntax=dosini
autocmd BufRead,BufNewFile /etc/php-fpm.conf set syntax=dosini
autocmd BufRead,BufNewFile /etc/php-fpm.d/*.conf set syntax=dosini

if dein#check_install()
  call dein#install()
endif

set t_Co=256

colorscheme Tomorrow-Night-Bright

set autoindent
" set number
set nobackup
set paste

set pastetoggle=<C-e>

let NERDSpaceDelims = 1
nmap ,, <Plug>NERDCommenterToggle
vmap ,, <Plug>NERDCommenterToggle

プラグイン管理ツールのdeinをインストールします。

mkdir -p ~/.vim/dein/repos/github.com/Shougo/dein.vim
git clone https://github.com/Shougo/dein.vim.git ~/.vim/dein/repos/github.com/Shougo/dein.vim

次回Vim起動時にdeinによるプラグインのインストールが行われます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む