自宅サーバーをUFWで守る!ファイアウォール設定で不要なポートをブロックして安全に外部公開する

未分類

前回の記事「自宅サーバーをSSL化する!NginxリバースプロキシとLet’s Encrypt設定ガイド」でHTTPS化まで完了しましたが、外部公開したサーバーのセキュリティ対策はまだ終わっていません。
Nginxのリバースプロキシ設定でGrafanaやFlask APIを外部からアクセスできないようにしてあっても、サーバーのポートが開いていれば直接アクセスできてしまうリスクがあります。
本記事では、Ubuntu ServerやRaspberry Pi OS(Debian系)に標準で使えるファイアウォールツールUFW(Uncomplicated Firewall)を使って、必要なポートだけを開放し、それ以外をすべてブロックする設定を解説します。
ポート転送(HGW&Deco設定)→ SSL化(Nginx&Let’s Encrypt)→ ファイアウォール(本記事)という3記事セットで、安全な自宅サーバー公開が完成します。

なぜこの機能を作ったのか?

自宅のNUCBox G3 Plusをインターネットに公開したことで、外出先からGrafanaダッシュボードを確認したり、Minecraft Bedrockサーバーに友人を招待できるようになりました。
しかし、外部公開するということは世界中からアクセスが来るということでもあります。
GrafanaはNginxリバースプロキシ経由でLAN内からのみアクセスを想定していますが、サーバーの3000番ポートが直接インターネットに露出していれば、その保護は意味をなしません。
また、SSH(ポート22)が外部に開いていれば、ブルートフォース攻撃やパスワードスプレー攻撃の標的になります。
外部に公開したからこそ、ファイアウォールで不要なポートをブロックすることが必須になりました。

外部に公開したサーバーは、不要なポートをしっかり閉じてセキュリティを確保したい!

実現したいこと

  • UFWでデフォルトの受信をすべてブロックし、必要なポートだけを許可する
  • SSHはLAN内(192.168.68.0/24)からのみアクセス可能にして、外部からの総当たり攻撃を防ぐ
  • HTTP(80)とHTTPS(443)は外部公開する(Let’s Encrypt認証とウェブアクセスのため)
  • GrafanaはLAN内からのみ許可し、インターネットへの直接露出を防ぐ
  • Flask API(8000番)はlocalhost専用のため、UFWルールでも外部・LAN双方からブロックする
  • Minecraft Bedrock(19132/UDP)は外部公開する

この記事でわかること

  • UFWとiptablesの関係、パケットフィルタリングの基本的な仕組み
  • 「最小権限の原則」に基づいたファイアウォールルールの設計方法
  • 送信元IPアドレスを制限したルール(LAN内のみ許可)の書き方
  • UFWのインストール確認から有効化までの手順
  • SSHセッション中にUFWを有効化する際のロックアウト対策
  • CMAN・nmapを使った外部からのポート疎通確認方法

必要な準備と用意するもの

ハードウェア
  • NUCBox G3 Plus(本記事のサーバー、IP: 192.168.68.89)
    • Ubuntu Server または Raspberry Pi OS(Debian系)がインストール済みであること
  • TP-Link Deco XE75(WiFiルーター)
    • LAN サブネット: 192.168.68.0/24
ソフトウェア/サービス
  • UFW(Uncomplicated Firewall)
    • Ubuntu Server には標準でインストール済み。Raspberry Pi OSの場合は別途インストールが必要(後述)
  • Nginx(リバースプロキシとして稼働済み)
  • nmap(ローカルからのポート確認用)
    • sudo apt install nmap でインストール
  • CMAN(外部からのポート確認サービス)

前提記事について
本記事は下記の2記事の続きです。ポート転送とNginx/SSL設定が完了している前提で説明しています。
・「自宅サーバーをネットに公開する!HGW&Decoによる二重NAT環境のポート転送設定ガイド」
・「自宅サーバーをSSL化する!NginxリバースプロキシとLet’s Encrypt設定ガイド」

完成イメージ

UFW設定完了後のファイアウォールルールは以下の通りです。必要最小限のポートだけを開放し、それ以外はすべてブロックします。

サービスポートプロトコルアクセス範囲理由
SSH22TCPLAN内のみ(192.168.68.0/24)外部からの総当たり攻撃を防ぐ。Decoが52341→22に変換
Nginx (HTTP)80TCP外部OKLet’s Encrypt証明書更新とHTTPS転送のため
Nginx (HTTPS)443TCP外部OKメインウェブサービス
Grafana3000TCPLAN内のみ(192.168.68.0/24)外部アクセスはNginxリバースプロキシ経由のみ
Flask API8000TCPブロック(localhost専用)Nginxリバースプロキシ経由のみ。UFWルール追加不要
Minecraft Bedrock19132UDP外部OKDecoが52351→19132に変換して転送
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    192.168.68.0/24       # SSH LAN only
80/tcp                     ALLOW IN    Anywhere              # HTTP
443/tcp                    ALLOW IN    Anywhere              # HTTPS
3000/tcp                   ALLOW IN    192.168.68.0/24       # Grafana LAN only
19132/udp                  ALLOW IN    Anywhere              # Minecraft Bedrock

システムの仕組み

UFWとiptablesの関係

Linuxのパケットフィルタリングは、カーネル内のnetfilterというフレームワークが担っています。iptablesはnetfilterを操作するコマンドラインツールですが、構文が複雑でルールの管理が煩雑です。
UFW(Uncomplicated Firewall)はiptablesのフロントエンドとして、シンプルなコマンドでファイアウォールを設定できるツールです。内部的にはiptablesルールを生成しますが、ユーザーはUFWの簡単なコマンドだけを覚えれば済みます。

パケットフィルタリングとは?
ネットワークの通信はIPパケットという単位でやり取りされます。ファイアウォールはこのパケットを見て「許可するか(ALLOW)」「拒否するか(DENY)」を判断します。送信元IP・宛先IP・ポート番号・プロトコルなどの条件でフィルタリングできます。

デフォルトポリシーの考え方

UFWには「デフォルトポリシー」という概念があります。個別ルールに一致しないパケットをどう扱うかを決める設定です。

  • deny incoming:受信パケットはデフォルトで全ブロック(個別に許可したものだけ通す)
  • allow outgoing:送信パケットはデフォルトで全許可(サーバーからの通信は自由)

「deny incoming」がセキュリティの基本
受信をデフォルトでブロックしておくことで、新しいサービスをインストールした際に意図せずポートが外部に露出するリスクを防げます。許可したいポートは明示的に追加するため、設定の意図が明確になります。

NginxリバースプロキシとUFWの組み合わせ

本環境ではNginxがリバースプロキシとして動作しており、外部からのHTTPS(443)リクエストをGrafana(3000)やFlask API(8000)に転送しています。
UFWはNginxへの443番通信は許可しますが、GrafanaやFlask APIへの直接アクセスはブロックします。これにより、外部からは必ずNginxを経由する構成となり、Nginxの認証・アクセス制御が確実に機能します。

インターネット
    ↓ HTTPS (443)
UFW → ALLOW(外部からのアクセスを許可)
    ↓
Nginx (リバースプロキシ)
    ↓ localhost:3000        ↓ localhost:8000
Grafana                   Flask API
(UFWはlocalhost通信を制限しない)(UFWはlocalhost通信を制限しない)

※ 外部からの直接アクセス(192.168.68.89:3000など)はUFWがブロック

実装のポイント

最小権限の原則
必要最小限のポートだけを開放し、それ以外はすべてブロックする。これはセキュリティの基本原則「最小権限の原則(Principle of Least Privilege)」です。使っていないサービスのポートは絶対に開けない。

SSHはLAN内に限定する重要性
SSHポート(22)を外部に開放すると、世界中のボットによる総当たり攻撃(ブルートフォース攻撃)のターゲットになります。本環境ではDecoのポート転送で別ポート(52341)経由でSSHアクセスできるため、標準の22番ポートをLAN内のみに限定することで外部攻撃のリスクを大幅に下げられます。

Flask APIをlocalhost専用にする理由
Flask APIはNginxリバースプロキシ経由でのみアクセスさせる設計のため、UFWでも外部・LAN双方からのアクセスをブロックします。サービス側(Flask)もlocalhostのみでListenする設定(host='127.0.0.1')と組み合わせることで二重の防御になります。

Grafanaの3000番ポートについて
Grafanaへの外部アクセスはNginxの443番経由で行いますが、万が一の設定ミスに備えてUFWでも3000番をLAN内のみに限定しています。「NginxだけでなくUFWでも守る」という多層防御(Defense in Depth)の考え方です。

事前準備

UFWのインストール確認

Ubuntu Serverには標準でUFWがインストールされています。まずインストール状況と現在のステータスを確認します。

# UFWのバージョン確認(インストール済みか確認)
ufw --version

# UFWの現在のステータス確認
sudo ufw status

UFWが未インストールの場合(Raspberry Pi OSなど)は以下でインストールします。

sudo apt update
sudo apt install ufw

UFW未インストールの場合の出力例
bash: ufw: command not foundsudo apt install ufw でインストールしてください。
UFWインストール済み・無効状態の出力例
Status: inactive → まだ有効化されていない状態。この記事の手順で設定・有効化します。

現在のネットワーク設定を確認する

UFWを設定する前に、サーバーのIPアドレスとSSH接続状況を確認しておきます。特にSSHで接続中の場合は、SSHを許可するルールを追加してからUFWを有効化することが重要です(後述の注意点を参照)。

# サーバーのIPアドレスを確認
ip addr show

# 現在のSSH接続を確認(接続中のセッションが表示される)
who

注意点:SSHで接続中のロックアウトリスク

重要:SSHセッション中にUFWを有効化するとロックアウトされる危険があります!
SSHでサーバーに接続しながらUFWを有効化する場合、必ずSSHを許可するルールを追加してからUFWを有効化してください。
ルールを追加せずに sudo ufw enable を実行すると、デフォルトポリシー「deny incoming」によってSSH(22番ポート)がブロックされ、SSH接続が切断されてサーバーに入れなくなります。
この手順書では先にSSHルールを追加してから最後にUFWを有効化する順番になっていますが、必ず手順通りに実行してください。

ロックアウトしてしまった場合の復旧方法
SSHで接続できなくなった場合は、サーバーに直接モニターとキーボードを接続してコンソールログインするか、クラウドサーバーならコンソール機能を使って以下を実行します。

sudo ufw disable ← UFWを無効化して通信を元の状態に戻す
sudo ufw reset ← すべてのルールをリセットする(より確実だが設定が消える)

その後、正しい順番でルールを追加してから再度有効化してください。

実装方法

① デフォルトポリシーを設定する

まず最初に、デフォルトのポリシーを設定します。受信をすべてブロックし、送信はすべて許可します。
この時点ではまだUFWは有効化していないため、SSH接続には影響ありません。

# 受信をデフォルトでブロック
sudo ufw default deny incoming

# 送信をデフォルトで許可
sudo ufw default allow outgoing

実行後、以下のように表示されれば成功です。

Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)
Default outgoing policy changed to 'allow'
(be sure to update your rules accordingly)

② SSH(LAN内からのみ)を許可する

UFWを有効化する前に必ずこのステップを実行してください。
SSHをLAN内のIPアドレス帯(192.168.68.0/24)からのみ許可します。

sudo ufw allow from 192.168.68.0/24 to any port 22 proto tcp comment 'SSH LAN only'

コマンドの意味
from 192.168.68.0/24:送信元をLANサブネット(192.168.68.0〜192.168.68.255)に限定
to any port 22:宛先ポート22番(SSH)
proto tcp:TCPプロトコル
comment 'SSH LAN only':ルールにコメントを付ける(管理しやすくなる)

③ HTTP(80番)を外部公開する

Let’s Encrypt(Certbot)によるSSL証明書の更新時にHTTPアクセスが必要です。また、HTTPでアクセスしてきたユーザーをHTTPSにリダイレクトするためにも80番を開放します。

sudo ufw allow 80/tcp comment 'HTTP'

④ HTTPS(443番)を外部公開する

ウェブサービスのメインポートです。Nginxがリバースプロキシとしてこのポートでリッスンしています。

sudo ufw allow 443/tcp comment 'HTTPS'

⑤ Grafana(3000番)をLAN内のみ許可する

GrafanaはNginxリバースプロキシ経由でHTTPS(443)から接続されますが、直接の3000番ポートアクセスはLAN内のみに制限します。

sudo ufw allow from 192.168.68.0/24 to any port 3000 proto tcp comment 'Grafana LAN only'

⑥ Minecraft Bedrock(19132/UDP)を外部公開する

Minecraft BedrockはUDPを使用します。Decoのポート転送で外部の52351番ポートから内部19132番に変換されるため、サーバー側では19132番を開放します。

sudo ufw allow 19132/udp comment 'Minecraft Bedrock'

Flask API(8000番)はUFWルールを追加しない
Flask APIはlocalhost(127.0.0.1)のみでListenしているため、UFWでLAN外からのアクセスは届きません。そのため、明示的なDENYルールの追加は不要です。デフォルトポリシー「deny incoming」で自動的にブロックされます。

⑦ UFWを有効化する

ここまでで必要なルールがすべて追加できました。いよいよUFWを有効化します。
コマンド実行時に確認メッセージが表示されるので y を入力してEnterを押します。

sudo ufw enable
Command may disrupt existing ssh and other connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

「Firewall is active and enabled on system startup」と表示されれば成功
UFWが有効になり、OS起動時に自動的に有効化されるように設定されました。SSHが②で許可済みのため、接続は維持されているはずです。

⑧ ルールを確認する

最後に、設定したルールが正しく反映されているか確認します。

sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    192.168.68.0/24       # SSH LAN only
80/tcp                     ALLOW IN    Anywhere              # HTTP
443/tcp                    ALLOW IN    Anywhere              # HTTPS
3000/tcp                   ALLOW IN    192.168.68.0/24       # Grafana LAN only
19132/udp                  ALLOW IN    Anywhere              # Minecraft Bedrock

上記の通りに表示されれば、ファイアウォールの設定は完了です。

コマンドをまとめて実行する場合

参考として、上記の全手順をまとめたコマンド一覧です。手順①〜⑧をまとめて実行する場合に使用してください。

# デフォルトポリシー設定
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH(LAN内からのみ)
sudo ufw allow from 192.168.68.0/24 to any port 22 proto tcp comment 'SSH LAN only'

# HTTP(外部公開 - Let's Encrypt認証とHTTPS転送用)
sudo ufw allow 80/tcp comment 'HTTP'

# HTTPS(外部公開 - メインウェブ)
sudo ufw allow 443/tcp comment 'HTTPS'

# Grafana(LAN内からのみ)
sudo ufw allow from 192.168.68.0/24 to any port 3000 proto tcp comment 'Grafana LAN only'

# Minecraft Bedrock(外部公開)
sudo ufw allow 19132/udp comment 'Minecraft Bedrock'

# UFW有効化
sudo ufw enable

# 確認
sudo ufw status verbose

SSHで接続中の場合、必ず sudo ufw allow from 192.168.68.0/24 to any port 22 proto tcp のSSH許可ルールを追加してから sudo ufw enable を実行してください。順番を間違えるとSSHでログインできなくなります。

動作確認

① CMANで外部からのポート開放状況を確認する

CMAN(https://www.cman.jp/network/support/port.html)は、外部ネットワークから自宅サーバーの指定ポートにアクセスを試みて、開放されているかどうかを確認できるウェブサービスです。

各ポートで期待される結果は以下の通りです。

ポート期待される結果理由
80OPENHTTPアクセス可(NginxがHTTPSにリダイレクト)
443OPENHTTPSアクセス可(メインウェブ)
22CLOSEDLAN外からはUFWでブロック
3000CLOSEDLAN外からはUFWでブロック(Grafana直接アクセス不可)
8000CLOSEDlocalhost専用のためブロック(Flask API)

CMANを使う手順
1. ブラウザで https://www.cman.jp/network/support/port.html を開く
2. 「ポート番号」に確認したいポート番号を入力
3. 「確認する」ボタンをクリック
4. 「OPEN」または「CLOSED」の結果が表示される

Deco等で外部ポートを変換している場合(例:マイクラの52351番など)は、サーバーの内部ポートではなく、外部公開しているポート番号を入力して確認してください。

② nmapでローカルからポートをスキャンする

LAN内の別端末(または同じサーバー上)からnmapを使って、サーバーのポート開放状況を確認します。

# TCP ポートの確認(サービス名も表示)
nmap -sV 192.168.68.89

# UDP ポートの確認(Minecraft Bedrock)
sudo nmap -sU -p 19132 192.168.68.89
Starting Nmap 7.80 ( https://nmap.org )
Nmap scan report for 192.168.68.89
Host is up (0.00045s latency).
Not shown: 996 filtered ports
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.9p1 Ubuntu
80/tcp   open  http     nginx
443/tcp  open  https    nginx
3000/tcp open  ppp?

# UDP確認結果
PORT      STATE         SERVICE
19132/udp open|filtered unknown

nmapについての補足
LAN内からのnmapスキャンではSSH(22)とGrafana(3000)が open と表示されますが、これは正常です。LAN内からのアクセスはUFWで許可しているためです。外部からのアクセスは別途CMANで確認してください。
また、nmap -sV はTCPポートのみスキャンします。UDPの確認には sudo nmap -sU が必要です(root権限が必要)。

③ UFWのログを確認する

UFWはブロックしたパケットをsyslogに記録します。不審なアクセスがないか定期的に確認しましょう。

# UFWのブロックログを確認(最新20行)
sudo grep "UFW BLOCK" /var/log/syslog | tail -20

# UFWのログファイルを直接確認
sudo tail -f /var/log/ufw.log

まとめ

本記事では、UFWを使って自宅サーバーのファイアウォールを設定し、外部公開に必要なポートだけを開放する方法を解説しました。

  • デフォルトで全ブロックが基本sudo ufw default deny incoming を設定し、必要なポートだけを個別に許可する
  • SSHは先に許可してからUFWを有効化:順番を間違えるとロックアウトされるため、必ずSSHルールを追加してから sudo ufw enable を実行する
  • 送信元IPによる制限が有効from 192.168.68.0/24 でLAN内のみに限定することで、SSHやGrafanaへの外部アクセスをブロックできる
  • NginxリバースプロキシとUFWの組み合わせ:Nginxで443番からGrafana(3000)などに転送しつつ、UFWで直接アクセスをブロックすることで多層防御を実現する
  • CMANとnmapで動作確認:外部と内部の両方からポート開放状況を確認することで、設定が意図通りになっているかを検証できる

これで3記事シリーズが完結しました。

  1. ポート転送設定(HGW&Deco):インターネットからサーバーへのパケットを届ける仕組みを作った
  2. Nginx+Let’s Encrypt(SSL/TLS):HTTPS化してセキュアな通信路を確立し、リバースプロキシでサービスを整理した
  3. UFWファイアウォール(本記事):不要なポートをすべてブロックし、最小限のアクセスだけを許可するセキュリティの最終仕上げを行った

この3つが揃って初めて、外部公開サーバーとして最低限のセキュリティが確保された状態になります。自宅サーバーを公開している方は、ぜひファイアウォールの設定も見直してみてください。

タイトルとURLをコピーしました