2022 年度 OSS リテラシー 3 : セキュリティ対策 (ポート)
開いているポートの確認
開いているポートを確認するには, netstat コマンドや nmap コマンドを用いる. なお, nmap コマンドはクラッキングの前段階として行われることが多いので, 無用の誤解を与えないために localhost 以外のホストに対して実行しないこと.
netstat
netstat コマンドは net-tools パッケージに含まれているので, それをインストールする.
$ sudo apt-get update $ sudo apt-get install net-tools
netstat とても強力かつ有用なツールであり, 多くのオプションが存在する. 詳細は man netstat コマンドを実行するとオンラインマニュアルが表示されるので それを参照して欲しいが, 以下に代表的なものを挙げる.
- -A : 接続状態を表示するアドレスファミリを指定する
- -I : 指定したNICの情報のみ表示する(ex. -Ieth0)
- -a : 全てのアクティブなソケットを表示する
- -c : 1秒ごとに更新しつつ表示する
- -e : 詳細情報を表示する
- -g : IPv4とIPv6のマルチキャストグループメンバーシップ情報を表示する
- -i : 全てのNICの状態テーブルを表示する
- -l : 接続待ち(LISTEN)状態にあるソケットのみ表示する
- -n : ホストやユーザーの名前解決を行わず数字のまま出力する
- -o : ネットワーキングタイマーの情報を出力する
- -p : ソケットが属すプログラムのPIDとプロセス名を表示する
- -r : ルーティングテーブルを表示する
- -s : 各プロトコルの統計情報を表示する
- -t : TCPソケットを表示する
- -u : UDPソケットを表示する
- -v : 詳細な情報を表示する
例えば, netstat -ntlp コマンドを実行するとポート一覧が表示される. 最初の文字列が tcp は IP v4 を意味し, tcp6 は IPv6 を意味する.
$ netstat -ntl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN tcp6 0 0 :::80 :::* LISTEN tcp6 0 0 :::22 :::* LISTEN tcp6 0 0 :::3000 :::* LISTEN tcp6 0 0 ::1:25 :::* LISTEN tcp6 0 0 :::443 :::* LISTEN
上記の例では, ポート 22, 80, 443, 3000 が全ての IP アドレス (0.0.0.0) に 対して開いており, ポート 25, 3306 が自ホスト (127.0.0.1) に対してのみ開 かれていることがわかる. 上記の例ではポート 80, 443, 3000 が IP v6 で のみ開かれているように書かれているが, 実際には IP v4 に対してもこれらの ポートは開かれている (後述の nmap で確かめられる.netstat の出力がこのようになる理由は不明)
ここでポート番号とサービス名との対応が問題になるが, 基本的なポートについては /etc/services ファイルにその対応が書かれている. less などのページャーで /etc/services の中身を確認すると良い. なお, /etc/services に書かれているのはあくまで「代表的」なものであり, ポート番号 3000 は載っていない.
$ less /etc/services ...(略)... daytime 13/tcp daytime 13/udp netstat 15/tcp qotd 17/tcp quote msp 18/tcp # message send protocol msp 18/udp chargen 19/tcp ttytst source chargen 19/udp ttytst source ftp-data 20/tcp ftp 21/tcp fsp 21/udp fspd ssh 22/tcp # SSH Remote Login Protocol telnet 23/tcp smtp 25/tcp mail ...(略)...
いちいち /etc/services を確認しなくても,netstat に権限やオプションをつけることでポート番号と対応するサービス やプログラム名を一度に表示することができる. 以下を試してみよ.
$ netstat -tl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 localhost:mysql 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN tcp 0 0 localhost:smtp 0.0.0.0:* LISTEN tcp6 0 0 [::]:http [::]:* LISTEN tcp6 0 0 [::]:ssh [::]:* LISTEN tcp6 0 0 [::]:3000 [::]:* LISTEN tcp6 0 0 localhost:smtp [::]:* LISTEN tcp6 0 0 [::]:https [::]:* LISTEN $ sudo netstat -ntlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 9414/mariadbd tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 37955/sshd: /usr/sb tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 629/exim4 tcp6 0 0 :::80 :::* LISTEN 39359/apache2 tcp6 0 0 :::22 :::* LISTEN 37955/sshd: /usr/sb tcp6 0 0 :::3000 :::* LISTEN 36702/grafana-serve tcp6 0 0 ::1:25 :::* LISTEN 629/exim4 tcp6 0 0 :::443 :::* LISTEN 39359/apache2 $ sudo netstat -tlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 localhost:mysql 0.0.0.0:* LISTEN 9414/mariadbd tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN 37955/sshd: /usr/sb tcp 0 0 localhost:smtp 0.0.0.0:* LISTEN 629/exim4 tcp6 0 0 [::]:http [::]:* LISTEN 39359/apache2 tcp6 0 0 [::]:ssh [::]:* LISTEN 37955/sshd: /usr/sb tcp6 0 0 [::]:3000 [::]:* LISTEN 36702/grafana-serve tcp6 0 0 localhost:smtp [::]:* LISTEN 629/exim4 tcp6 0 0 [::]:https [::]:* LISTEN 39359/apache2
なお,"Local Address" (内部アドレス) の部分をみると,接頭詞が 0.0.0.0 のものと,localhost (127.0.0.1) の 2 種類あることがわかる. 0.0.0.0 はホスト内部・外部の両方からアクセス可能なポート,localhost (127.0.0.1) はホスト内からのみアクセス可能なポートである. 外部に不用意に開放されているポートが無いかの確認をしておくことが重要である.なお,ポートの閉め方については後述する.
nmap
次に nmap を使って開いているポートを確認する. まずは nmap のインストールを行う.
$ sudo apt-get update $ sudo apt-get install nmap
nmap は netstat と異なり, ホスト名もしくは IP アドレスを引数に与えることになる. 自ホストを意味する localhost (もしくは 127.0.0.1) を引数に与えた場合には, 以下のように開いているポートが一覧される. 22, 25, 80, 443, 3000, 3306 が開いていることがわかる. なお,ポート 3000 は grafana であるが,ここでは ppp と示されている (3000 は 特定のサービスというよりも,最近の多くのサービスで使われることが多く, それゆえに異なるサービス名が表示されることがある).引数を localhost と した場合に表示されるポート番号は,"netstat -lp" コマンドを実行されたときの "Local Address" (内部アドレス) の接頭詞が 0.0.0.0 のものと,localhost (127.0.0.1) のものである.
$ nmap localhost PORT STATE SERVICE 22/tcp open ssh 25/tcp open smtp 80/tcp open http 443/tcp open https 3000/tcp open ppp 3306/tcp open mysql
イーサネットの口 (ens192) に割り当てた IP アドレス (10.176.0.XXX, XXX は自分のに変更すること) を指定することもでき, その場合は以下のようになる. 引数を IP アドレスに した場合に表示されるポート番号は,"netstat -lp" コマンドを実行されたときの "Local Address" (内部アドレス) の接頭詞が 0.0.0.0 のものだけである.
$ nmap 10.176.0.XXX PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https 3000/tcp open ppp
上記を見比べると, localhost では 3306 が表示されるが 10.176.0.XX では それが表示されていない. これはポート 3306 (mysql (DB)) には 自ホストからのみアクセスできることを意味する (= ネットワーク経由でのアクセスはできない). 加えて, nmap コマンドの出力は前述の netstat -ntlp の出力と整合的であることがわかる.
ポートを閉じる (1) : /etc/init.c/ 以下のスクリプトを用いる
サービス単位でポートを閉めることができる. /etc/init.d 以下にサービスをスタート/ストップするためのスクリプトが 置かれているので, それを使えば良い.
例えば HTTP (80 ポート) を閉じる場合を考える. まず始めに 80 番ポートの支配しているプログラム名を netstat で調べる. プログラム名が apache2 であることがわかる.
$ sudo netstat -ntlp| grep 80 tcp6 0 0 :::80 :::* LISTEN 605/apache2
次に, /etc/init.d 以下を眺めると, apache2 が存在することがわかる.
$ ls /etc/init.d/ apache-htcacheclean cron hwclock.sh networking screen-cleanup udev zabbix-server apache2 dbus keyboard-setup.sh procps snmpd vmware-tools apparmor exim4 kmod rsync ssh x11-common console-setup.sh grafana-server mysql rsyslog sudo zabbix-agent
通常, プログラム名とスクリプト名は一致するので, 今回は /etc/init.d/apache2 を使えば良い. 結果的に 80 番ポートを閉める (サービスを止める) には以下のようにする. なお,apache2 は HTTP の 80 番ポートだけでなく HTTPS の 443 番ポートも 管理しているので,apache2 を止めると結果的に 80, 443 の両方が閉じる.
$ sudo /etc/init.d/apache2 stop [ ok ] Stopping apache2 (via systemctl): apache2.service.
改めて netstat もしくは nmap コマンドを実行すると, 80 番ポートと 443 番ポートが閉まった (= 80, 443 ポートを使う apache2 が停止した) ことがわかる.
$ nmap 10.176.0.XXX (自分の IP) PORT STATE SERVICE 22/tcp open ssh 3000/tcp open ppp
再びサービスを開始する場合はスクリプトの引数に start を与える.
$ sudo /etc/init.d/apache2 start [ ok ] Starting apache2 (via systemctl): apache2.service.
再び netstat もしくは nmap を使うと 80 ポートが再び開いたことがわかる.
$ nmap 10.176.0.XXX (自分の IP) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https 3000/tcp open ppp
ポートを閉じる (2) : ファイヤーウォールを用いる
ファイヤーウォールを用いて明示的に開けるポート・閉めるポートを指定するのが Linux 管理運営上は望ましい.ソフトウェアインストール時に,管理者が気づかない うちにポートを開けてしまうことがあるためである.
本演習ではファイヤーウォールとして ufw(Uncomplicated FireWall)を用いる. ufw は Debian 系の Linux ディストリビューションで標準的に利用できるツールであり, iptables のフロントエンドとなっている. ufw を用いると,「外部からの接続は基本的に受け付けない」,「sshだけは許す」 などといった設定を,iptables にくらべて格段に少ない操作で実現できる.
インストール
$ sudo -s # apt-get update # apt-get install ufw
全ての「受信パケット」を「許可」もしくは「拒否」する場合は以下のように書く.
# ufw default allow incoming (全ての受信パケットを許可) # ufw default deny incoming (全ての受信パケットを拒否)
全ての「送信パケット」を「許可」もしくは「拒否」する場合は以下のように書く.
# ufw default allow outgoing (全ての送信パケットを許可) # ufw default deny outgoing (全ての送信パケットを拒否)
特定のサービスへの送受信を許可もしくは拒否する場合は, ポート番号を指定するかサービス名を指定する.
# ufw allow out 'WWW Full' (http, https を内部ネットワークへ送信することは許可する) # ufw reject out 'WWW Full' (http, https を内部ネットワークへ送信することは許可しない) # ufw reject out to 10.0.0.0/8 app 'WWW Full' (http, https を内部ネットワークへ送信することは許可しない) # ufw allow in 'WWW Full' (http, https の受信を許可する) # ufw reject in 'WWW Full' (http, https の受信を許可する) # ufw reject in from 10.0.0.0/8 app 'WWW Full' (http, https の受信を許可する) # ufw allow in 22 # ufw reject in 22
送受信の両方を許可もしくは拒否する場合は以下のようにする.
# ufw allow SSH (SSH の送受信を許可) # ufw deny SSH (SSH の送受信を拒否)
サービス名として使える名称のリストは以下のコマンドで把握することができる.
# ufw app list AIM Bonjour CIFS DNS Deluge IMAP IMAPS IPP ...(以下略)...
その他にも以下のようなコマンドが使える.
# ufw logging on (ログの有効化) # ufw reset (設定のリセット)
IPv6 を使わないようにする場合は,設定ファイルを編集する.
# vi /etc/default/ufw IPV6=no <-- この行を no に変更.
設定を終えたら ufw を有効化する.
# ufw enable (ufw を有効化) # ufw reload (ufw の設定を再読み込み)
確認は以下のコマンドで行う. 例えば以下のような表示がなされる.
# ufw status verbose Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- 3000 ALLOW IN Anywhere 22/tcp (SSH) ALLOW IN Anywhere 80,443/tcp (WWW Full) ALLOW IN Anywhere # exit $
課題
ufw の適切な設定を考え,そのためのコマンドを実行せよ.
なお,これを上手く設定できたかは,WBT 上のチェックスクリプトで確認できる. もし,スクリプトで NG が出たら設定を見直すこと!
(参考) ポートを閉じる (3) : TCP Wrapper によるアクセス制限
TCP Wrapper を用いて ssh 接続を許可するホストを限定する方法を述べる . 本講義では行わなくて良い.
TCP Wrapper の設定ファイルは /etc/hosts.allow と /etc/hosts.deny の 2 つである. 動作としては /etc/hosts.allow に書かれたアクセスルールが最初に適用され, 次に /etc/hosts.deny に書かれたアクセスルールが適用される.
まず, /etc/hosts.allow において, サーバ sky.epi.it.matsue-ct.jp (10.100.100.1) のみに SSH アクセスの許可を出すことにする.
$ sudo -s # vi /etc/hosts.allow sshd: 10.100.100.6 (末尾に追記)
書式としては, コロンの前にデーモン名 (SSH ならば sshd (ssh daemon)),
コロンの後ろにホスト名である. ホスト名には .matsue-ct.jp のように,
matsue-ct.jp のドメインを持つ全てのホスト, という書き方もできる.
[注: 書式的にはドメインで書いても良いはずだが, IP で指定しないとうまく動かなかった]
次に, /etc/hosts.deny において, 全てのホストからの接続を禁止する 設定を行う. ALL: ALL とすると, 外部の全てのホストから, 全てのデーモン (ポート) へのアクセスを拒否することとなる.
# vi /etc/hosts.deny ALL: ALL (末尾に追記)
TCP wrapper は再起動は必要なく, 編集したファイルの内容は直ちに適用される.
以上の設定で, sky.epi.it.matsue-ct.jp 以外はラズパイのどのポートにも アクセスできなくなったはずである. 動作確認として, 友人のラズパイに SSH 接続してみよ. 正しく設定されていれば, 以下のように Connection がリセットされ, SSH 接続できないことがわかる.
$ ssh -l pi XX.XX.XX.XX (友人のラズパイの IP) ssh_exchange_identification: read: Connection reset by peer
さらに確認として, サーバからはラズパイに対して ssh 接続できることを確かめる.
$ ssh -l jxxxx sky.epi.it.matsue-ct.jp ...(略)... sky:~$ ssh -l hogehoge XX.XX.XX.XX (自分のラズパイの IP) Enter passphrase for key '/home/jxxxx/.ssh/id_rsa': (パスフレーズ入力) ...(略)... $ (ラズパイに再度ログイン) $ exit (ラズパイからサーバへ) sky$ exit (サーバからラズパイへ) $