なぜ守りの章が、サービスを動かす章より先か
普通の入門書なら、ここでそろそろ「何かを動かしてみよう」という章が来る頃だ。Webサーバーを立てる、ファイル共有を始める、自分のアプリを公開する——楽しいのはそこからだ。
この本は、その前に守りの章を置く。理由は単純で、サーバーを世界に公開した瞬間、世界中のbotが一斉に扉を叩き始めるからだ。これは脅し文句ではない。試しに、まだ何も公開していない今のうちに、SSHへのログイン失敗を覗いてみてほしい。
sudo journalctl -u ssh --since "1 hour ago" | grep -i failed
家庭内LANにだけ置いたサーバーなら、ここはほぼ静かなはずだ。失敗ログはせいぜい、自分が打ち間違えた数行だろう。この「静けさ」を一度自分の目で見ておくことに意味がある。公開後に同じコマンドを打つと、見知らぬIPからの失敗ログが分単位で積み上がる。その対比こそが、この章で何を守っているのかを体で理解させてくれる。
# 古いシステムでは /var/log/auth.log にも同じ記録が残る
sudo tail -n 50 /var/log/auth.log 2>/dev/null
守りの濃さは、自分のサーバーがどこに置かれているかで変わる。整理すると二段階だ。
- LAN内のみ:自宅や事務所のルーターの内側にいて、外からは直接届かない。脅威は限定的で、守りは「家庭内の事故」を防ぐ程度でいい
- 公開(インターネットから直接届く):VPS、固定IP、ポート開放——この瞬間に脅威モデルは一段上がる。この章のすべてが必須になる
今がLAN内なら、まだ静かなうちに守りを身につけておく。 公開してから慌てて学ぶのは、扉を開け放したまま鍵の付け方を調べるようなものだ。
第一節 攻撃面という考え方
セキュリティの話は専門用語が多くて身構えるが、土台にある発想は驚くほど単純だ。
入れていないソフトは、攻撃されない。動いていないサービスは、穴にならない。 これを「攻撃面(attack surface)を減らす」という。守りの第一歩は、難しい設定を足すことではなく、要らないものを引くことだ。「引き算の設計」で繰り返した姿勢が、ここでも効く。
まず、いま自分のサーバーが外に向けて「開いている耳」を全部見る。
# いま待ち受けている(LISTEN している)ポートとプロセス
ss -tlnp
出力はこんな形になる。
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=701,fd=3))
LISTEN 0 4096 127.0.0.1:631 0.0.0.0:* users:(("cupsd",pid=812,fd=7))
ここで大事なのは、各行が何を意味するかを一行残らず読むことだ。0.0.0.0:22 は「すべてのネットワークインタフェースの22番ポートで待っている」——つまり外から届く。127.0.0.1:631 は「ローカルホストだけで待っている」——外からは届かない。この違いが分かると、どのサービスが本当に外に晒されているかが見える。
Claudeに聞いてみよう①:ss -tlnp の出力を読み解く
私のDebian 13サーバーで
ss -tlnpを実行したら、次の出力でした:〔ss -tlnp の出力をそのまま貼る〕各行が何のサービスで、外(インターネット)から届く状態か、ローカルだけかを一行ずつ説明してください。 そのうえで、サーバー用途〔例:LAN内のファイル共有のみ〕を踏まえて、「閉じてよいもの」「閉じてはいけないもの」を理由付きで分類してください。
ss の出力を一行ずつ解説させる——これは本章で最も価値のある使い方だ。自分の目には記号の羅列でも、Claudeに渡せば「この耳は要る、この耳は塞いでいい」という地図に変わる。
要らないと判明したサービスは、止めて、自動起動も切る。
# 例:印刷サーバー cups がサーバー機では不要だった場合
sudo systemctl disable --now cups
# もう待ち受けていないことを確認
ss -tlnp
disable --now は「いま止める(stop)」と「次回以降も起動しない(disable)」を一度にやる。攻撃面が一つ減った。
第二節 ファイアウォール——最小の許可から始める
攻撃面を引いたら、次は「残った耳のうち、誰に応答するか」を決める。これがファイアウォールの仕事だ。Debianでは ufw(Uncomplicated Firewall)が扱いやすい。
考え方は一つ。デフォルトはすべて拒否。必要なものだけを名指しで許可する。 ホワイトリスト方式だ。
sudo apt install ufw
ここで順番を絶対に間違えないこと。先に「全部拒否」を有効化してから SSH を許可すると、SSH で繋いでいる自分自身が締め出される。必ず allow ssh を先に書いてから enable する。
# 1. まず方針:入ってくる通信はデフォルト拒否、出ていく通信は許可
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 2. 自分が締め出されないよう、SSHを先に許可する(最重要)
sudo ufw allow ssh
# 3. ここでようやく有効化
sudo ufw enable
# 4. いまのルールを確認
sudo ufw status verbose
enable の直前に一拍置いて、allow ssh が入っているか必ず見返す癖をつける。リモートのサーバーでこれを間違えると、物理コンソールか復旧手段がない限り入れなくなる。
LAN内だけで使うサービスなら、「特定のネットワークからだけ許可する」と書ける。これが攻撃面をさらに絞る。
# 例:自宅LAN(192.168.1.0/24)からのみ、Webアクセスを許可
sudo ufw allow from 192.168.1.0/24 to any port 80
# 自宅LANからだけSSHを許可したい場合(公開しないサーバー向け)
sudo ufw allow from 192.168.1.0/24 to any port 22
from 192.168.1.0/24 は「このサブネット内の機器からだけ」という意味だ。外からはそもそも届かない設定になる。
Claudeに聞いてみよう②:自分の利用形態からufwルール一式を設計させる
私のDebian 13サーバーで ufw を設定します。利用形態は次の通りです:
- 設置場所:〔例:自宅LANの内側 / VPSでインターネットに公開〕
- 動かす予定のサービス:〔例:SSH、ファイル共有(Samba)、後でWebサーバー〕
- 管理する端末のIP帯:〔例:自宅LANは 192.168.1.0/24〕
締め出し事故を避ける順番を守った上で、
ufwのコマンドを上から順に実行できる形で出してください。 各コマンドが何を許可・拒否しているか、一行コメントを添えてください。
利用形態を正確に伝えれば、ルール一式がそのまま手元に降りてくる。自分はそれを一行ずつ吟味して実行する。 ファイアウォールは一文字の間違いが締め出しに直結するので、丸呑みせずレビューする姿勢が要る。
第三節 更新の自動化
守りで最も効くのに最も忘れられがちなのが、更新を当て続けることだ。攻撃の多くは、とっくに修正パッチの出ている既知の穴を狙う。穴は塞がれているのに、更新していないから開いたまま——これが最頻の事故だ。
サーバーは画面の前に座っていないことが多い。だから手動更新に頼らず、セキュリティ更新だけは自動で当たるようにする。Debianには unattended-upgrades がある。
sudo apt install unattended-upgrades
既定でセキュリティ更新を自動適用する設定になっているが、対話式に確認・有効化しておく。
sudo dpkg-reconfigure -plow unattended-upgrades
「自動更新を有効にしますか」に Yes と答えれば、以後はセキュリティ更新が静かに当たり続ける。何が当たったかは後から cat /var/log/unattended-upgrades/unattended-upgrades.log で読める。
ただし、更新には「再起動しないと効かないもの」がある。カーネルやglibcの更新がそれだ。再起動が必要かどうかは、ファイルの有無で分かる。
# このファイルが存在すれば、再起動が保留されている
ls -l /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required
# どのサービスが古いライブラリを掴んだままか教えてくれる
sudo apt install needrestart
sudo needrestart
needrestart は、再起動なしで済むものはサービス再起動だけで済ませ、本当に再起動が要るものだけを教えてくれる。サーバーをむやみに再起動したくないとき、この見分けは効く。
Claudeに聞いてみよう③:auth.log の不審な行を読み解く
私のDebian 13サーバーで、
sudo journalctl -u ssh --since "today"の出力に次のような行がありました:〔失敗ログ・見慣れない行をそのまま貼る〕これは通常のbotによる総当たりですか、それとも特定の標的型の兆候ですか。 一行ずつ、何を意味しているか(接続元、試したユーザー名、失敗理由)を説明してください。 いま私が取るべき対応があれば、危険度の高い順に教えてください。
本編第8章のトラブルシュートで身につけた「ログをそのままClaudeに渡す」作法が、ここでセキュリティの文脈に移る。読めない行を恐れず貼る。 Claudeはそれを「ただのbotの雑音」か「気にすべき兆候」かに腑分けしてくれる。
第四節 fail2ban という番犬
最後に番犬を置く。fail2ban は、ログを監視して同じIPからのログイン失敗が短時間に続いたら、そのIPを一定時間ブロックする道具だ。総当たり攻撃を物理的に遅くする。
ここで優先順位をはっきりさせておきたい。fail2ban は補助であって、主役ではない。 SSHの守りの本丸は、第4章でやった鍵認証+パスワード認証の無効化だ。パスワードを無効にしてしまえば、何万回総当たりされても通らない。fail2banはその上で、ログを汚し続けるbotを追い払い、ログを読みやすく保つために置く。順番は「鍵認証が先、fail2banは後」だ。
sudo apt install fail2ban
Debianの作法では、設定は jail.conf を直接いじらず、jail.local に上書き分だけ書く。SSH向けの最小設定はこれだけだ。
# /etc/fail2ban/jail.local を作る(中身は下記)
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[DEFAULT]
# BAN する時間(10分)
bantime = 10m
# この時間内に
findtime = 10m
# この回数失敗したら
maxretry = 5
[sshd]
enabled = true
EOF
sudo systemctl restart fail2ban
動いているか、誰かをBANしたかは、専用クライアントで確認する。
sudo fail2ban-client status sshd
Banned IP list: に並んだIPが、いま締め出されている相手だ。LAN内だけのサーバーでは何日も空のままだろう。公開サーバーでは、数時間で数件並び始める。番犬がちゃんと吠えている証拠だ。
Claudeに聞いてみよう④:自分のサーバーに守りが揃ったか点検させる
私のDebian 13サーバーの守りの現状です:
$ ss -tlnp 〔出力〕 $ sudo ufw status verbose 〔出力〕 $ sudo fail2ban-client status sshd 〔出力〕SSHは鍵認証〔有効/まだ〕、パスワード認証は〔無効/まだ〕です。 設置は〔LAN内のみ / 公開予定〕です。 この状態で、守りとして抜けている点・順番がおかしい点・過剰な点を指摘してください。 いま公開しても致命的な穴がないか、点検してください。
複数のコマンド出力をまとめて渡すと、Claudeは「攻撃面・ファイアウォール・番犬・SSH本体」を一枚の絵として点検できる。公開ボタンを押す前の最終チェックを、Claudeに頼む。 自分一人だと見落とす「順番の穴」を拾ってくれる。
まとめ
この章でやったこと:
- 公開前のSSH失敗ログを覗き、「静けさ」を基準として記録した
ss -tlnpで開いている耳を全部読み、要らないサービスをdisable --nowした- ufwで「デフォルト拒否+SSHのみ許可」から始め、LAN限定の許可も覚えた
- unattended-upgradesでセキュリティ更新を自動化し、再起動要否の見分け方を押さえた
- fail2banを番犬として置き、鍵認証との優先順位を整理した
手元に残ったもの:
- 攻撃面を絞った、ufwで守られたサーバー
- セキュリティ更新が自動で当たり続ける状態
- ログを読み、Claudeに渡して点検する作法(本編第8章からの継承)
- 「公開してよいか」をClaudeに点検させるチェックリスト的な対話
守りの土台ができた。次の第6章「サービスという単位」では、いよいよ何かを動かす。サーバーの中身は結局「systemdが面倒を見るプロセスの集まり」だ。起動・停止・自動起動・ログ・障害対応を、すべて同じ型——「サービス」という単位——で扱えるようにする。この章で systemctl disable --now を打ったその動詞が、次の章の入口になる。
シリーズ全体はClaudeと一緒に学ぶDebian サーバー編 一覧から辿れる。本編(デスクトップ編)は全章一覧へ。コメント・議論は Facebook グループへ:AISeed — 生物多様性・食料・AIと暮らし