4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GitHub Actions×Microsoft Defender for DevOpsによるセキュアなCI/CD環境の構築

Posted at

はじめに

DevOpsにより高速な開発と安定したリリースが実現できますが、デプロイからフローが自動化されていることでサイバー攻撃のリスクが高まるため、セキュリティを考慮することがますます重要になっています。そこで、本記事ではGitHub Actionsを用いたDevOpsの実現とMicrosoft Defender for DevOpsを用いてセキュリティ対策を実施します。本記事は、以下の公式ドキュメントのクイックスタートをベースにアレンジした内容になっております。細かい設定や手順については割愛させていただくので、必要であれば適宜ドキュメントをご覧ください。

DevOpsとは、開発と運用のプロセスを統合することによって、より迅速で効率的なソフトウェア開発を実現するための方法論のことを言います。

ファイルとフォルダ構成

本記事では、以下のようなシンプルな構成のWebアプリを用います。.env.gitignoreについては割愛します。

フォルダ構成
.
├─ .github
│  └─ workflows
│     ├─ codeql.yml
│     ├─ defender-for.yml
│     └─ devops.yml
├─ conf
│  └ default.conf
├─ src
│  ├ js
│  │  └ index.js
│  └ index.html
├─ .env
├─ .gitignore
└─ Dockerfile

このファイルは、CodeQLを使用してjavascriptコードを自動的に分析するために設定されています。

CodeQLは、静的コード解析ツールの一種で、バグ、脆弱性、セキュリティ上の問題などを自動的に検出することができます。また、プログラムの検証や脆弱性診断に利用されます。CodeQLを用いたコードスキャンを行うことで、クロスサイトスクリプティング(XSS)の検出、不正な正規表現の検出、エクスポートされた関数の脆弱性を検出、クロスオリジンリソース共有(CORS)の検出などが可能です。

.github/workflows/codeql.yml
# ワークフローの名前を"CodeQL"と指定しています。
name: "CodeQL"

# トリガーとなるイベントを設定します。
# この例では、pushとpull_requestの両方がmasterブランチに対して実行されます。
# さらに、定期的に実行されるようにscheduleが設定されており、cron式で毎週土曜日の9:16に実行されます。
on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]
  schedule:
    - cron: '16 9 * * 6'

# ジョブのリストを定義します。
jobs:
  # analyzeというジョブが定義されています。
  analyze:

    # ジョブの名前を設定します。
    name: Analyze

    # ジョブが実行される仮想マシンのオペレーティングシステムを指定します。
    # Ubuntuを使用しています。
    runs-on: ubuntu-latest

    # アクションが使用するGitHubリソースへのアクセス許可を設定します。
    permissions:
      actions: read
      contents: read
      security-events: write

    # 並列ジョブの設定を定義します。
    strategy:
      fail-fast: false

      # 異なる言語で同時に分析するために、CodeQLが使用する言語のリストを定義します。
      matrix:
        language: [ 'javascript' ]

    # このジョブで実行される一連のステップを定義します。
    steps:

      # リポジトリをチェックアウトします。
    - name: Checkout repository
      uses: actions/checkout@v3

      # CodeQLを使用するために必要な環境を設定します。
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: ${{ matrix.language }}

      # コンパイルされた言語の自動ビルドを試みます。
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2

      # CodeQLを使用してコードを分析します。
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      with:
        category: "/language:${{matrix.language}}"

このファイルは、Microsoft Defender for DevOpsを使用してGitHubリポジトリのセキュリティスキャンを自動化するためのワークフローです。このワークフローは、GitHubリポジトリのセキュリティスキャンを自動化し、Microsoft Defender for DevOpsによって生成された結果をSecurityタブにアップロードすることが目的です。

.github/workflows/defender-for-devops.yml
# ワークフローの名前を指定しています。
name: "Microsoft Defender For Devops"

# ワークフローのトリガーを定義しています。
# この場合、masterブランチにプッシュまたはプルリクエストが作成された場合
# または毎週日曜日の22:00にスケジュールされた実行がトリガーとなります。
on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]
  schedule:
    - cron: '22 0 * * 0'

# 実行されるジョブの定義を開始します。
jobs:

  # MSDOという名前のジョブが定義されています。
  MSDO:

    # ジョブが実行されるランナーのOSを定義します。
    # Windows最新版を指定しています。
    runs-on: windows-latest

    # ジョブのステップのリストを開始します。
    # これらは上から下に順番に実行されます。
    steps:

      # 各ステップで使用するアクションを指定しています。
      # リポジトリをチェックアウトしています。
    - uses: actions/checkout@v3

    # NET Coreランタイムをセットアップしています。
    - uses: actions/setup-dotnet@v3
      with:
        dotnet-version: |
          5.0.x
          6.0.x

      # Microsoft Security DevOpsアクションを実行しています。
    - name: Run Microsoft Security DevOps
      uses: microsoft/security-devops-action@v1.6.0
      id: msdo

      # セキュリティスキャン結果をアップロードしています。
    - name: Upload results to Security tab
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: ${{ steps.msdo.outputs.sarifFile }}

このファイルはGitHub Actionsのワークフローを定義しています。この特定のワークフローは、Linuxコンテナのビルド、プッシュ、デプロイを行います。

.github/workflows/devops.yml
# このワークフローは、リポジトリにプッシュがあった場合に実行されます。
on: [push]

# このワークフローの名前は Linux_Container_Workflow です。
name: Linux_Container_Workflow

# このセクションは、このワークフローが含むジョブを定義します。
jobs:

    # ジョブの名前です。
    build-and-deploy:

        # ubuntu-latest 環境でジョブを実行するように指定しています。
        runs-on: ubuntu-latest

        # ジョブで実行される手順を定義するブロックです。
        steps:

          # リポジトリをチェックアウトします。
        - name: 'Checkout GitHub Action'
          uses: actions/checkout@main
          
          # Azure CLI を使用してログインし、Azureリソースにアクセスします。
        - name: 'Login via Azure CLI'
          uses: azure/login@v1
          with:
            creds: ${{ secrets.AZURE_CREDENTIALS }}
        
          # Dockerイメージをビルドし、Azure Container Registryにプッシュします。
        - name: 'Build and push image'
          uses: azure/docker-login@v1
          with:
            login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }}
            username: ${{ secrets.REGISTRY_USERNAME }}
            password: ${{ secrets.REGISTRY_PASSWORD }}
          
          # run コマンドを使用して、シェルコマンドを実行します。
          # ここではDockerコマンドを使用してイメージをビルドし、プッシュします。
        - run: |
            docker build . -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/sampleapp:${{ github.sha }}
            docker push ${{ secrets.REGISTRY_LOGIN_SERVER }}/sampleapp:${{ github.sha }}

          # ACIにアプリをデプロイします。
        - name: 'Deploy to Azure Container Instances'
          uses: 'azure/aci-deploy@v1'
          with:

            # デプロイするAzureリソースグループを指定します。
            resource-group: ${{ secrets.RESOURCE_GROUP }}

            # デプロイされるコンテナーのDNS名を指定します。
            dns-name-label: ${{ secrets.RESOURCE_GROUP }}${{ github.run_number }}

            # ACRからイメージを取得して、ACIにデプロイします。
            image: ${{ secrets.REGISTRY_LOGIN_SERVER }}/sampleapp:${{ github.sha }}

            # Azure Container Registryにログインするためのログインサーバーを指定します。
            registry-login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }}

            # Azure Container Registryにログインするためのユーザー名を指定します。
            registry-username: ${{ secrets.REGISTRY_USERNAME }}

            # Azure Container Registryにログインするためのパスワードを指定します。
            registry-password: ${{ secrets.REGISTRY_PASSWORD }}

            # ACIの名前を指定します。
            name: aci-sampleapp

            # ACIが展開される場所を指定します。
            location: 'west us'

default.confファイルは、nginxの設定ファイルです。このファイルでlistenするポート番号、サーバー名、ドキュメントルートなどの設定を記述します。

conf/default.conf
server {
        # HTTPリクエストの受信に使用されるWebサーバーを設定しています。
        # listenにより、nginxが受信するポート番号を設定しています。
        # HTTPリクエストを受信するポート80とを設定しています。
        listen 80;
        # HTTPSリクエストを受信するポート443を設定しています。
        listen 443;
        
        # server_nameによりサーバー名の設定を行っています。
        server_name localhost;

        # ドキュメントルートの設定
        root /usr/share/nginx/html;

        # デフォルトのインデックスファイルの設定
        index index.html index.html;

        # リクエストの処理ルールの設定
        # locationディレクティブは、リクエストの処理ルールを設定するためのディレクティブです。
        # ここでは、リクエストされたファイルが存在しない場合には、/src/index.htmlファイルを返すように設定しています。
        location / {
                try_files $uri $uri/ /src/index.html$query_string;
        }

        # エラーページの設定
        error_page   500 502 503 504  /50x.html;
                 location = /50x.html {
                   root   /usr/share/nginx/html;
        }
}

Hello Worldを表示するシンプルなhtmlファイルです。脆弱性を持つindex.jsを読み込んでいます。

src/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>サンプルアプリ</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div>Hello World</div>
  </body>
  <!-- 脆弱性を持つindex.jsのインポート -->
  <script src="js/index.js"></script>
</html>

CodeQLを用いた脆弱性チェックを行うためにindex.jsでは、意図的にシークレットキーとhttpのURLを記述しています。

src/js/index.js
function dummy_func() {
  // 意図的な脆弱性(シークレットキーはソースコード内に記述すべきでない)
  // ダミーシークレットキー
  var DUMMY_SECRET_KEY = "sk_test_51HFbZTKgBlpEUSwRBlkbXZPCt2pfHRAs5DHZB5jbMmnnsVGD8hazbBAAVp8UeEqnC6wZZ9IY2rsXllwzxonWQKZL00nmvWRYUy"
  // 意図的な脆弱性(httpsにするべきである)
  var url = "http://umayadia-apisample.azurewebsites.net/api/persons/Shakespeare" 
}

このDockerfilenginxのWebサーバーをベースとしたDockerイメージを構築するためのファイルです。

Dockerfile
# ベースとなるイメージとして、最新のNginxイメージを指定しています。
FROM nginx:latest

# ローカルの./conf/default.confファイルを、Dockerイメージ内の/etc/nginx/conf.d/default.confにコピーします。
# ここでは、nginxの設定ファイルをDockerイメージ内に取り込んでいます。
ADD ./conf/default.conf /etc/nginx/conf.d/default.conf

# ローカルの./srcフォルダを、Dockerイメージ内の/usr/share/nginx/htmlフォルダにコピーします。
# ここでは、nginxが提供するWebコンテンツのルートディレクトリをDockerイメージ内に取り込んでいます。
ADD ./src /usr/share/nginx/html

# nginxを起動するためのコマンドを指定します。
# ここでは、単にstart nginxという文字列を表示するだけのコマンドを実行します。
# この行は、Dockerイメージのビルド時に実行されます。
RUN echo "start nginx"

製品一覧

本記事で扱う製品一覧になります。今回はGitHubを用いてDevOpsの構築をしていますが、Azure DevOpsを用いても構築可能です。
image.png

Microsoft Defender for DevOpsとは

image.png
Microsoft Defender for DevOpsは、Azure DevOpsおよびGitHubなどのCI/CDプラットフォームに統合されたセキュリティ製品で、コード解析や脆弱性診断、悪意のあるアクティビティの検出などを提供します。ビルド、デプロイ、テストなどのアクティビティを実行するときに、セキュリティとコンプライアンスをサポートすることができ、脆弱性の検出、シークレット管理、脅威検知、コンプライアンス監視、コンテナセキュリティ、アプリケーションの保護など、さまざまな機能を提供します。また、AIおよび機械学習技術を利用して、異常なアクティビティや攻撃を自動的に検知することもできます。

アーキテクチャ

今回構築するアーキテクチャになります。ソースコードをGitHubにpushをすると、事前に定義されていたGitHub Actionsのワークフローがトリガーされ、javascriptの脆弱性チェック及び、自動ビルド&自動デプロイが行われます。脆弱性はCodeQLにチェックされ、コードスキャンやシークレットスキャンが行われます。GitHubMicrosoft Defender for DevOpsは連携されていることから、脆弱性が見つかれば、GitHubAzure portalの両方から確認をすることができます。
image.png

代替可能なアーキテクチャ

DevOps構築のために、GitHubの代わりにAzure DevOpsを用いることができます。また、コンテナーアプリのデプロイ先として、Azure Container Instanceの代わりにApp ServiceAzure Kubernetes ServiceAzure Container Appsでも指定することができます。
image.png

Azure DevOpsとGitHub Actionsの使い分け

Azure DevOpsGitHub Actionsの違いについて簡単にまとめました。プロジェクトの規模や、他にMicrosoft製品を使用しているかによって使い分けることができそうです。
image.png

Azure DevOpsとGitHub Actionsの違い

2023年3月現在、Azure DevOpsGitHub Actionsでいくつかの機能の違いが確認されました。しかし、今後はこれらの違いはなくなると思われます。
image.png

ACI/ACA/AKS/Azure App Serviceの違い

デプロイ先のサービスとして、いくつか候補があるのでACIACAAKSAzure App Serviceについて違いを簡単にまとめました。プロジェクトの規模や、アプリの形式によって使い分けができます。
image.png

セキュアな自動ビルドと自動デプロイ

gitにより、事前に作成&登録してあるリモートリポジトリにファイルをpushします。

git .
git commit -m "first commit"
git push origin master

GitHubの画面からActionsを選択すると、自動ビルド&自動デプロイが行われていることが確認できます。
image.png
同時にCodeQLによるjavascriptファイルの脆弱性チェックも行われています。
image.png

GitHub画面

Securityタブ>Code scanningでアラートの内容が表示できます。
image.png
アラートの内容を選択すると、ソースコードの該当箇所とアラート内容が確認できます。(表示されているurlが多少違いますが、類似した内容のアラートが表示されるはずです。)
image.png
シークレットキーの漏洩についてもスキャンを行い、アラートを出してくれます。(当然ですが、シークレットキーに似せた適当な文字列も公開しましたが、アラートは出されませんでした。)
image.png

Azureポータル画面

Microsoft Defender for Cloud>DevOps Securityのページより、GitHub画面で表示されたアラートと同じアラートを確認することができました。
image.png
GitHubのプロジェクトを選択することで、アラートの内容を確認することができます。
image.png

おわりに

DevOpsのCI/CD環境の構築を始めて行ったのですが、GitHubにソースコードをpushするだけで自動的にビルドとデプロイをしてくれるのは非常に役に立つと感じました。また、GitHub Actionsを用いたCodeQLによる脆弱性チェックにより、javascriptの脆弱性を簡単に見つけることができました。私自身Azureの勉強中でなので、何か間違いを見つけたら教えていただけると幸いです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?