この記事では、RHEL 9系/Rocky Linux 9を想定環境として、シェルスクリプトの実行権限を正しく設計・配布する方法と、setuidが使えないLinuxの制約に対する安全な代替手段を解説します。
この記事のポイント
・Linuxカーネルはインタープリタ系スクリプトのsetuidビットを意図的に無視する
・chmod +x と所有者設計の組み合わせがスクリプト配布の基本
・特権操作にはsudoers設定またはCラッパーによる代替設計が安全
・「Permission denied」は実行ビット・ディレクトリ権限・SELinuxの順に確認する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜシェルスクリプトにsetuidが効かないのか
setuidビット(chmod u+s)は本来、ファイルをオーナーの権限で実行させる仕組みです。ところがLinuxでは、シェルスクリプトを含むインタープリタ型ファイルのsetuidを意図的に無視します。理由はセキュリティ上の制約です。シェルスクリプトの実行は「まず/bin/bash(インタープリタ)が起動し、スクリプトを引数として読み込む」という二段階になります。このとき、/bin/bashに渡す前の一瞬にシンボリックリンク差し替え攻撃(TOCTOU: Time-Of-Check to Time-Of-Use)が成立してしまうため、Linuxカーネルは設計上setuidを無効にしています。
結果として、setuidビットを設定しても無害に無視されるだけです。
# setuidビットを立ててもlsで確認するとsビットは見えるが実行時は無視される $ chmod u+s /usr/local/bin/backup.sh $ ls -la /usr/local/bin/backup.sh -rwsr-xr-x 1 root root 512 Oct 28 10:00 backup.sh # 一般ユーザーで実行しても root 権限にはならない(wohoami の出力は alice のまま) $ /usr/local/bin/backup.sh
「setuidを立てれば特権でスクリプトを動かせる」という思い込みが配布設計の誤りの根本です。
chmod +x と実行権限の基本設計
setuidは使えないとわかったうえで、スクリプトを安全に配布するための基本設計を押さえましょう。1. シェバン行と実行ビットの両方を整える
スクリプトを直接実行(./script.sh)するには2つの条件が揃う必要があります。シェバン行(1行目の#!/bin/bash)と、実行ビット(chmod +x)の両方です。どちらが欠けても「Permission denied」または「bad interpreter」エラーになります。# 実行ビットを付与する(配布先での基本手順) chmod 755 /usr/local/bin/backup.sh # 全ユーザーが実行可能 chmod 750 /usr/local/bin/backup.sh # オーナーとグループのみ実行可能 chmod 700 /usr/local/bin/backup.sh # オーナーのみ実行可能
2. 所有者とグループの設計
スクリプトの実行可否は「誰が所有者か・どのグループに属するか」で決まります。運用スクリプトを複数のオペレーターに使わせたい場合は、専用グループ(例: ops)を作成し、そのグループに実行権限を付ける設計が安全です。
# スクリプトを root 所有・ops グループで管理する例 chown root:ops /usr/local/bin/backup.sh chmod 750 /usr/local/bin/backup.sh # → root と ops グループメンバーのみ実行可能(その他のユーザーはアクセス不可) # ユーザーを ops グループに追加する usermod -aG ops alice
3. 配置先ディレクトリのパーミッション
スクリプト自身の権限が正しくても、配置先ディレクトリに実行権限(x)がなければ「Permission denied」になります。# ディレクトリの実行権限を確認する ls -la /usr/local/ # drwxr-xr-x 12 root root 4096 Oct 28 10:00 /usr/local/ ← r-x があれば OK # 問題例: ディレクトリが 700 だと他のユーザーはスクリプトに到達できない ls -la /opt/myapp/ # drwx------ 2 root root 4096 Oct 28 09:00 /opt/myapp/ ← alice はアクセス不可
ディレクトリにxビットがなければ、中のファイルのパーミッションに関係なくアクセス自体が遮断されます。配布時は配置先ディレクトリの権限も必ず確認してください。
setuidを使わない特権操作の3つの代替手段
特定の処理にどうしても昇格権限が必要な場合は、以下の代替手段を検討します。setuidに頼らず、より安全に同じ目的を達成できます。シェルスクリプトの権限設計・配布設計についてさらに深く学びたい方は、シェルスクリプト実践入門(LinuxMaster.JP)をあわせてご覧ください。
1. sudoersで特定スクリプトだけに許可を絞る
最もよく使われる方法です。visudoコマンドで /etc/sudoers に1行追加するだけで、特定の一般ユーザーが特定のスクリプトのみをパスワードなしでroot権限で実行できるようになります。# /etc/sudoers に追加(必ず visudo コマンドで編集する) alice ALL=(root) NOPASSWD: /usr/local/bin/backup.sh # alice がパスワードなしで実行できる sudo /usr/local/bin/backup.sh
・許可ユーザー:特定ユーザー・グループ単位で絞り込める
・パスワード要否:NOPASSWDで省略可能(自動化cron向け)
・引数制限:スクリプトパスの後に引数パターンを書くと引数レベルでも制限できる
2. Cラッパーバイナリでsetuidを安全に使う
どうしてもsetuidで動かしたい処理がある場合は、シェルスクリプトではなくCで書いたラッパーバイナリを間に挟みます。バイナリはTOCTOU攻撃の影響を受けないため、setuidが安全に機能します。/* wrapper.c — シェルスクリプトをroot権限で呼び出すCラッパーの例 */ #include <unistd.h> int main() { setuid(0); execl("/bin/bash", "bash", "/usr/local/bin/backup.sh", (char *)0); return 1; }
# コンパイルしてsetuidを設定する gcc -o /usr/local/bin/backup-wrapper wrapper.c chown root:root /usr/local/bin/backup-wrapper chmod 4755 /usr/local/bin/backup-wrapper # 4=setuidビット有効
3. Linux capabilitiesで最小権限を付与する
「root全権は要らないが、特定のシステムコールだけ昇格させたい」という場合はLinux capabilitiesが有効です。ただし、シェルスクリプト自体にはcapabilitiesを付与できないため、専用バイナリを経由する必要があります。# 例: バイナリに net_admin capability のみを付与 setcap cap_net_admin+ep /usr/local/bin/net-tool # 付与した capabilities を確認 getcap /usr/local/bin/net-tool # /usr/local/bin/net-tool = cap_net_admin+ep # 削除する場合 setcap -r /usr/local/bin/net-tool
配布設計のチェックリスト
スクリプトを新しいサーバーに配布・配置するときに確認する手順をまとめます。・シェバン行:1行目に
#!/bin/bash(または /bin/sh)が記載されているか・実行ビット:chmod で x ビットが付いているか(
ls -la で確認)・所有者:セキュリティ要件に合った chown 設定になっているか
・グループ:実行させたいユーザーがグループメンバーになっているか(
id で確認)・配置先ディレクトリ:x ビットがあり実行ユーザーがアクセスできるか
・setuid:スクリプトに setuid を設定しない(設定しても無視される・混乱の元)
・書き込み権限:他者がスクリプトに書き込めないか(777 は厳禁。sudoers 対象スクリプトは 755 以下・root 所有)
・改行コード:WindowsのCR+LFが混入していないか(
file script.sh で確認)「Permission denied」が出た時の対処法
スクリプトの実行権限まわりで「Permission denied」が出たときの調査手順を整理します。# ステップ1: スクリプト自身の権限と所有者を確認する ls -la /path/to/script.sh # ステップ2: 配置先ディレクトリのパーミッションを確認する ls -la /path/to/ # ステップ3: 実行ユーザーのグループ所属を確認する id alice # ステップ4: SELinuxが遮断していないか確認する(RHEL・Rocky Linux系) ls -Z /path/to/script.sh ausearch -m AVC -ts recent | grep script.sh
「bad interpreter: No such file or directory」はシェバン行のパスが間違っているか、Windows改行コード(CR+LF)が混入しているケースです。
# Windows改行コードの混入チェックと除去 file script.sh # script.sh: Bash script, ASCII text executable, with CRLF line terminators # sed で CR を除去する sed -i 's/ //' script.sh
本記事のまとめ
シェルスクリプトの実行権限設計と配布のポイントをまとめます。| やりたいこと | 正しい手段 |
|---|---|
| スクリプトを実行可能にする | chmod 755 script.sh |
| 特定グループだけに実行許可 | chown root:ops script.sh && chmod 750 script.sh |
| 一般ユーザーに特権スクリプト実行を許可する | visudo で NOPASSWD 設定 |
| setuid で特権実行(スクリプトには不可) | C ラッパーバイナリに setuid を適用 |
| 最小権限の付与 | setcap で capability を限定付与 |
| 「Permission denied」の調査 | ls -la + id + ausearch(SELinux)の順に確認 |
setuidはシェルスクリプトに立てても無視されます。特権操作が必要な場面では sudoers による絞り込みか、Cラッパーを使った設計に切り替えることで、セキュリティを保ちながら要件を満たせます。配布時は「実行ビット・所有者・配置先ディレクトリ」の3点を必ず確認するようにしてください。
>> シェルスクリプト実践入門|LinuxMaster.JP
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:sudoで動かすシェルスクリプトの権限設計|root判定と自己再実行・sudoersで特定スクリプトだけ許可する方法
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます