「設定したつもり」が一番危険だ。CIS BenchmarksをCI/CDに組み込み、OSを「要塞」に変える技術
現場で多くのインシデントを追跡していると、ある共通点に気づく。攻撃者は決して「ゼロデイ脆弱性」だけで侵入してくるわけじゃない。その大半は、「デフォルトのまま放置されたポート」「無効化されていない不要なサービス」「甘すぎるパーミッション」といった、いわゆる「設定不備」の隙を突いているんだ。
OSの要塞化(ハーデニング)は、泥臭い作業だ。だが、これを人間の目視でチェックするのは不可能だし、何より面白くない。今日は、CIS Benchmarksという「業界の共通言語」を使い、CI/CDパイプラインで自動的にOSの脆弱性を潰し込む、実戦的な手法を伝授する。
—
1. なぜ「手動の要塞化」では守れないのか
多くのエンジニアが「構築時にスクリプトを流したから大丈夫」と安心するが、それは幻想だ。アプリケーションの改修やライブラリのアップデートの裏で、いつの間にか telnet が有効化されていたり、不要なデーモンが再起動で立ち上がったりする。
攻撃者は、あなたのサーバーがOSインストール直後にどういう状態かを熟知している。例えば、rpcbind や avahi-daemon が動いていれば、そこからローカル権限昇格の糸口を探すのは彼らにとって朝飯前だ。
だからこそ、「CI/CDのゲートとして、OSの構成をテストする」という考え方が不可欠になる。
—
2. InSpecを用いた自動監査の実装:IaCのその先へ
今回は、Chef社が開発した InSpec を使う。これはテストコードを書く感覚で、サーバーのセキュリティ状態を検証できるツールだ。
InSpecのインストールと初期設定
まずは、環境構築だ。CI/CDのパイプライン(GitHub ActionsやGitLab CI)から実行できるようにしておく必要がある。
# InSpecのインストール(Ruby環境が必要)
gem install inspec
# 監査対象のOSを指定してプロファイルを作成
inspec init profile hardening-check
CIS Benchmarksに基づくチェックコード(Ruby)
controls/os_hardening.rb に、絶対に守らせたいルールを記述する。例えば、「SSHのルートログイン禁止」と「不要なサービスの停止」を強制するコード例だ。
# CIS Benchmarksの要件をコード化する
control 'ssh-hardened' do
title 'SSHサーバーの構成を厳格化する'
# SSH設定ファイルからPermitRootLoginがnoになっているか確認
describe sshd_config do
its('PermitRootLogin') { should cmp 'no' }
end
end
control 'service-check' do
title '不要なサービスが動いていないか'
# avahi-daemonのような不要なサービスを止める
describe service('avahi-daemon') do
it { should_not be_enabled }
it { should_not be_running }
end
end
—
3. CI/CDパイプラインへの統合(GitHub Actions例)
このコードをただ書くだけでは意味がない。デプロイのたびに自動的にこのテストが走り、基準を満たさなければデプロイを止める。これが「要塞化」の自動化だ。
以下は、main.yml に組み込むための設定例だ。
# .github/workflows/security-audit.yml
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# サーバーへの接続設定(SSH鍵などはGitHub Secretsから注入)
- name: Run CIS Benchmark Audit
run: |
inspec exec hardening-check -t ssh://user@your-server-ip \
--key-files ~/.ssh/id_rsa \
--reporter cli json:results.json
# 失敗したらパイプラインを停止する
- name: Check audit results
if: failure()
run: exit 1
—
4. 現場のプロが教える「盲点」
自動監査を入れた後、多くの現場で陥る罠がある。「警告(Warning)だらけで、結局誰も見なくなる」ということだ。
1. 段階的に導入せよ: いきなりCISの全項目を適用しようとすると、既存システムが確実に壊れる。まずは「SSHの禁止」「パスワードポリシー」「不要サービス」という、影響範囲の大きいものから取り掛かること。
2. IaC(Ansible/Terraform)と連動せよ: InSpecで検知した違反を修復するためのAnsible Playbookをセットで用意しておくのがベストだ。「壊れたら自動で直る」状態が、真のハーデニングだ。
3. WAFとの併用を忘れるな: OSレベルの要塞化は「守りの堅い門」だが、アプリケーション層(XSSやSQLi)を突く攻撃には無力だ。Nginx 等のフロントで mod_security を使い、OSの要塞化と多層防御を構成することを忘れてはいけない。
Nginxでの基本的なセキュリティ設定(抜粋)
# サーバーヘッダーを隠蔽し、情報漏洩を防ぐ
server_tokens off;
# クリックジャッキング対策
add_header X-Frame-Options "SAMEORIGIN";
# XSS対策
add_header X-XSS-Protection "1; mode=block";
# コンテンツタイプスニッフィングの防止
add_header X-Content-Type-Options "nosniff";
—
最後に:セキュリティは「設定」ではなく「文化」だ
OSのハーデニングを自動化し、CI/CDで弾く体制を作ることは、単なる技術的な対策ではない。「セキュリティ基準を満たさないコードは本番に出さない」というチームの姿勢をエンジニア全員に染み込ませるための装置なんだ。
最初は面倒かもしれない。だが、インシデント対応で徹夜し、顧客への謝罪に追われることに比べれば、遥かに安上がりで生産的な作業だ。
さあ、今日からあなたのサーバーの「不要な扉」を、一つずつ閉じていこう。それが、プロのエンジニアとしての最低限のたしなみだ。
コメント