2023 年度 OSS リテラシー 3 : ポート管理

開いているポートの確認

開いているポートを確認するには, netstat コマンドや nmap コマンドを用いる. なお, nmap コマンドはクラッキングの前段階として行われることが多いので, 無用の誤解を与えないために localhost 以外のホストに対して実行しないこと.

netstat

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

  稼働中のインターネット接続 (サーバのみ)
  Proto 受信-Q 送信-Q 内部アドレス            外部アドレス            状態
  tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN
  tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
  tcp6       0      0 :::4369                 :::*                    LISTEN
  tcp6       0      0 ::1:631                 :::*                    LISTEN
  tcp6       0      0 :::22                   :::*                    LISTEN

上記の例では, ポート 22, 4369 が全ての IP アドレス (0.0.0.0) に 対して開いており, ポート 631 が自ホスト (127.0.0.1, ::1:) に対してのみ開 かれていることがわかる. 上記の例ではポート 4369 が 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
   ...(略)...

631 ポートは Internet Printing Protocol であることが,/etc/services に書かれている. このポートは名前の通り,ネットワークプリンタのためのポートである. 検索すればすぐに分かるが,このポートを管理しているサーバソフトウェアは cups (Common UNIX Printing System) である.

$ grep 631 /etc/services 

  ipp        631/tcp             # Internet Printing Protocol

また,4369 ポートは, Erlang で使うものらしい.以下のように /etc/services で確認できる.

$ grep 4369 /etc/services 

  epmd        4369/tcp         # Erlang Port Mapper Daemon

Erlang に関係するパッケージは dpkg -l コマンドで確認できる.

$ dpkg -l | grep Erlang

  ii  erlang-base                           1:23.2.6+dfsg-1                   armhf        Erlang/OTP virtual machine and base applications
  ii  erlang-crypto                         1:23.2.6+dfsg-1                   armhf        Erlang/OTP cryptographic modules
  ii  erlang-syntax-tools                   1:23.2.6+dfsg-1                   armhf        Erlang/OTP modules for handling abstract Erlang syntax trees

今回は次の「穴埋め (2)」で SSH 以外のポートは閉めるので, 631, 4369 ポートはこのままにしておく.

いちいち /etc/services を確認しなくても,netstat に権限やオプションをつけることでポート番号と対応するサービス やプログラム名がある程度はわかる.管理者権限で以下を試してみるとよい.

$ sudo netstat -ntlp

  稼働中のインターネット接続 (サーバのみ)
  Proto 受信-Q 送信-Q 内部アドレス            外部アドレス            状態        PID/Program name
  tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      611/cupsd
  tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1742/sshd: /usr/sbi
  tcp6       0      0 :::4369                 :::*                    LISTEN      1/init
  tcp6       0      0 ::1:631                 :::*                    LISTEN      611/cupsd
  tcp6       0      0 :::22                   :::*                    LISTEN      1742/sshd: /usr/sbi

nmap

次に nmap を使って開いているポートを確認する. まずは nmap のインストールを行う.

$ sudo apt update

$ sudo apt install nmap

nmap は netstat と異なり, ホスト名もしくは IP アドレスを引数に与えることになる. 自ホストを意味する localhost (もしくは 127.0.0.1) を引数に与えた場合には, 以下のように開いているポートが一覧される. 引数を localhost と した場合に表示されるポート番号は,"netstat -lp" コマンドを実行されたときの "Local Address" (内部アドレス) の接頭詞が 0.0.0.0 のものと,localhost (127.0.0.1) のものである.

$ nmap localhost -p 0-5000

  PORT    STATE SERVICE
  22/tcp  open  ssh
  631/tcp open  ipp
  4369/tcp open  epmd

イーサネットの口 (ens192) に割り当てた IP アドレス (10.162.XXX.XXX, XXX は自分のに変更すること) を指定することもでき, その場合は以下のようになる. 引数を IP アドレスに した場合に表示されるポート番号は,"netstat -lp" コマンドを実行されたときの "Local Address" (内部アドレス) の接頭詞が 0.0.0.0 のものだけである.

$ nmap  10.162.XXX.XXX

  PORT     STATE SERVICE
  22/tcp   open  ssh
  4369/tcp open  epmd

上記を見比べると, localhost では 631 が表示されるが 10.162.XXX.XXX では それが表示されていない. これはポート 631 は 自ホストからのみアクセスできることを意味する (= ネットワーク経由でのアクセスはできない). 加えて, nmap コマンドの出力は前述の netstat -ntlp の出力と整合的であることがわかる.

ポートを閉じる (1) : /etc/init.c/ 以下のスクリプトを用いる

サービス単位でポートを閉めることができる. /etc/init.d 以下にサービスをスタート/ストップするためのスクリプトが 置かれているので, それを使えば良い.

例えば 631 ポートは上記で cups であることが分かっているので, 以下のようにポートを閉め,その確認を行うことができる.

$ sudo -s

# /etc/init.d/cups stop

  Stopping cups (via systemctl): cups.service.

# nmap localhost -p 0-5000

  PORT     STATE SERVICE
  22/tcp   open  ssh
  4369/tcp open  epmd

再びサービスを開始する場合はスクリプトの引数に start を与える.

# /etc/init.d/cups start

  Starting cups (via systemctl): cups.service.

# nmap localhost -p 0-5000

  PORT     STATE SERVICE
  22/tcp   open  ssh
  631/tcp  open  ipp
  4369/tcp open  epmd

# exit

ポートを閉じる (2) : ファイヤーウォールを用いる

ファイヤーウォールを用いて明示的に開けるポート・閉めるポートを指定するのが Linux 管理運営上は望ましい.ソフトウェアインストール時に,管理者が気づかない うちにポートを開けてしまうことがあるためである.

本演習ではファイヤーウォールとして ufw(Uncomplicated FireWall)を用いる. ufw は Debian 系の Linux ディストリビューションで標準的に利用できるツールであり, iptables のフロントエンドとなっている. ufw を用いると,「⁠外部からの接続は基本的に受け付けない」,「⁠sshだけは許す」 などといった設定を,iptables にくらべて格段に少ない操作で実現できる.

インストール

$ sudo -s
# apt update
# apt install ufw
具体的な設定は課題に回すため,以下では 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
$ 

課題 2 : ポート管理

上記の演習を通して,以下を行いなさい.

  1. ラズパイに対して,ufw で「受信は SSH のみ」「送信は全て OK」という設定を行いなさい.
  2. nmap もしくは netstat コマンドを用いて空きポートを調べ,ネットワーク経由では SSH 以外のサービスにはアクセスできなくなっていること (localhost に対しては SSH 以外のポートも開いていること) を確認しなさい.

設定がうまくいったことを示すため,以下が含まれるターミナルのスナップを提出すること.なぜうまくいっていると判断したかの根拠を簡潔にまとめ,その文章も提出すること.

  • "ufw status verbose" コマンドの実行結果
  • netstat もしくは nmap コマンドを用いて開きポートを調べたときの実行結果
  • ラズパイのホスト名
  • ラズパイの IP アドレス
  • ラズパイにログインしているユーザ名

(参考) ポートを閉じる (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  (サーバからラズパイへ)

$