2
2

Raspberry Pi as a Router

Last updated at Posted at 2023-12-08

この記事は、木更津高専 Advent Calender 2023の9日目の記事です。

前→距離減衰式で長周期地震動階級を推定してみた by @maikuradentetu
次→RustでDNAT設定いじるサイトを作った by @toma09to

動機

私は、Raspberry Piを使って自宅サーバーを公開していましたが、自宅の他のネットワーク機器と切り離されていなかったため、万が一Raspberry Piが乗っ取られた時に、他の機器にも影響しかねない危険な状態でした。
これを解消するために、Raspberry Piを使ってDMZ機能を持つルーターを構築することにしました。
あれ?Webサーバーに使うRaspberry Piは何処に?

使用機器

  • Raspberry Pi 4 Model B 4GB
    • OS: Ubuntu Server 22.04.3 LTS
  • UGREEN 20256 × 2
  • 光BBユニット

NICの購入ページ(Amazon)はこちら

ネットワーク構成

今回は、下図のようなネットワークを作成します。元のルーターは残す方針で構築しました。

各ネットワークの概要は以下の通りです。

ネットワーク名 ネットワークアドレス 概要
外部ネットワーク 192.168.0.0/24 親ルーターとラズパイ
DMZ 192.168.1.0/24 公開サーバー等の置き場
内部ネットワーク 192.168.3.0/24 自宅のネットワーク機器の所属先

親ルーターの設定

ネットワークアドレスを192.168.0.0/24に変更し、ルーターのIPアドレスを192.168.0.1とします。

Screenshot 2023-11-03 144450.png

また、ルーターの簡易DMZ機能等の、グローバルIPアドレスへの接続要求を転送する機能を有効にして、転送先アドレスを192.168.0.2にします。

Screenshot 2023-11-03 144515.png

Raspberry Piの設定

IPアドレスの固定

NICをラズパイに挿すと、enx************のようなインターフェースが追加されます。
私の環境では、enx207bd26c92bbを外部ネットワーク、enx207bd27657d3を内部ネットワーク、eth0をDMZに割り当てました。以下、各自の環境に合わせて読み替えてください。

今回はnetplanを用いてIPアドレスの設定をします。/etc/netplan/99_config.yamlを作成して、以下のように編集します。

/etc/netplan/99_config.yaml
network:
  ethernets:
    # external network
    enx207bd26c92bb:
      dhcp4: false
      dhcp6: false
      addresses:
        - 192.168.0.2/24
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
      routes:
        - to: default
          via: 192.168.0.1
    # DMZ network
    eth0:
      dhcp4: false
      dhcp6: false
      addresses:
        - 192.168.1.1/24
    # internal network
    enx207bd27657d3:
      dhcp4: false
      dhcp6: false
      addresses:
        - 192.168.3.1/24
  version: 2

ファイルを作成出来たら、それを適用します。

$ sudo netplan apply

IPフォワードの有効化

このままでは、NICの間でパケットが転送されないので、IPフォワードを有効にします。
/etc/sysctl.confにIPフォワードの設定があるので、その部分をアンコメントします。

/etc/sysctl.conf
# Uncomment the next line to enable packet forwarding for IPv4
- #net.ipv4.ip_forward=1
+ net.ipv4.ip_forward=1

これで、IPフォワードの設定が出来たので、それを適用するために再起動します。

$ sudo reboot

iptablesの設定

iptablesを使って、ファイアウォール等の設定をしていきます。
/etc/iptables.shを作成して、そこにルールを記述していきます。

変数の定義とテーブルの初期化

まず、変数と初期化を記述します。GLOBAL_IPにはあなたのグローバルIPアドレスを入れて、その他の値についても適宜読み替えてください。

/etc/iptables.sh
#!/bin/bash

# interface names
EXT_INT=enx207bd26c92bb
DMZ_INT=eth0
INT_INT=enx207bd27657d3

# network addresses
EXT_NET=192.168.0.0/24
DMZ_NET=192.168.1.0/24
INT_NET=192.168.3.0/24

# my IP address
PRIVATE_IP=192.168.0.2
GLOBAL_IP=0.0.0.0

# initialize iptables
iptables -F
iptables -t nat -F
iptables -X

ポリシー

ポリシーはどのルールにもマッチしないパケットの処理を決めます。
INPUTとFORWARDはDROP(破棄)、OUTPUTはACCEPT(許可)とするのが安全です。

/etc/iptables.sh
# policy
iptables -P INPUT   DROP
iptables -P FORWARD DROP
iptables -P OUTPUT  ACCEPT

各種通信の許可

許可された通信のパケットは許可します。

/etc/iptables.sh
# established packets
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

ループバック(自分自身)からのパケットは許可します。

/etc/iptables.sh
# loopback
iptables -A INPUT -i lo -j ACCEPT

今回の構成では、インターネットからpingが飛んでくることは(多分)ないので、利便性のためにpingは許可します。

/etc/iptables.sh
# ping
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT

内部ネットワークからのSSHを許可します。

/etc/iptables.sh
# SSH
iptables -A INPUT -s ${INT_NET} -i ${INT_INT} -p tcp --dport 22 -j ACCEPT

フォワード

内部→外部、内部→DMZ、DMZ→外部は無条件でパケットを通します。また、後述するDNATの設定によって許可されたポートへの通信を通すために、外部→DMZも許可します。
さらに、確立された通信のパケットも通します。

/etc/iptables.sh
# forward
iptables -A FORWARD -s ${INT_NET} -i ${INT_INT} -o ${EXT_INT} -j ACCEPT
iptables -A FORWARD -s ${INT_NET} -i ${INT_INT} -o ${DMZ_INT} -j ACCEPT
iptables -A FORWARD -s ${DMZ_NET} -i ${DMZ_INT} -o ${EXT_INT} -j ACCEPT
iptables -A FORWARD               -i ${EXT_INT} -o ${DMZ_INT} -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

SNAT

インターネットへの通信が親ルーターに届くようにSNATを設定します。

/etc/iptables.sh
# SNAT
iptables -t nat -A POSTROUTING -o ${EXT_INT} -j MASQUERADE

DNAT

外部からの特定ポートへの通信がDMZへ届くようにDNATを設定します。これは、宛先アドレスをルーターから他のアドレスに書き換える機能で、今回はDMZ内の機器のアドレスを指定します。
私の環境では、192.168.1.10にWebサーバーがあるので、ここにHTTPとHTTPSを通します。
また、通常はローカルからサーバーへグローバルIPを使ってアクセスすることが出来ないので、ここで設定します。
ポート番号と送信先アドレスを変更すれば他の通信も許可できます。

/etc/iptables.sh
# DNAT
iptables -t nat -A PREROUTING               -p tcp -d ${PRIVATE_IP} --dport 80  -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING               -p tcp -d ${PRIVATE_IP} --dport 443 -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING -i ${INT_INT} -p tcp -d ${GLOBAL_IP}  --dport 80  -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING -i ${INT_INT} -p tcp -d ${GLOBAL_IP}  --dport 443 -j DNAT --to-destination 192.168.1.10

ログ

破棄されたパケットを記録します。

/etc/iptables.sh
# logging
iptables -N LOGGING
iptables -A LOGGING -m limit --limit 1/s -j LOG --log-level warning --log-prefix "DROP: "
iptables -A LOGGING -j DROP
iptables -A INPUT   -j LOGGING
iptables -A FORWARD -j LOGGING

設定の適用

完成したファイルは以下のようになります。

/etc/iptables.sh
#!/bin/bash

# interface names
EXT_INT=enx207bd26c92bb
DMZ_INT=eth0
INT_INT=enx207bd27657d3

# network addresses
EXT_NET=192.168.0.0/24
DMZ_NET=192.168.1.0/24
INT_NET=192.168.3.0/24

# my IP address
GLOBAL_IP=0.0.0.0

# initialize iptables
iptables -F
iptables -t nat -F
iptables -X

# policy
iptables -P INPUT   DROP
iptables -P FORWARD DROP
iptables -P OUTPUT  ACCEPT

# established packets
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# ping
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT

# SSH
iptables -A INPUT -s ${INT_NET} -i ${INT_INT} -p tcp --dport 22 -j ACCEPT

# forward
iptables -A FORWARD -s ${INT_NET} -i ${INT_INT} -o ${EXT_INT} -j ACCEPT
iptables -A FORWARD -s ${INT_NET} -i ${INT_INT} -o ${DMZ_INT} -j ACCEPT
iptables -A FORWARD -s ${DMZ_NET} -i ${DMZ_INT} -o ${EXT_INT} -j ACCEPT
iptables -A FORWARD               -i ${EXT_INT} -o ${DMZ_INT} -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# SNAT
iptables -t nat -A POSTROUTING -o ${EXT_INT} -j MASQUERADE

# DNAT
iptables -t nat -A PREROUTING               -p tcp -d ${PRIVATE_IP} --dport 80  -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING               -p tcp -d ${PRIVATE_IP} --dport 443 -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING -i ${INT_INT} -p tcp -d ${GLOBAL_IP}  --dport 80  -j DNAT --to-destination 192.168.1.10
iptables -t nat -A PREROUTING -i ${INT_INT} -p tcp -d ${GLOBAL_IP}  --dport 443 -j DNAT --to-destination 192.168.1.10

# logging
iptables -N LOGGING
iptables -A LOGGING -m limit --limit 1/s -j LOG --log-level warning --log-prefix "DROP: "
iptables -A LOGGING -j DROP
iptables -A INPUT   -j LOGGING
iptables -A FORWARD -j LOGGING

このスクリプトを適用するために、実行権限を与えて実行します。

$ sudo chmod +x /etc/iptables.sh
$ sudo /etc/iptables.sh

iptablesのルールの自動適用

iptablesのルールはシャットダウンすると消えてしまいます。そこで、systemdで起動時に先ほど作成したスクリプトを実行させるデーモンを作成します。
/etc/systemd/system内にauto-iptables.serviceというファイルを作成します。

/etc/systemd/system/auto-iptables.service
[Unit]
Description=Automatic iptables setup service
After=network.target

[Service]
User=root
ExecStart=/etc/iptables.sh
Type=oneshot

[Install]
WantedBy=multi-user.target

そして、以下のコマンドを実行して、システム起動時にデーモンが起動するようにします。

$ sudo systemctl daemon-reload
$ sudo systemctl enable auto-iptables.service

DHCPの設定

ルーターとしての設定はここまでで終わりなのですが、家族に

内部ネットワークにはDHCPでIPアドレスが自動で降ってくるようにしてほしい

と言われたので、DHCPサーバーの設定もしていきます。

まず、isc-dhcp-serverをインストールします。

$ sudo apt install isc-dhcp-server

次に、設定ファイルである/etc/dhcp/dhcpd.confを編集します。
以下の部分をそれぞれコメント、アンコメントします。

/etc/dhcp/dhcpd.conf
# option definitions common to all supported networks...
- option domain-name "example.org";
- option domain-name-servers ns1.example.org, ns2.example.org;
+ #option domain-name "example.org";
+ #option domain-name-servers ns1.example.org, ns2.example.org;
/etc/dhcp/dhcpd.conf
# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
- #authoritative;
+ authoritative;

そして、ファイルの末尾に以下のように追記します。

/etc/dhcp/dhcpd.conf
subnet 192.168.3.0 netmask 255.255.255.0 {
  range 192.168.3.2 192.168.3.254;
  option routers 192.168.3.1;
  option domain-name-servers 8.8.8.8;
  option broadcast-address 192.168.3.255;
  ignore declines;
}

最後に、サービスを起動し、有効化します。

$ sudo systemctl start isc-dhcp-server.service
$ sudo systemctl enable isc-dhcp-server.service

/var/lib/dhcp/dhcpd.leasesに、取得されたIPアドレス等の情報が記録されます。

あとがき

今回は内容に含みませんでしたが、

  • DMZへのアクセスのDoS対策(hashlimit)
  • IPv6対応

などのことをこれから実現させていきたいと思っています。
それでは、また明日お会いしましょう!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2