💣

Azure DevOps×ServiceNowで始めるSBOM管理

に公開

対象読者

  • SBOMについてちょっと解像度を上げたい人
  • Azure DevOpsを使ってソフトウェアを管理している人
  • ServiceNowでSBOMを管理してみたい人

きっかけ

久しぶりに公共系の案件を対応することになり、「政府機関等のサイバーセキュリティ対策のための統一基準群(政府統一基準群)」の改定内容を確認していたところ、SBOMに関する言及がありました。

政府機関等のサイバーセキュリティ対策のための統一基準群の
改定に伴う対応[1]

SBOMという単語は聞き馴染みがありつつも、SBOMが何でどのような目的で管理されるものなのか腹落ちしていなかったので、学習・実践してみたいと思ったのがきっかけです。

SBOMについて

まずはIPAが出している「SBOM導入・運用の手引き[2]」から抜粋して、SBOMについて理解を深めていきます。
今回は抜粋してご紹介しますが、非常によくまとまっていて参考になるので、ぜひ一度全量読んでみてください。

SBOMとは

直訳するとSBOM(Software Bill of Materials)は「ソフトウェアの部品表」です。
ソフトウェアを構成するコンポーネント等を部品表として管理します。

SBOM とは、図:2.1.1 で示すようにソフトウェアを構成するコンポーネントの名称やバージョン情報、依存関係、開発者情報などを含むソフトウェアの部品表を指す。情報にはOSS やプロプライエタリソフトウェアに関する情報も含めることが可能であり、機械処理可能なフォーマットが提供されている。

SBOM導入のメリット

SBOM導入によるメリットは以下の2つです。

1. 脆弱性管理能力の向上
2. ライセンス管理能力の向上

まず脆弱性管理能力についてご紹介します。
従来型の管理ではどのアプリケーションにどんなコンポーネントが含まれているか、常に可視化された状態ではありませんでした。
そういった場合、Log4jのようなゼロデイ脆弱性が見つかった際の初動が遅れてしまいます。
SBOM導入することによって、ソフトウェアに含まれているコンポーネントの可視性、検索性が向上し、脆弱性発見時の初動対応を早めることができます。

次にライセンス管理能力についてです。
ここでいうライセンスとはOSSのライセンス形態にフォーカスしてご説明します。
GPLやMPL、BSD等、OSにはライセンス形態があり、その形態ごとに複製やコードの開示等に制約がある場合があります。
開発しているソフトウェアのコンポーネントが、どの形態なのか適切に理解しておくことによってライセンス違反を防ぐことができます。

SBOMの作成・管理について

SBOMの実態はJSON、XML形式等のファイルです。
SBOMにはフォーマットが定められており、SPDXやCycloneDX、SWIDタグと呼ばれるフォーマットが代表的なフォーマットとして推奨されています。


CycloneDX形式のファイルサンプル

このSBOMファイルを生成するために無償/有償問わず、様々なツールが存在します。
要件に応じたツールを選定することが重要であると導入の手引き上でも言及されています。

SBOMファイル単体でも管理メリットがありますが、GUIを備えているとなおユーザビリティが高まります。
作成ツールにGUIが内包しているものもありますが、ファイルをインポートして可視化するようなツールもあります。(後述する実装フェーズでは、可視化ツールとしてServiceNowの開発者用インスタンスを利用しました。)

実装してみる

机上の学習がひと段落したところで、実際に手を動かしてみます。
まずはスモールに始めて行きたいので、極力お金がかからない構成にしました。
SBOMファイルの作成にはTrivy[3]を、可視化ツールにServiceNowのSBOM管理機能[4]を選定しました。

実際にServiceNowに取り込んだ画面はこちらです。
ソフトウェアに含まれるコンポーネントやそのバージョン、内包するCVE、同じコンポーネントを含むBOMエンティティの数等が確認できます。

ServiceNowの実装

ServiceNowに不慣れ&公開情報が少なく苦労しため、備忘もかねてステップバイステップで書いていきます。

アプリケーションのインストール

まずはApplication Managerで必要なアプリケーションをインストールしていきます。
今回インストールしたのは以下の3つです。

  • SBOM Core
  • Data Model for SBOM
  • SBOM Response

アプリケーションマネージャを開き、該当するソフトウェアをインストールします。



インストールが完了するとポップアップでお知らせしてくれました。

SBOM CoreやSBOM Responseも同様にインストールします。

SBOM Responseは関連するコンポーネントも多いので時間がかかりました。
アプリケーションマネージャのアクティビティログを確認して、インストールが完了していることを確認します。

ユーザ作成

APIを実行するためのユーザを作成します。
ユーザ画面で新しいユーザを作成します。

まずはユーザ名だけ入力した状態でユーザを作成し、

作成されたユーザのPWを設定します。


今回のAPI実行には「sn_sbom_core.sbom_ingest」ロールが必要になるので割り当てを行います。

認証スコープ作成

アプリケーションレジストリに割り当てる認証スコープを作ります。




アプリケーションレジストリ作成

最後にOAuthのアクセストークン発行用に、アプリケーションレジストリを作成します。


OAuth用なので、「Create an OAuth API endpoint for external clients」を選択します。

ここまででServiceNow側の設定は完了です。

Azure DevOps側の準備

Azure Repos

今回は別の記事[5]で作成したPythonアプリケーションのSBOMを作成していきます。
Azure Reposは当時のものを再利用するため、詳細は割愛します。

Azure Pipelines

次にSBOMを作成し、ServiceNowにPushするPipelineを構築していきます。
まずはシークレットを設定します。
今回は、API実行用ユーザの

yamlについてはコメントにて解説しています。

sbom.yaml
variables:
  pythonVersion: '3.11'
  # ServiceNowに表示されるアプリケーション名を設定
  projectname: python
  # ServiceNowインスタンスのURL
  INSTANCE_URL: 'https://xxxxxxxxx.service-now.com'
  # SBOMアップロード用のAPIエンドポイント
  UPLOAD_ENDPOINT: '/api/sbom/core/upload'
  # API実行用に作成したユーザ名
  USER_NAME: 'sbom_uploader'
  # アプリケーションレジストリのクライアントID
  CLIENT_ID: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'

resources:
  repositories: 
    - repository: "functions"
      name: "functions"
      ref: "main"
      type: git
 

stages:
- stage: Build
  displayName: 'Build stage'
  jobs:
  - job: Build
    displayName: 'Build Python Functions'
    pool:
      vmImage: 'ubuntu-latest'
    
    steps:
    - checkout: functions
    - task: UsePythonVersion@0
      inputs:
        versionSpec: '$(pythonVersion)'
        addToPath: true
      displayName: 'Use Python $(pythonVersion)'
    
    - script: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
      workingDirectory: '$(Build.SourcesDirectory)'
      displayName: 'Install dependencies'

    # Trivyのインストール
    - script: |
        curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.47.0
      displayName: 'Install Trivy'

    # SBOM生成
    - script: |
        trivy fs --format cyclonedx --output $(Build.ArtifactStagingDirectory)/functions_sbom.json $(Build.SourcesDirectory)
   
        jq '.metadata.component.name = "$(projectName)"' $(Build.ArtifactStagingDirectory)/functions_sbom.json > $(Build.ArtifactStagingDirectory)/temp.json
        mv $(Build.ArtifactStagingDirectory)/temp.json $(Build.ArtifactStagingDirectory)/functions_sbom.json
      displayName: 'Generate SBOM with Trivy'

    - task: PublishBuildArtifacts@1
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)'
        ArtifactName: 'SBOM'
        publishLocation: 'Container'
      displayName: 'Publish SBOM'
    
    # ServiceNowアクセストークン取得
    - script: |
        TOKEN_RESPONSE=$(curl -s -X POST \
        "$(INSTANCE_URL)/oauth_token.do" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        --data-urlencode "grant_type=password" \
        --data-urlencode 'username=$(USER_NAME)' \
        --data-urlencode 'password=$(PASSWORD)' \
        --data-urlencode 'client_id=$(CLIENT_ID)' \
        --data-urlencode 'client_secret=$(CLIENT_SECRET)')
        
        ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
        echo "##vso[task.setvariable variable=ACCESS_TOKEN]$ACCESS_TOKEN"

      displayName: 'Get Access Token'  

    # SBOMアップロード
    # https://www.servicenow.com/docs/ja-JP/bundle/xanadu-security-management/page/product/secops-integration-vr/sbom/concept/vr-sbom-preparing-upload.html
    - script: |
        response_body=$(mktemp)
        status_code=$(
          curl -v -X POST \
          "$(INSTANCE_URL)$(UPLOAD_ENDPOINT)?requestedBy=devops&buildId=$(Build.BuildId)&lifecycleStage=pre_production" \
          -H "Authorization: Bearer $(ACCESS_TOKEN)" \
          -H 'Content-Type: application/json' \
          --data-binary "@$(Build.ArtifactStagingDirectory)/functions_sbom.json" \
          -w '%{http_code}' \
          -s \
          -o "$response_body"
          )

          # レスポンスの確認
          if [ "$status_code" -eq 200 ]; then
            echo "SBOMファイルのアップロードに成功しました。"
          else
            error_message=$(cat "$response_body" | jq -r '.result.message // "Unknown error"')
            echo "失敗しました。ステータスコード: $status_code"
            echo "エラーメッセージ: $error_message"
            rm "$response_body"
            exit 1
          fi
      displayName: 'Upload SBOM'  

まとめ

SBOMの概要とAzure DevOps、ServiceNowを活用した可視化までの流れについてご説明いたしました。

脚注
  1. https://www.soumu.go.jp/main_content/000984561.pdf ↩︎

  2. https://www.ipa.go.jp/jinzai/ics/core_human_resource/final_project/2024/sbn8o10000001y6j-att/sbn8o10000001zcl.pdf ↩︎

  3. https://www.servicenow.com/docs/ja-JP/bundle/xanadu-security-management/page/product/secops-integration-vr/sbom/reference/sbom-landing.html ↩︎

  4. https://trivy.dev/latest/ ↩︎

  5. https://zenn.dev/hanabusashun/articles/f1c69efa0ea38c ↩︎

Discussion