sudoの設計とroot運用|sudoersの書き方と権限を最小化するサーバー管理の実践

kento_morota 12分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

「サーバーには全員rootで入っている」「デプロイのたびに sudo のパスワードを聞かれて自動化が止まる」「誰がいつ何を root で実行したのか追えない」。小さなチームでサーバーを運用していると、こうした状態が当たり前になりがちです。動いてはいるものの、誤操作ひとつで本番が壊れ、鍵が漏れたときの被害も最大になります。

この記事では、rootでの直接ログインを禁止すべき理由から、visudoとsudoers.dの正しい書き方、コマンド限定やNOPASSWDをどこまで許すかの判断基準、sudoログの監査、そしてデプロイ用ユーザーとサービス実行用ユーザーの分離までを扱います。読み終える頃には「誰に・何を・どこまで」許可するかを自分で設計できるようになります。なお、ユーザー・グループの作成やファイルのパーミッションの基礎はLinuxのユーザー・グループ管理ファイル権限(パーミッション)入門で解説していますので、本記事ではその先の設計に集中します。

なぜrootで直接ログインしてはいけないのか

rootは「間違えても止めてくれない」ユーザー

rootはLinuxにおける特権ユーザーで、パーミッションの検査をほぼ全て素通りします。rm -rf の引数を1文字間違えても、設定ファイルを壊しても、カーネルは「本人がそう言ったのだから」と実行します。一般ユーザーであれば「Permission denied」で止まっていた操作が、rootでは止まりません。sudoの本質は「必要な瞬間だけ特権を借りる」ことであり、普段は一般ユーザーの安全装置の中で作業することにあります。

rootログインを許すと「誰がやったか」が消える

複数人がrootのパスワードや鍵を共有すると、ログには全て root としか残りません。障害の原因調査でも、退職者のアクセス権剥奪でも、個人を特定できないことが致命的になります。sudoを経由すれば、ログには「どの一般ユーザーが、いつ、どのコマンドをrootとして実行したか」が記録されます。SSHの設定では PermitRootLogin no を明示しておきましょう(OpenSSH 7.0以降のデフォルトはパスワードのみ拒否する prohibit-password で、鍵によるrootログインは通ります)。SSH設定そのものはLinuxのSSH接続入門を参照してください。

visudoとsudoers.dの正しい書き方

必ずvisudoを使う理由

sudoの設定ファイルは /etc/sudoers ですが、直接エディタで開いてはいけません。visudo は保存時に構文チェックを行い、エラーがあれば警告して書き込みを防ぎます。sudoersに構文エラーが1行でもあると、sudoコマンド自体が一切使えなくなり、rootにも戻れなくなるからです。個別の設定は /etc/sudoers.d/ 配下に分割し、本体は触らないのが現代的な運用です。

# 新しい設定ファイルを安全に作る(ファイル名にドットを含めないこと)
sudo visudo -f /etc/sudoers.d/deploy

# 既存ファイルの構文だけ検査する
sudo visudo -cf /etc/sudoers.d/deploy

# 自分に許可されているコマンドを確認する
sudo -l

/etc/sudoers.d/ のファイルは、ファイル名にドット(.)が含まれるものや ~ で終わるものは読み込まれません。これはパッケージマネージャーが残す deploy.dpkg-new のような一時ファイルを無視するための仕様ですが、知らずに deploy.conf と名付けて「設定が効かない」と悩む方が多いポイントです。パーミッションは 0440、所有者はrootにしておきます。

sudoers行の構造

1行の基本形は「誰が 、どのホストで=(誰として) どのコマンドを」です。

# 書式: ユーザー  ホスト=(実行ユーザー:実行グループ)  コマンド
# opsグループ全員に全ての操作を許可(パスワード必須)
%ops    ALL=(ALL:ALL) ALL

# コマンドをまとめて名前を付ける
Cmnd_Alias APP_CTL = /usr/bin/systemctl restart myapp, \
                     /usr/bin/systemctl status myapp, \
                     /usr/bin/journalctl -u myapp *

# deployユーザーにはアプリの再起動と確認だけをパスワードなしで許可
deploy  ALL=(root) NOPASSWD: APP_CTL

# パスワード入力後に特権が維持される時間(分)を短くする
Defaults timestamp_timeout=5

コマンドは必ず絶対パスで書きます。相対パスを許すと、攻撃者が同名の別プログラムをPATHに置くだけで任意のコードをrootで実行できてしまいます。ホスト欄は複数サーバーで同じsudoersを配布する場合に意味を持ち、単一サーバーなら ALL で問題ありません。

コマンド限定とNOPASSWDをどこまで許すか

「コマンドを限定したつもり」が抜け穴になるケース

コマンド限定は有効な手段ですが、許可するコマンドの性質を理解していないと意味を失います。典型的な抜け穴は次の通りです。

許可した内容何が起きるか対策
/usr/bin/vimlessエディタ内から :!sh でrootシェルを起動できる編集は sudoedit を使う
/usr/bin/systemctl *systemctl の全サブコマンドが通り、任意サービスの停止や edit でユニット改変が可能サブコマンドと対象を固定して列挙する
/bin/cp, /bin/chown/etc/shadow の上書きや /etc/sudoers.d への配置に使えるスクリプト化してそのスクリプトのみ許可
/usr/bin/find, awk, tar-execsystem() でシェルを呼べる原則許可しない

実務では「複数の操作を1つのシェルスクリプトにまとめ、そのスクリプトをrootが所有・書き込み禁止にしたうえで、それだけを許可する」形が最も安全で管理しやすい方法です。スクリプトが一般ユーザーに書き換え可能だと意味がないので、chown root:rootchmod 755 をセットで行います。

NOPASSWDの判断基準

NOPASSWDは「対話できないプロセス(CI/CDやcron、監視エージェント)が特定の操作をするとき」に限って使うのが原則です。人間が使うアカウントに NOPASSWD: ALL を付けると、SSH鍵が漏れた時点でサーバー全体が乗っ取られます。逆に、GitHub Actionsからデプロイする自動化ユーザーにパスワードを要求すると、パスワードをシークレットに保存して echo pass | sudo -S のように渡すことになり、これはNOPASSWDより危険です。判断の軸は「人か機械か」と「許可範囲が十分に狭いか」の2つです。

sudoログの監査

誰が何を実行したかを追う

sudoは実行のたびにsyslogへ記録します。保存先はDebian/Ubuntu系なら /var/log/auth.log、RHEL系なら /var/log/secure で、systemd環境では journalctl からも参照できます。

# sudoの実行履歴(ユーザー・作業ディレクトリ・実行コマンドが残る)
sudo journalctl _COMM=sudo --since "1 day ago"

# 例: Sep  2 09:21:00 web1 sudo:  deploy : TTY=pts/0 ; PWD=/var/www/app ;
#     USER=root ; COMMAND=/usr/bin/systemctl restart myapp

# 認証失敗(パスワード間違い・許可外コマンド)だけを抽出
grep "sudo" /var/log/auth.log | grep -E "incorrect password|NOT in sudoers"

「NOT in sudoers」は許可されていないユーザーがsudoを試みた記録で、侵入の兆候か、設定漏れのどちらかです。ログの保管期間やローテーションについてはLinuxのログ管理入門で触れています。

操作内容まで記録するI/Oログ

コマンド名だけでなく、sudo -i でシェルに入った後の操作まで残したい場合は、I/Oログを有効にします。Defaults log_input, log_output を追加すると、セッションの入出力が /var/log/sudo-io/ に保存され、sudoreplay コマンドで再生できます。監査要件のある案件や、外部委託先にサーバーを触らせる場面では特に有効です。ただしパスワードを含む入力もそのまま記録されるので、ログの閲覧権限はrootに限定してください。

デプロイユーザーとサービスユーザーを分離する

「動かすユーザー」と「配置するユーザー」は別にする

Webアプリを運用するサーバーでは、少なくとも次の3種類の役割を分けるのが基本です。

  1. 管理者(人間): 個人ごとのアカウントを持ち、ops グループ経由でsudoを使う。
  2. デプロイユーザー(deploy): CI/CDがSSHで接続し、アプリのコードを配置してサービスを再起動する。sudoは再起動など数個のコマンドに限定。
  3. サービスユーザー(myapp): アプリのプロセスを実際に動かす。ログインシェルを持たず、sudo権限もゼロ。コードは読めるが書き換えられない。

この分離により、アプリの脆弱性を突かれてプロセスが乗っ取られても、攻撃者は「コードを書き換えられず、sudoも使えないユーザー」の権限しか得られません。

# サービスユーザー(ログイン不可・ホームなし・システムアカウント)
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

# デプロイユーザー(コードの配置先を所有)
sudo useradd --create-home --shell /bin/bash deploy
sudo mkdir -p /var/www/myapp
sudo chown -R deploy:myapp /var/www/myapp
sudo chmod -R 750 /var/www/myapp   # deployは読み書き、myappは読み取りのみ

# systemdユニット側で実行ユーザーを固定する
# /etc/systemd/system/myapp.service
# [Service]
# User=myapp
# Group=myapp
# WorkingDirectory=/var/www/myapp

アップロードディレクトリなどサービスユーザーが書き込む必要のある場所だけ、個別に chown myapp しておきます。systemdユニットの書き方はsystemdとは?Linuxのサービス管理で詳しく解説しています。

トラブル事例:sudoersを壊してsudoが使えなくなった

症状

チームメンバーが /etc/sudoers.d/deploy を通常のエディタで編集して保存した直後から、全ユーザーで sudo を実行すると >>> /etc/sudoers.d/deploy: syntax error near line 3 <<< と表示され、どのコマンドも実行できなくなりました。rootのパスワードは設定しておらず、su でも入れません。

原因

行末の \ による継続行の後に空行が入り、構文が壊れていました。visudoを使っていれば保存時に検出されていたエラーです。sudoはsudoers全体を読み込んでから判定するため、sudoers.d内の1ファイルの誤りでも全体が無効になります。

対処

  1. polkitが有効な環境であれば pkexec visudo -f /etc/sudoers.d/deploy で修正できる場合があります(管理者グループのパスワードで昇格)。
  2. それも使えない場合は、クラウドのシリアルコンソールやレスキューモードからrootで起動し、該当ファイルを修正または削除します。
  3. 再発防止として、sudoers.dの編集はvisudo経由に統一し、Ansibleなどで配布する場合も validate: 'visudo -cf %s' のように検証を挟みます。

クラウド環境ではインスタンスを作り直せば済むこともありますが、データを持つサーバーではそうもいきません。「sudoが壊れたときの復旧手段が用意されているか」を平常時に確認しておくことが、この事故の最大の保険です。

まとめ

sudoの設計は、単に「rootを使わない」ことではなく、「誰が・何を・どこまで」を明文化し、記録に残す仕組みを作ることです。要点を振り返ります。

  1. rootの直接ログインは禁止し、個人アカウントからsudoで昇格する。
  2. 設定は必ずvisudoで編集し、/etc/sudoers.d/ に分割する(ファイル名にドットを入れない)。
  3. コマンド限定は抜け穴を理解したうえで、スクリプト化して許可する。
  4. NOPASSWDは機械用アカウントの、狭い範囲に限る。
  5. デプロイユーザーとサービスユーザーを分け、サービスユーザーにはsudoを与えない。

まずは sudo -l で自分のサーバーの現状を確認し、NOPASSWD: ALL が人間のアカウントに付いていないかを見直すところから始めてみてください。サーバー全体の堅牢化についてはLinuxサーバーのセキュリティ強化も合わせて参考になります。権限設計を含めたサーバー構成の見直しや運用体制の整備でお困りの際は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。

#sudo#root#Linux#権限管理

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します