はじめに
Linux運用者なら一度は聞いたことがある危険なコマンドがあります。
rm -rf /
現在の多くのLinuxディストリビューションでは、GNU rmの保護機能によって実行がブロックされます。
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe
しかし実際の現場では、管理者が意図的に rm -rf / を実行するケースよりも、スクリプトの不具合や変数展開ミスによって同等の結果を引き起こしてしまうケースの方が危険です。
今回は、システム全体削除につながる代表的なヒヤリハット事例を紹介します。
事例1: rm -rf /*
最も有名なパターンです。
rm -rf /*
一見すると rm -rf / とは違うように見えます。
しかしシェルが先にワイルドカードを展開するため、
rm -rf /bin /boot /dev /etc /home /usr /var ...
として実行されます。
GNU rmの --preserve-root は「/そのもの」を保護する機能です。
そのため、
rm -rf /*
は防げません。
事例2: 変数が空になった
現場で最も発生しやすい事故です。
本来は以下のような処理を想定していました。
TARGET_DIR=/tmp/work
rm -rf ${TARGET_DIR}/*
ところが変数取得に失敗すると、
TARGET_DIR=
rm -rf ${TARGET_DIR}/*
となります。
実際に実行されるコマンドは
rm -rf /*
になってしまい、事例1で実行可能なコマンドになってしまいます。
事例3: cd失敗を考慮していない
よくある保守スクリプトです。
cd /backup/work
rm -rf *
通常は問題ありません。
しかし、
cd /backup/work
が失敗した場合を考えていないと危険です。
例えば、
cd /backup/work
rm -rf *
の前に作業ディレクトリが / で、cdコマンドが事前のフォルダ削除などで失敗した場合、
rm -rf *
は実質的に
rm -rf /*
になります。
そのため、
cd /backup/work || exit 1
のように、事前のコマンドの実行結果を持って終了判定を付与するべきです。
事例4: 危険な変数展開
以下のような記述を見かけることがあります。
rm -rf "$TARGET_DIR"
これはまだ安全です。
しかし、
rm -rf "$TARGET_DIR"/*
になると話が変わります。
例えば、
TARGET_DIR="/"
の場合、
rm -rf "/"/*
となり、
rm -rf /*
と同じ結果になります。
防止策
1. ${VAR:?} を利用する
推奨される書き方です。
rm -rf "${TARGET_DIR:?}"/*
変数が空の場合は即座にエラーになります。
bash: TARGET_DIR: parameter null or not set
2. set -u を利用する
set -u
未定義変数を参照した時点でスクリプトを停止できます。
3. cd失敗時に終了する
cd /backup/work || exit 1
4. rootディレクトリを明示的に拒否する
if [ "$TARGET_DIR" = "/" ]; then
echo "Refuse to delete root directory"
exit 1
fi
まとめ
Linuxで危険なのは
rm -rf /
だけではありません。
実際の事故の多くは、
- 変数が空になる
- cdに失敗する
- ワイルドカードが展開される
- sudoで実行する
- スクリプトの想定外動作
といった要因によって発生します。
特に以下のようなコードは要注意です。
rm -rf ${VAR}/*
cd some_dir
rm -rf *
find "$VAR" -delete
システムを守るためには、
${VAR:?}set -ucd ... || exit 1- 実行前確認
を習慣化し、「削除処理は常に失敗する前提」で設計することが重要です。