【実務・中級編】 systemdによる不要サービスの無効化と依存関係の最小化 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

攻撃者の「足がかり」を断つ:systemd要塞化による攻撃対象領域の極小化

現場でインシデント対応をしていると、いつも痛感することがある。「なぜ、このWebサーバーで avahi-daemon が動いているんだ?」と。

攻撃者は、あなたのアプリケーションの脆弱性(RCEやSQLi)を突く前に、まずその「足元の脆弱性」をスキャンしている。不要なサービスは、攻撃者にとっての「贈り物」だ。たとえWebアプリが堅牢でも、裏で動いている設定不備のデーモンが権限昇格の踏み台になれば、ゲームオーバーは一瞬だ。

今日は、systemdを駆使して「攻撃者が入り込む隙間すら残さない」ための、泥臭くも強力な要塞化術を伝授する。

—

1. なぜ systemctl disable では足りないのか

多くのエンジニアが systemctl disable で満足している。だが、これは「自動起動をオフにする」だけで、別のサービスからの依存関係で勝手に起動したり、手動で誤って実行されるリスクが残る。

我々がやるべきは、systemctl mask による「恒久的な無効化」だ。これは対象のユニットファイルを /dev/null へのシンボリックリンクに置き換える。つまり、何があってもそのサービスは起動不能になる。物理的な「封印」に近い。

攻撃者の視点:なぜそこを狙うのか

攻撃者は、rpcbind や cups、あるいは古い telnet のような、メモリ破壊の脆弱性が放置されがちなサービスを常に探している。これらが動いていれば、たとえWebサーバーが chroot や docker で隔離されていても、カーネルや他のプロセスへ横展開(Lateral Movement)する突破口にされる。

—

2. 実践:攻撃対象領域を最小化する構成

サーバー構築時の「最低限の構成」を自動化するスクリプトを紹介しよう。これをプロビジョニングの最終段階で流し込むだけで、サーバーの守りは格段に堅くなる。

セキュアなサービス遮断用スクリプト (hardening.sh)

#!/bin/bash

# セキュリティチーフの鉄則:不要なサービスは物理的に殺す
# このスクリプトは、一般的なWebサーバーで不要なサービスのリストをマスクする

SERVICES_TO_MASK=(
    "avahi-daemon"  # mDNSサービス。サーバーには不要
    "cups"          # プリンタサービス。Webサーバーで使う理由は皆無
    "rpcbind"       # RPCサービス。脆弱性の宝庫
    "nfs-server"    # NFSを直接Webサーバーで動かす必要はない
    "telnet"        # 論外。SSHがあれば即座に無効化
)

for service in "${SERVICES_TO_MASK[@]}"; do
    if systemctl list-unit-files | grep -q "^$service"; then
        echo "[-] サービス $service をマスクしています..."
        systemctl stop "$service" 2>/dev/null
        systemctl mask "$service"
    fi
done

echo "[+] ハーデニング完了。攻撃対象領域を最小化しました。"

—

3. アプリケーション層での防衛:systemdとの連携

インフラ側でサービスを絞っても、アプリケーション側で「OSのコマンドを実行できる」ような書き方をしていれば無意味だ。例えば、PHPの shell_exec 等でサーバーの情報を覗こうとする攻撃は後を絶たない。

以下は、万が一アプリケーションが突破された際、攻撃者が環境を偵察できないようにするためのPHPの防衛的実装例だ。

不正なコマンド実行を抑止するサンプル (security_check.php)

<?php
/**
 * 攻撃者が shell_exec 等でサーバー情報を探ろうとする際、
 * 可能な限り情報を返さないための防衛的アプローチ。
 */

function secure_execute($command) {
    // 許可されたコマンド以外は実行させないホワイトリスト
    $allowed_commands = ['/usr/bin/git status', '/usr/bin/whoami'];
    
    if (!in_array($command, $allowed_commands)) {
        // 本来はログに記録してアラートを飛ばすべき
        error_log("セキュリティ警告: 不正なコマンド試行 - " . $command);
        return "Access Denied.";
    }
    
    return shell_exec($command);
}

// OSの全サービスリストを表示させようとする攻撃をブロック
$cmd = $_GET['cmd'] ?? '';
echo secure_execute($cmd);
?>

—

4. 最後に:エンジニアが持つべき「疑いの精神」

システム運用において、最も危険なのは「デフォルトで動いているから、必要なのだろう」という思考停止だ。

systemd の世界では、systemctl list-units --type=service --state=running を定期的に確認してほしい。そこに「なぜ動いているのか説明できないプロセス」があるなら、それが攻撃者の次の足がかりだ。

今日の宿題:
1. 今すぐ自分の管理するサーバーで systemctl list-unit-files --state=enabled を叩いてみろ。
2. そのリストの中に、Webアプリの機能に直結しないサービスがあれば、即座に要件を確認し、不要なら mask してしまえ。

セキュリティとは、派手なファイアウォール製品を導入することではない。泥臭く、不要な窓を一つずつ閉じていく作業の積み重ねだ。その地道な作業こそが、あなたのインフラを鉄壁にする。

コメント

タイトルとURLをコピーしました