【実践シェルスクリプト講座 番外編】動かない時はここを見ろ!安全性向上と「ハマりポイント」総まとめ
こんにちは!「リナックス先生」です。
全4回の「実践シェルスクリプト講座」、お疲れ様でした!
コウ君、その後スクリプトを運用していて、何か困ったことは起きてない?
先生!実は…手動で動かすと完璧なのに、Cronに登録したら動かなかったり、別のLinuxに入れたら変なエラーが出たりするんです。
あと、第3回で作ったSSH鍵で、なぜかログインできないユーザーがいて…。
僕の書き方が悪いんでしょうか?
それはスクリプトあるあるね!
プログラミングの世界では「環境の差異」や「見落としがちな設定」が原因で動かないことがよくあるの。
今回は番外編として、全4回の記事に対する「補足」と、より安全に運用するための「プロの修正テクニック」を解説するわよ!
本記事は、これまでの講座(全4回)で作成したスクリプトを、より安全に、より確実に動作させるための補足・修正記事です。
特に「SELinux」や「Cronのパス問題」など、現場で必ず直面する壁の乗り越え方を学びましょう。
講座アーカイブ
各回の記事はこちらから確認できます。
共通の落とし穴:Cronで動かない時の「パス問題」
現象:
手動で ./script.sh と叩くと動くのに、Cronに登録すると動かない、または一部のコマンドだけ失敗する。
原因:
Cronが実行される時は、環境変数 $PATH が最小限しか設定されていません。
そのため、普段使えているコマンド(例えば rsync や firewall-cmd)が見つからないことがあります。
【対策】絶対パスで書くか、PATHを定義する
スクリプトの冒頭で、環境変数を明示的に読み込ませるのが一番確実です。
#!/bin/bash # 方法1: PATHを通す(推奨) export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 方法2: コマンドを全て絶対パスで書く(面倒だが確実) # rsync -> /usr/bin/rsync # firewall-cmd -> /usr/bin/firewall-cmd
コマンドの場所がわからないときは、which コマンド名(例:which rsync)で調べることができます。
第1回(ログローテーション)の補足・修正
修正:空変数の恐怖と「set -u」
第1回のログローテーションスクリプトで、find コマンドを使って古いログを削除しました。
もし、設定ミスで LOG_DIR 変数が空っぽだったらどうなるでしょうか?
LOG_DIR="" cd "$LOG_DIR" # 空なので移動せず、カレントディレクトリに留まる find . -name "*.gz" -delete # カレントディレクトリ(例えば /root)のファイルを消し始める!
これは大惨事を招きます。
これを防ぐために、スクリプトの冒頭に set -u を入れる習慣をつけましょう。
#!/bin/bash set -e # エラーで停止 set -u # 未定義の変数を使おうとしたらエラーで停止 LOG_DIR="/var/log/myapp" # ...以下略
set -u があれば、万が一変数が空だった場合に「unbound variable」というエラーが出て、削除コマンドが実行される前に止まってくれます。
第2回(バックアップ)の補足・修正
補足:tarの「-P」オプションの危険性
記事中で、バックアップ時に「絶対パスを保持する -P オプション」を使用しました。
これは便利な反面、リストア(解凍)時に注意が必要です。
# -P つきで圧縮されたファイルを、ルートディレクトリ(/)で解凍すると... cd / tar -xzvf backup.tar.gz # いきなり本番環境のファイル (/var/www/html/...) が上書きされてしまう!
安全なリストア方法:
不安な場合は、特定のディレクトリに展開するように -C オプションを指定しましょう。
mkdir /tmp/restore_work tar -xzvf backup.tar.gz -C /tmp/restore_work
第3回(ユーザー作成)の補足・修正
重要:RHEL/CentOS/AlmaLinuxでの「SELinux」の罠
第3回のスクリプトで、.ssh ディレクトリや authorized_keys を作成し、権限(chmod/chown)も完璧に設定したのに、「なぜかSSHログインできない」という現象が起きることがあります。
その犯人は、RHEL系OSの強力なセキュリティ機能 SELinux である可能性が高いです。
手動で作成したファイルには、SELinuxの正しい「コンテキスト(ラベル)」が付与されていないため、SSHデーモンが読み込みを拒否するのです。
【対策】restorecon コマンドの追加
スクリプトの最後(chownの後あたり)に、以下のコマンドを追加してください。
# 8. 権限修正
chown -R "$USERNAME:$USERNAME" "$SSH_DIR"
# 【追加】SELinuxのコンテキストを修復する
if command -v restorecon &> /dev/null; then
restorecon -R -v "$SSH_DIR"
echo "[SELinux] $SSH_DIR のコンテキストを修正しました。"
fi
restorecon コマンドは、ファイルのある場所(パス)に応じて、本来あるべき正しいSELinuxラベルを貼り直してくれます。
これを知っているかどうかで、トラブルシューティングにかかる時間が数時間変わります!
第4回(SSH攻撃ブロック)の補足・修正
修正:ログフォーマットのOS差分
第4回では、awk '{print $(NF-3)}' を使ってIPアドレスを抜き出しました。
しかし、OSの設定やバージョンによって、ログの形式は微妙に異なります。
パターンA (IPのみ): from 192.168.1.1 port...
パターンB (ホスト名あり): from bad-host.com [192.168.1.1] port...
パターンBの場合、$(NF-3) ではIPではなくホスト名が取れてしまったり、ずれたりする可能性があります。
より確実に行うには、「正規表現でIPアドレスを抜き出す」方法が安全です。
【改善版】IP抽出コマンド
grep -oE (Extended Regexでマッチした部分だけ出力) を使います。
# 修正前
... | awk '{print $(NF-3)}' | ...
# 修正後:行の中からIPアドレスっぽい文字列(数字.数字.数字.数字)だけを無理やり引っこ抜く
... | grep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | ...
これなら、ログの文言が多少変わっても、確実にIPアドレスだけを集計できます。
補足:firewalldの「即時反映」と「永続化」
記事中のコードで、同じコマンドを2回打っている箇所がありました。
firewall-cmd --zone=drop --add-source="$IP" # (1) 今すぐ有効にする firewall-cmd --permanent --zone=drop --add-source="$IP" # (2) 再起動後も有効にする
これにはちゃんとした理由があります。--permanent だけを打って firewall-cmd --reload すると、一瞬ファイアウォールが再読み込みされ、通信が途切れるリスクがあります。
攻撃をブロックするたびにリロードするのは負荷が高いため、「メモリ上(即時)に追加」と「設定ファイル(永続)に追加」を別々に行うのが、無停止運用の鉄則なのです。
まとめ:失敗こそが一番の教科書
今回の番外編では、スクリプトをより実践的にするための「泥臭い」知識を紹介しました。
| 対象 | ポイント | プロの処方箋 |
|---|---|---|
| 全体 | Cronで動かない | export PATH=... を書く |
| 全体 | 変数が空で事故る | set -u を冒頭に書く |
| ユーザー作成 | SSHログインできない | restorecon でSELinuxを修正 |
| セキュリティ | IP抽出がずれる | grep -oE でIPパターン抽出 |
なるほど…!特にSELinuxの話は目からウロコでした。
「権限は合ってるのになぜ?」って3時間くらい悩んだ経験があります…。
スクリプトって、書いて終わりじゃなくて、育てていくものなんですね!
その通り!
エラーが出たらラッキーだと思いなさい。それはシステムが「ここがおかしいよ」と教えてくれているメッセージなんだから。
今回学んだテクニックを、第1回〜第4回のスクリプトに組み込んで、自分だけの「最強ツール」を作り上げてみてね!
これで本当に「実践シェルスクリプト講座」は完結です。
あなたのサーバーエンジニアとしての旅が、トラブル少なく、楽しいものになることを祈っています!
▼安全に実験するなら、壊してもすぐ直せるVPSで!



コメント