内部統制へのGitHub活用事例

f:id:yuhei_kagaya:20191130165714p:plain

この記事は、「BASE Advent Calendar 2019」の3日目の記事です。

devblog.thebase.in

前の日は id:ngswEC2における単位時間あたりの名前解決制限の対応id:chiiichellエンジニア2年生がリーダブルコードを読んで今年自分が書いたコードを振り返るでした。 devblog.thebase.in

Product Dev Divisionの加賀谷です。内部統制の実現手段の1つである「ITへの対応」について整備改善を日々進めています。社内の内部統制事務局と監査法人に評価とアドバイスをいただきながら、IT統制のうちの全般統制への対応を実施しています。

AnyPay社さんのGitHubを組織運営に活用する事例にあるとおり、活動の中で行う文書管理、承認、証跡管理にGitHubがとても有効だと感じることが多くありました。この記事では先の記事に学び行ってきた中で、弊社のIT統制におけるGitHubの活用事例をご紹介したいと思います。

規程類の管理

GitHubにリポジトリを作り、規程類をMarkdownで統制項目毎に.mdファイルを作っています。アドバイスいただいたことをGitHub Issueに起こして議論を進め、規程の更新はプルリクエストを経てmasterへマージしています。GitHub Flowで運用していて、masterが現在適用されている統制の整備状況としています。

規程類は一度作り終えたら終了というものではなく、会社やサービスの維持と発展のために改善を加えていくものなので、どういった経緯でこの方法になったのか、どんなIssueからどんな議論がされたのか、追跡や検索ができることが大切です。そういった面からもGitHubはとても有効です。

PDFとホスティング

また、GitHubとは直接関係ありませんが、規程類をMarkdownで書いておいて良かったことを紹介します。GitHubでMarkdownが表示できるとはいえ社外の監査法人とやりとりする時があるので、リポジトリに参加してもらうことは現実的に難しかったりします。そこで、スクリプトでMarkdownをPDFに変換しています。markdown-pdfではCSSでフォントや紙のサイズを指定することができるので、文書のデータと体裁を分けて考えることができます。concatもできるので、1つのMarkdownファイル毎に1PDFではなく、規程類のまとまり毎に1つのPDFにconcatしています。

pdf.js

#!/usr/bin/env node

const
    fs = require('fs'),
    path = require('path'),
    markdownpdf = require('markdown-pdf'),
    config = require('config');

const
    walk = (dir, callback) => {    
        fs.readdirSync(dir).forEach((item) => {
            const filePath = path.join(dir, item);
            const stat = fs.statSync(filePath);
            if (stat.isDirectory()) {
                walk(filePath, callback);
            } else if (stat.isFile()) {
                callback(filePath);
            }
        });
    };
config.get('pdf.docs').map(doc => {
    let strings = [];
    walk(doc.dir, filePath => {
        if (/.*\.md$/.test(filePath)) {
            strings.push(fs.readFileSync(filePath, {encoding:'utf-8'}).replace(/---[\s\S]*?---/, '---'));
        }
    }); 
    markdownpdf({
        paperFormat: "A4",
        cssPath: "pdf.css",
        remarkable: {
            linkify: true,
        }
    })
    .concat
    .from.strings(strings)
    .to(doc.dest, function() {
        console.log("created", doc.dest);
    });
});

pdf.css

body {
    font-size: 0.625em;
    font-family:"ヒラギノ明朝 ProN W3","Hiragino Mincho ProN","MS P明朝","MS PMincho",serif;

}
abbr[title]:after,
a[href]:after {
    content: "";
}

GitHubに参加していない社内メンバーに公開するにはDocusaurusが便利です。Markdown+Docusaurusの活用についてはBASEの開発者様向け機能のドキュメントサイトをリニューアルしたときの記事もご覧下さい。

特権IDの管理と承認

本番データベースなどの重要なシステムにアクセスする特別な権限を持つメンバーは、業務に応じてアクセス管理権限一覧に基づき設定しています。また特権の付与や権限を変更する際には責任者の承認を必要とするワークフローにしています。今誰がアクセス権限を持っているかの一覧、どういう理由で申請したのか、誰が承認したのか。ワークフローの構築と証跡管理、モニタリングにGitHubを使っています。

アクセス管理権限一覧をYAMLで書いておき、追加及び削除の申請はYAMLファイルへプルリクエストをして、必要な承認者のレビュー(PullRequestのapproved)を受ける運用にしています。承認者はCODEOWNERS に定義することでプルリクエスト作成時に自動で固定できます。.github/CODEOWNERS ファイル自体のコードオーナーは、モニタリングするメンバーにしています。

まだ実施していませんが、gitリポジトリ内のYAMLファイルにすることでプログラムとの親和性が上がり、TerraformやAnsibleなどインフラ構成管理の中にも組み込むこともできると思います。

CODEOWNERS

# CODEOWNERSの例です。

# デフォルト
* @violetyk

# 本番DBへアクセスするユーザの承認
/permissions/production_db.yml @dmnlk

障害管理台帳

前提として障害は起きないように策を講じるのですが、万が一事故や障害が起きたときには解決に至るまでを記録・管理する手段を講じておくことが必要です。

障害の記録や管理にはGitHub Issueを、Issueの自動作成にGitHub Actionを使っています。弊社では障害発生時のワークフローとして、Slackで #incident_yyyymmdd のようなチャンネルを作り現況報告をするところから始まります(Slackがダウンしていたら代替のチャットで)。

GitHub Actionで定期的に障害のチャンネルが作られているかどうかを監視し、あった場合にはGitHubに障害管理Issueを作ります。障害管理Issueは自動的にincidentタグをつけて責任者にアサインされます。責任者が障害報告を書いて障害対応が終わったらIssueをクローズしています。

GitHubで管理し起票を自動化することで、報告漏れをなくし、未解決の障害や根本対応を要するものの定期的な棚卸しなどが容易になりました。

GitHub Action

障害のチャンネルを監視してIssueを自動で作るGitHub Actionは次のように書いています。

.github/workflows/create_issues.yml

name: Create Github issues from Slack incident channels

on:
  schedule:
  - cron: '0 1 * * *'

jobs:
  build:

    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: '3.7'
        architecture: 'x64'
    - name: Install dependencies
      run: |
        pip install --upgrade pip pipenv
        pipenv sync
    - name: Create issues
      env:
        SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
      run: |
        YYYYMMDD=`date +"%Y%m%d" --date "1 day ago"`
        pipenv run create_issues --repo=baseinc/info-system --term=${YYYYMMDD} --label=incident --assignee=xxxx

JasonEtco/create-an-issue

GitHub ActionのJasonEtco/create-an-issueはIssueをテンプレートから作るActionです。このActionを定期的に行う必要があるSaaSのアカウントの棚卸しタスクに使っています。GitHub Issueとして作成し、各SaaS管理者にアサイン、棚卸し内容を書いて終わったらクローズするという運用で証跡管理しています。

.github/workflows/create_issues_quarterly.yml

name: Create issues quarterly

on:
  schedule:
  # 1Q(1/1), 2Q(4/1), 3Q(7/1), 4Q(10/1)  (UTC)
  - cron: '0 1 1 1,4,7,10 *'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v1
    - name: Review Slack
      uses: JasonEtco/create-an-issue@v1.2.0
      with:
        args: workflow_templates/review_accounts/slack.md

内部統制の環境整備と開発者体験の両立を目指して

弊社の事例をご紹介させていただきました。まだまだ手探りでやっているところが多いので、活用事例をお持ちの方いらっしゃったらぜひお話を聞かせていただきたいです。 コーポレートエンジニアのミッションは、ショップオーナーに安心してサービスを使い続けていただくために、業務の有効性及び効率性・財務報告の信頼性・事業活動に関わる法令などの遵守並びに資産の保全といった内部統制の環境整備と開発者体験の両立を考え続け、改善し続けていくことです。必要以上のルールや運用が難しい統制を固めることでサービスの成長スピードを落とさず、逆に加速できるよう、柔軟なバランス感を持って改善を進めていきたいと思います。

明日は id:khigashigashiid:smdksk です。お楽しみに!

「BASE Advent Calendar」の記事一覧はこちらです。 devblog.thebase.in