第21章 セキュリティの基本(アカウントとサービス管理)
LPIC-1 110.1 / 110.2 相当
中級の最終章として、システムのセキュリティを維持するための基本的なタスクを扱います。
su と sudo の違い
| コマンド | 動作 |
|---|---|
su - | rootのパスワードを入力して、rootユーザーそのものに切り替える |
su alice(ハイフンなし) | rootのままaliceに切り替えるが、環境変数などはrootのものを引き継ぐ(ログインシェルとしての初期化を行わない) |
sudo コマンド | 自分自身のパスワードを入力し、許可された範囲でそのコマンドだけをroot権限で実行する |
sudo -i / sudo -s | root権限のシェルを開始する(ログイン扱いか否かで挙動がわずかに異なる) |
誰が sudo を使えるか、どのコマンドを許可するかは /etc/sudoers(編集は専用コマンド visudo を使うのが安全)で管理されます。sudoの大きな利点は、rootのパスワードそのものを共有しなくて済むことと、/var/log/auth.logなどに「誰が・いつ・何のコマンドを実行したか」の記録が残るため、複数の管理者がいる環境でも操作の追跡(アカウンタビリティ)が可能になる点です。su -でrootに切り替えてしまうと、その後の操作がすべて「root」としてしか記録されず、実際に誰が操作したのかが分かりにくくなります。
$ sudo visudo # /etc/sudoersを安全に編集
(エディタが開く。保存終了時に自動で文法チェックが行われ、エラーがなければそのままシェルへ戻る)
# /etc/sudoers への記述例
alice ALL=(ALL) ALL # aliceは全ホストで全コマンドをroot権限で実行可能
%staff ALL=(ALL) /usr/bin/systemctl restart nginx # staffグループはnginx再起動のみ許可
この行のこの値に注目: visudoは保存終了時に構文チェックを行う点が、直接vi /etc/sudoersで編集する場合との最大の違いです。構文エラーがある行を保存しようとすると、次のようなプロンプトが表示され、危険な状態のまま保存されるのを防ぎます。
$ sudo visudo
>>> /etc/sudoers: syntax error near line 28 <<<
What now?
Options are:
(e)dit sudoers file again
e(x)it without saving changes to sudoers file
(Q)uit and save changes to sudoers file (DANGER!)
What now?
この行のこの値に注目: ここでxを選べば変更を保存せずに終了でき、破損したsudoersファイルによってsudoそのものが使えなくなる事態を避けられます。Q(大文字)を選ぶとエラーを含んだまま強制的に保存してしまうため、通常はeで該当行を修正するかxで中断します。
パスワードの有効期限管理
$ chage -l alice # aliceのパスワード有効期限などを確認
Last password change : Aug 15, 2026
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
$ sudo chage -M 90 alice # 90日ごとにパスワード変更を要求(正常時は無出力)
$ sudo chage -m 7 alice # 変更後7日間は再度の変更を禁止(頻繁な変更の繰り返しを防ぐ。正常時は無出力)
$ sudo chage -W 14 alice # 期限の14日前から警告メッセージを表示(正常時は無出力)
$ sudo passwd -l alice # passwdコマンドでもアカウントロックが可能(-l)
passwd: password expiry information changed.
この行のこの値に注目: chage -lのMaximum number of days between password changeが既定値の99999(事実上無期限)のままだと、パスワード有効期限のポリシーが設定されていないことを意味します。-M/-m/-Wで数値を変更した後は、再度chage -l aliceを実行して反映されたことを確認できます。
パスワードの定期変更を強制することには、漏洩したパスワードの有効期間を限定できるというメリットがある一方、頻繁すぎる変更要求はユーザーが単純な使い回しパスワードを選びがちになるという副作用も指摘されています。近年はパスワードの複雑さや長さ、多要素認証の併用のほうを重視する考え方も広まっており、方針は組織のセキュリティポリシーに応じて検討する必要があります。
usermod -L/passwd -lは管理者が手動でアカウントをロックする操作です。ログイン失敗が一定回数続いた場合に自動でロックする仕組み(pam_faillock)は、PAMの応用として「DHCPサーバーとPAM認証」の章(上級編)で扱っています。
SUIDファイルの棚卸し
SUIDが設定された実行ファイルは、悪用されるとroot権限を奪われる糸口になりえます。定期的に棚卸しするのはセキュリティ管理の基本的なタスクの一つです。
$ find / -perm -4000 -type f 2>/dev/null # SUIDファイルを全体から検索
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/su
/usr/bin/mount
/usr/bin/umount
...(省略。ディストリの既定インストールでは20〜30件程度が一般的)
この行のこの値に注目: 2>/dev/nullで権限不足によるPermission deniedエラーを非表示にし、実際にヒットしたSUIDファイルだけを見やすくしています。一覧に見覚えのない実行ファイルパス(例えば/tmpや/home配下)が含まれている場合は、攻撃者による裏口(バックドア)の可能性を疑います。
ログイン履歴の監査
誰がいつログインしたか、現在誰がログインしているかを確認することは、不審なアクセスに気づく第一歩です。
| コマンド | 確認できる内容 |
|---|---|
who | 現在ログイン中のユーザー一覧 |
w | ログイン中のユーザーに加えて、実行中のコマンドも表示 |
last | 過去のログイン履歴(/var/log/wtmpを参照) |
lastb | ログイン失敗の履歴(/var/log/btmpを参照。要root権限) |
lastlog | 全ユーザーそれぞれの最終ログイン日時を一覧表示 |
$ last -n 5 # 直近5件のログイン履歴
alice pts/0 192.168.1.50 Fri Aug 21 09:00 still logged in
bob pts/1 203.0.113.10 Thu Aug 20 21:14 - 21:45 (00:31)
alice pts/0 192.168.1.50 Thu Aug 20 08:55 - 18:20 (09:25)
reboot system boot 6.8.0-49-generic Thu Aug 20 07:58 still running
alice pts/0 192.168.1.50 Wed Aug 19 09:02 - 18:10 (09:08)
$ lastb # ログイン失敗(総当たり攻撃の痕跡調査などに使う。要sudo)
admin ssh:notty 198.51.100.7 Fri Aug 21 07:12 - 07:12 (00:00)
root ssh:notty 198.51.100.7 Fri Aug 21 07:12 - 07:12 (00:00)
admin ssh:notty 198.51.100.7 Fri Aug 21 07:11 - 07:11 (00:00)
$ w
10:32:01 up 5 days, 3:11, 2 users, load average: 0.15, 0.22, 0.30
USER TTY FROM LOGIN@ IDLE WHAT
alice pts/0 192.168.1.50 09:00 0.00s w
この行のこの値に注目: lastのstill logged inは現在も接続中であることを示し、rebootの行はシステム再起動そのものの記録です。lastbでadminやrootのように実在しないはずのアカウント名への失敗が同一IP(この例では198.51.100.7)から短時間に連続している場合は、総当たり攻撃の典型的な痕跡です。
lastbの実行結果に見覚えのないIPアドレスから大量のログイン失敗が記録されている場合、そのホストからの総当たり攻撃(ブルートフォース攻撃)を受けている可能性があります。fail2banのようなツールを導入すると、一定回数以上ログインに失敗した送信元IPを自動的に一時ブロックし、こうした攻撃の影響を軽減できます。
不要なサービスの無効化とリソース制限
攻撃対象となりうる面(アタックサーフェス)を減らすため、使っていないサービスは停止・無効化しておくのが基本です。
$ systemctl list-units --type=service --state=running # 現在稼働中のサービス一覧
UNIT LOAD ACTIVE SUB DESCRIPTION
cron.service loaded active running Regular background program processing daemon
ssh.service loaded active running OpenBSD Secure Shell server
telnet.socket loaded active running Telnet Server Activation Socket
$ sudo systemctl stop telnet.socket # 停止(正常時は無出力)
$ sudo systemctl disable telnet.socket # 次回起動時に自動起動しないようにする
Removed "/etc/systemd/system/sockets.target.wants/telnet.socket".
この行のこの値に注目: 一覧のSUB列がrunningのサービスが実際に稼働中のものです。この中にtelnet.socketのような平文通信の古いサービスが含まれていれば、使用実態がない限り停止・無効化の対象になります。disable実行時のRemoved "..."行は、自動起動用のシンボリックリンクが削除されたことを示します。
また、1ユーザーが使えるリソース(プロセス数やファイルディスクリプタ数など)を制限しておくことで、一部の暴走プロセスがシステム全体を巻き込むのを防げます。
$ ulimit -a # 現在のシェルに適用されているリソース制限を確認
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
open files (-n) 1024
max user processes (-u) 3813
stack size (kbytes, -s) 8192
$ ulimit -u 100 # 起動できる最大プロセス数を100に制限(このシェル限り。正常時は無出力)
この行のこの値に注目: ulimit -aのmax user processes (-u)とopen files (-n)が、それぞれこのシェルから起動できる最大プロセス数と開ける最大ファイル数の上限です。ulimit -u 100のように値を下げた後は、このシェルおよびその子プロセスだけに制限が適用され、シェルを終了すると元の値に戻ります。
恒久的な設定は/etc/security/limits.confに記述します(PAMのpam_limitsモジュール経由で適用されます)。
alice hard nproc 100
alice soft nofile 1024
/etc/nologinというファイルを作成しておくと、root以外の一般ユーザーのログインを一時的に禁止できます(メンテナンス作業時など)。ファイルの中身は、ログインを拒否された際にそのままメッセージとして表示されます。
スーパーサーバーの仕組み(inetd・xinetd・systemdソケット)
telnetやftpのような小規模なサービスを、常に個別のデーモンとして常駐させておくのはメモリの無駄になります。そこで古くから使われてきたのがスーパーサーバーという考え方です。特定のポートへの接続要求が来たときだけ、対応するサービスを起動する仕組みで、複数のサービスをまとめて1つのプロセスが待ち受けます。
歴史的には、まずinetdが使われ、その後アクセス制御やログ機能を強化したxinetdが広く使われるようになりました。現在の主流であるsystemdでは、この役割をsystemdソケットユニット(.socket)が引き継いでいます。
| 世代 | 特徴 |
|---|---|
inetd | 最も古い実装。/etc/inetd.confに1行1サービルで設定する。アクセス制御機能は乏しい |
xinetd | inetdの後継。/etc/xinetd.confと/etc/xinetd.d/以下の個別ファイルで、サービスごとに詳細なアクセス制御ができる |
| systemdソケットユニット | 現在の主流。.socketユニットが接続を待ち受け、実際の接続が来た時点で対応する.serviceユニットを起動する(ソケットアクティベーション) |
xinetdの設定は、/etc/xinetd.confに全体の既定値を置き、サービスごとの詳細を/etc/xinetd.d/以下の個別ファイルに記述するのが基本です。
/etc/xinetd.d/telnet(設定例)
service telnet
{
disable = yes
flags = REUSE
socket_type = stream
only_from = 192.168.1.0/24
no_access = 203.0.113.0/24
access_times = 08:00-19:00
instances = 10
}
| 設定項目 | 意味 |
|---|---|
disable | yesにするとそのサービスを無効化する(xinetd特有の逆説的な書き方に注意) |
only_from | 接続を許可する送信元(ホスト・ネットワーク) |
no_access | 接続を拒否する送信元 |
access_times | 接続を許可する時間帯 |
instances | 同時に起動できるインスタンス数の上限(過負荷防止) |
systemdソケットユニットでは、同様のことを.socketユニットのListenStream=でポートを指定し、対応する.serviceユニットが実際の処理を担う形で実現します。前の節で扱ったtelnet.socketの停止・無効化は、まさにこのソケットアクティベーションの仕組みを止める操作でした。
TCP Wrappersによるアクセス制御
TCP Wrappersは、libwrapというライブラリにリンクされたプログラムに対して、接続元ホストに基づくアクセス制御を提供する仕組みです。設定は/etc/hosts.allowと/etc/hosts.denyの2つのファイルで行います。
/etc/hosts.allow
sshd: 192.168.1.0/255.255.255.0, 127.0.0.1
ALL: 192.168.1.100
/etc/hosts.deny
ALL: ALL
評価はまずhosts.allowを上から順に確認し、一致するルールがあればそこで許可が確定します。一致しなければ次にhosts.denyを確認し、一致すれば拒否、どちらにも一致しなければ許可される、という順序で処理されます。上の例のようにhosts.denyにALL: ALL(すべてのサービス・すべてのホストを拒否)と書き、hosts.allow側で必要な接続だけを個別に許可する「デフォルト拒否」の運用が定石です。
| キーワード | 意味 |
|---|---|
ALL | すべてのサービス、またはすべてのホストを表す |
LOCAL | ホスト名にドットを含まない、同一ネットワーク内のホストを表す |
EXCEPT | 「Aだが、Bを除く」という除外指定に使う(例:ALL EXCEPT 192.168.1.100) |
ホスト側の指定には、192.168.1.のような前方一致のワイルドカードや、*.example.comのようなドメイン指定も使えます。またspawnキーワードを使うと、接続があった際に任意のコマンドを実行させることもできます(不審な接続をログに記録する、通知を送るなど)。
sshd: 203.0.113.0/24: spawn /usr/local/bin/alert.sh %h &: deny
あるプログラムがTCP Wrappersの対象になるかどうかは、libwrapにリンクされているかどうかで決まります。lddコマンドで確認できます。
$ ldd $(which sshd) | grep wrap
libwrap.so.0 => /usr/lib/x86_64-linux-gnu/libwrap.so.0 (0x00007f2a1c000000)
この行のこの値に注目: 出力にlibwrap.so.0が含まれていれば、そのプログラムはTCP Wrappersによるアクセス制御の対象になります。含まれていなければ、hosts.allow/hosts.denyを設定しても効果がないため、ファイアウォールなど別の手段でアクセスを制限する必要があります。近年のディストリビューションではTCP Wrappers自体が非推奨・廃止されつつあり、同等の制御はファイアウォールやsystemdソケットユニットのIPAddressAllow=/IPAddressDeny=で行う流れになっています。
不要サービスの棚卸し手順
「使っていないはずのサービス」を客観的に洗い出すには、有効化されているユニットの一覧と、実際に外部からの接続を待ち受けているポートの一覧を突き合わせるのが確実です。
$ systemctl list-unit-files --state=enabled # 自動起動が有効なユニットの一覧
UNIT FILE STATE
cron.service enabled
ssh.service enabled
cups.service enabled
$ sudo ss -tulnp # 実際に待ち受けているポートとプロセスの一覧
Netid State Local Address:Port Peer Address:Port Process
tcp LISTEN 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))
tcp LISTEN 0.0.0.0:631 0.0.0.0:* users:(("cupsd",pid=915,fd=6))
この行のこの値に注目: ss -tulnpのLocal Address:Port列に並ぶポート番号のうち、心当たりのないものがあれば、対応するプロセス名(Process列)を手がかりに、systemctl list-unit-files --state=enabledの一覧と突き合わせて、そのサービスが本当に必要かどうかを判断します。631番でcupsd(印刷サービス)が待ち受けているのに、そのサーバーで印刷を使う予定がなければ、無効化の候補になります。
-t(TCP)、-u(UDP)、-l(LISTEN状態のみ)、-n(名前解決せず数値のまま表示)、-p(プロセス情報を表示、要root)というssのオプションの組み合わせは、旧来のnetstat -tulnpと同じ考え方で使えます。
ファイアウォールによる通信の制限
不要なサービスを止めるのと並んで、そもそも外部から到達できる通信を制限しておくこともセキュリティの基本です。Linuxではカーネルのnetfilterという仕組みを土台に、さまざまなフロントエンドツールでファイアウォールルールを管理します。
| ツール | 特徴 |
|---|---|
firewalld(firewall-cmd) | RHEL系で標準的に使われる、ゾーンという概念でルールをまとめて管理する仕組み |
ufw(Uncomplicated Firewall) | Ubuntuなどで使われる、iptablesをより簡単なコマンドで扱えるようにしたフロントエンド |
iptables / nftables | より低レベルで細かい制御ができる、伝統的または後継のパケットフィルタリングツール |
$ sudo firewall-cmd --list-all # 現在のルールを確認(Rocky Linux 9の例)
public (active)
target: default
interfaces: eth0
services: dhcpv6-client ssh
ports:
$ sudo firewall-cmd --add-service=http --permanent # HTTPを恒久的に許可
success
$ sudo firewall-cmd --reload # 設定を反映
success
$ sudo ufw allow 22/tcp # ufwでSSHポートを許可する例(Ubuntu 24.04の例)
Rule added
Rule added (v6)
$ sudo ufw status # ufwの現在の状態を確認
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
この行のこの値に注目: firewall-cmd系のコマンドは操作が成功するとsuccessとだけ表示されます。--list-allのservices行に列挙されているサービス名(許可済み)が現状のルールで、追加操作は--permanentを付けても--reloadするまでは反映されない点に注意します。ufw statusのStatus: activeはファイアウォールが有効であることを示し、その下のテーブルが実際に許可されているポート・送信元の一覧です。
「使っていないサービスを止める」(この章で先に扱った内容)と「使っているサービスであっても、必要な相手からの通信だけに絞る」(ファイアウォール)は、どちらも多層防御(defense in depth)という考え方の一部です。1つの対策だけに頼らず、複数の防御層を重ねることで、どこか一箇所が突破されても被害を最小限に抑えるという設計思想です。
iptables/nftablesでデフォルトポリシーをDROPに変更するような、より踏み込んだファイアウォール設定と、その際の自分自身を締め出さないための注意点は「ルーター設定とパケットフィルタリング」の章(上級編)で詳しく扱います。
セキュリティアップデートの適用
どれだけファイアウォールやアクセス制御を整えても、稼働中のソフトウェア自体に既知の脆弱性が残っていれば、そこを突かれてしまいます。初級の第9章で扱ったパッケージ管理の知識は、セキュリティの観点からも重要です。多くのディストリビューションには、セキュリティ関連のアップデートだけを抽出して確認する仕組みが用意されています。
$ sudo apt list --upgradable # 更新可能なパッケージの一覧(Ubuntu 24.04 LTSの例)
Listing... Done
openssl/noble-security 3.0.13-0ubuntu3.5 amd64 [upgradable from: 3.0.13-0ubuntu3.4]
libssl3t64/noble-security 3.0.13-0ubuntu3.5 amd64 [upgradable from: 3.0.13-0ubuntu3.4]
$ sudo dnf updateinfo list security # セキュリティ関連の更新のみを抽出(Rocky Linux 9の例)
FEDORA-EPEL-2026-a1b2c3d4e5 Important/Sec. openssl-3.2.2-1.el9.x86_64
FEDORA-EPEL-2026-f6g7h8i9j0 Moderate/Sec. curl-8.5.0-3.el9.x86_64
$ sudo unattended-upgrades --dry-run # 自動更新の動作を試験的に確認(Debian系)
Checking: openssl (3.0.13-0ubuntu3.4 => 3.0.13-0ubuntu3.5)
Packages that will be upgraded: openssl libssl3t64
Writing dpkg log to /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
この行のこの値に注目: apt list --upgradableの[upgradable from: ...]で現在のバージョンと更新後のバージョンを比較でき、パッケージ名に-securityが含まれていることからセキュリティ更新だと分かります。dnf updateinfo list securityの1列目は各アドバイザリの重要度(Important/Moderateなど)で、優先的に適用すべき更新を判断する材料になります。
本番環境では、すべての更新を無条件に自動適用するとサービスの動作に影響するリスクもあるため、「セキュリティパッチは自動または速やかに適用し、機能更新は動作確認をしたうえで計画的に適用する」という運用ルールを設けている現場が多く見られます。
最小権限の原則
セキュリティ対策の根底にある考え方の1つに「最小権限の原則(Principle of Least Privilege)」があります。これは、各ユーザーやプロセスには、その役割を果たすために本当に必要な権限だけを与え、それ以上は与えないという設計方針です。本章で扱ったsudoの許可範囲を必要最小限に絞る、サービス専用アカウントには対話ログインを許可しない、といった個別の設定は、すべてこの原則の実践例にあたります。「動かないから念のため全部の権限を与えておく」という判断は一時的には楽ですが、後から見直すことを忘れがちで、結果的にセキュリティ上の弱点として残り続けることが多いため、最初から必要な範囲だけを見極める習慣が重要です。