ラズパイで簡単なNATコンバータを使ってiptablesコマンドからIoTファイアウォールの理解を深める


※ 当ページには【広告/PR】を含む場合があります。
2022/12/11
蛸壺の中の工作室|ラズパイで簡単なNATコンバータを使ってiptablesコマンドからIoTファイアウォールの理解を深める



遡ること3年も前になりますが、
「NASAの機密データがRaspberry Piを侵入口として盗まれた」 という当時としてはショッキングな事件がIoTセキュリティー史に刻まれることになり、「あのNASAがハッキングされるなんて、シングルボードコンピュータに代表されるIoT機器も安全とは言えない」という認識が世間一般に知られることになったように記憶しています。
NASAのような国家機密級の技術情報を外部に漏洩させないためのセキュリティー部門は当然最高レベルに堅牢でなくてはいけませんが、組織の内部から見ると、ITセキュリティー技術に疎い研究者やスタッフが何気なく接続してしまったラズパイを外部のハッカーから踏み台にされてしまったのが問題のようです。
近年では、企業や研究機関の試作や製品の一部にもラズパイが採用されるケースも増えてきているので、裏を返すと、開発エンジニアにも必要最低限の「IoTセキュリティー」の理解が必要になっています。
今回は、以前説明した
『ラズパイで構成するNATコンバーター』 を使って、ラズパイIoTセキュリティーの要とも言える 『iptables』 コマンドに慣れてみようという企画です。

合同会社タコスキングダム|蛸壺の技術ブログ
ラズパイでiptablesを使って有線LANを中継するスイッチングハブ的なNATコンバータを作る

自宅のホームネットワークで使えるNATコンバーターをラズパイ4をベースに構築する手順を紹介します。


おさらい〜ラズパイで簡単なNATコンバータを構成する

前の回 では、2つの異なるローカルネットワーク間を跨げるようにしたNATコンバータの詳しい設定手順までを解説していました。
Dockerネットワークなどもそうですが、2つ以上のローカルネットワークを超えるにしてもファイアウォールのルールの設置は結構面倒です。
ただ、ブロードバンドルーターを一つ越えてインターネットに接続する単純な設定ならさほど小難しいことを考える必要はありません。

            1. DHCPサーバー(ここではisc-dhcp-server)をセットアップする
2. ラズパイ側のIPアドレスを固定する(etc/dhcpcd.conf)
3. DNSレゾルバー(ここではdnsmasq)をセットアップする
4. IPv4のパケット転送を有効にする(/etc/sysctl.conf)
5. ファイアウォールにルールを追加する(iptables)

        

という主に5つの手順を抑えることでラズパイを簡単にNATコンバーターにすることができます。

isc-dhcp-server(DHCPサーバー)のセットアップ



まずはラズパイをWiFi無線をインターネット側に接続した状態でのネットワークデバイスの構成から確認してみます。

            $ ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether 11:11:11:22:22:22 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 11:11:11:33:33:33 brd ff:ff:ff:ff:ff:ff
    inet 192.168.x.y/24 brd 192.168.x.z scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
    inet6 2001:1111:1111:1111:2222:2222:2222:2222/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 296sec preferred_lft 296sec
    inet6 fe80:1111:1111:1111:2222:2222/64 scope link 
       valid_lft forever preferred_lft forever

        

ラズパイ3/4には
lo(ローカルホスト=127.0.0.1)wlan0(WiFiネットワークデバイス)eth0(有線LANデバイス) の3つが主に存在していることが分かります。
ネットワークルーティングの状態も確認すると

            $ ip route show
default via 192.168.x.1 dev wlan0 src 192.168.x.y metric 303 
192.168.x.0/24 dev wlan0 proto dhcp scope link src 192.168.x.y metric 303 

        

となっています。

このラズパイの
eth0 側にイーサネットで接続した下位のデバイスがインターネット接続できるように新しいネットワークを作成するためには、まずラズパイがルーター機能を持つ必要があります。
つまりこの構成だと適切に
DHCPサーバー 化が必要になります。
初心者からベテランまで幅広く使えるDebian系OSの定番DHCPサーバーとしては
「isc-dhcp-server」 と、小規模ネットワーク向けのDNSサーバー・リゾルバーの役割として利用できる 「dnsmasq」 をセットでインストールします。
先に
isc-dhcp-server を入れて、DHCPサーバーの起動までを確認します。

            $ sudo apt update
$ sudo apt upgrade -y
$ sudo apt install -y isc-dhcp-server

        

ではisc-dhcp-serverをインストールしたらDHCPサーバーから下位のネットワークに接続したデバイスに配布するローカルアドレスを
/etc/dhcp/dhcpd.conf に設定します。
とりあえず今回はさほど接続するデバイスも多くないので192.168.x.11から192.168.x.20までの範囲でアドレスの割当をしてみます。
なお伏せ字の
x = ... はご自宅のブロードバンドルーターが提供しているローカルネットワークのアドレス値に被らないように適宜読み替えてください。


            #...中略

#👇DNSレゾルバーはdnsmasqにお任せするため、以下はコメントアウト
#option domain-name "example.org";
#option domain-name-servers ns1.example.org, ns2.example.org;

#...中略

#👇デフォルトのDHCPサーバー以外を利用するためコメントアウト
authoritative;

#👇追記
subnet 192.168.x.0 netmask 255.255.255.0 {
    range 192.168.x.11 192.168.x.20;
    option routers 192.168.x.1;
    option domain-name-servers 192.168.x.1;
    option broadcast-address 192.168.x.255;
    ignore declines;
}

        


DHCPサーバーは
eth0 インターフェースに紐付けをします。

            #...

#👇eth0を追加
INTERFACESv4="eth0"
#👇IPv6は今回利用しない
#INTERFACESv6=""

#...

        

では、
isc-dhcp-server が自動で立ち上がるようにしておきます。

            $ sudo systemctl enable isc-dhcp-server
isc-dhcp-server.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable isc-dhcp-server

        

ラズパイを再起動して、
isc-dhcp-server が自動で起動しているかを確認しましょう。

            $ systemctl status isc-dhcp-server
● isc-dhcp-server.service - LSB: DHCP server
   Loaded: loaded (/etc/init.d/isc-dhcp-server; generated)
   Active: failed (Result: exit-code) since Thu 2022-12-08 12:00:48 JST; 35s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 536 ExecStart=/etc/init.d/isc-dhcp-server start (code=exited, status=1/FAILURE)
#...

        

この時点では、おそらくまだ設定が足りていないのでisc-dhcp-serverは起動でコテてしまうはずです。

ルーター(ラズパイ)側のIPアドレスを固定する



そもそもラズパイにルーターのデフォルゲートウェイとなる起点のIPアドレス(
192.168.***.1 )をeth0に拘束していなかったので、ちゃんと /etc/dhcpcd.conf で修正します。

            #...

#👇以下を追加
interface eth0
static ip_address=192.168.x.1/24
static routers=0.0.0.0
static domain_name_servers=0.0.0.0

#...

        

ここで
routersdomain_name_servers0.0.0.0 (ネットワーク外部へのルート)を指定していることで、 wlan0 側のネットワークにパケットの行き先を変えるようにできます。

dnsmasq(DNSレゾルバー)のセットアップ



まだDNSレゾルバが正しく設定されていないので、おそらく下位デバイスを接続しても正しくIPアドレスが付与されない状態だと思います。
ということで、
dnsmasq を入れます。

            $ sudo apt install -y dnsmasq

        

こちらはラズパイを再起動してもすぐに正常に立ち上がります。


            $ systemctl status dnsmasq
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
   Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: 
#...

        

IPv4のパケット転送を有効にする




忘れてはいけないのが、
/etc/sysctl.conf からIPv4のパケット転送を有効にしておく設定です。

            net.ipv4.ip_forward=1

        

これでラズパイを介して、IPv4が異なるネットワーク間で転送可能になります。
ここまで、NATコンバーターの設定はおおよそ完成ですが、
ラズパイの起動時にeth0側にイーサネットで接続されているデバイスがないとisc-dhcp-serverサーバーの起動に失敗 してしまいます。
ここで、一旦ラズパイの電源をOFFにして、適当なPCをeth0にイーサネット接続してから、再度ラズパイの電源をONにしてから次の
「ファイアウォール(iptabels)」 の設定に進みましょう。
ちなみに、有線LANできるマシーンが1台しかない状況ならば、イーサネット接続で繋いだクライアントからSSH接続により、ラズパイ側を設定するほうが便利かも知れません。


iptablesからNATコンバーターのルールを追加する



ラズパイの起動後、問題なければ下のような感じでDHCPサーバーが立ち上がっていると思います。

            $ systemctl status isc-dhcp-server
● isc-dhcp-server.service - LSB: DHCP server
   Loaded: loaded (/etc/init.d/isc-dhcp-server; generated)
   Active: active (running) since Thu 2022-12-08 14:04:28 JST; 8min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 585 ExecStart=/etc/init.d/isc-dhcp-server start (code=exited, status=
    Tasks: 1 (limit: 2178)
   CGroup: /system.slice/isc-dhcp-server.service
           └─644 /usr/sbin/dhcpd -4 -q -cf /etc/dhcp/dhcpd.conf eth0
#...

        

ラズパイ側のネットワークインターフェースの状態も確認すると、

            $ ip address show
#...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    #...
    ☆ inet 192.168.xxx.1/24 brd 192.168.xxx.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    #...

        

という感じで(☆)のアドレスがeth0に固定されていることが分かります。
ルーティング経路も確認してみますと、

            $ ip route show
default via 192.168.zzz.1 dev wlan0 src 192.168.zzz.250 metric 303 
#...
192.168.xxx.0/24 dev eth0 proto dhcp scope link src 192.168.xxx.1 metric 202 

        

というように、
eth0 で紐付けされた 192.168.xxx.0/24 ネットワークが新しく追加されていることも分かります。

ちなみにラズパイに接続したマシーンからもネットワークインターフェースの状態を確認すると、

            $ ip address show
#...
2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    #...
    ☆inet 192.168.xxx.11/24 brd 192.168.xxx.255 scope global dynamic noprefixroute enp0s25
       valid_lft 497sec preferred_lft 497sec
#...

        

☆の箇所にようにラズパイルーターのDHCP機能で割り当てられた
192.168.xxx.11 が付与されていることが分かります。
よし、これでラズパイを介してインターネット使える!...かと思いきや、ファイアウォールを何も設定していないため、ラズパイとしては通信をどう扱っていいものかルールがないので何もしてはくれません。

            $ sudo iptables -vnL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination     

        

つまり、まだインターネット側には繋がっていない状態です。
ちなみに、eth0越し直接接続されている下位のクライアントデバイスからの
ping は通過します。

            $ ping -c 4 192.168.xxx.1
PING 192.168.xxx.1 (192.168.xxx.1) 56(84) bytes of data.
64 bytes from 192.168.xxx.1: icmp_seq=1 ttl=64 time=0.569 ms
64 bytes from 192.168.xxx.1: icmp_seq=2 ttl=64 time=0.414 ms
64 bytes from 192.168.xxx.1: icmp_seq=3 ttl=64 time=0.343 ms
64 bytes from 192.168.xxx.1: icmp_seq=4 ttl=64 time=0.328 ms

--- 192.168.xxx.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3061ms
rtt min/avg/max/mdev = 0.328/0.413/0.569/0.095 ms

        

pingだけでなく、SSHやFTPなども同一ネットワークではファイアウォールの影響は受けません。
基本的にファイアウォールは異なるネットワーク間でのパケットの通信経路を制御するための仕組みですので、同一ネットワーク間での通信は制限されることは無いようです。
ではインターネットが繋がらない状態から、iptablesでファイアウォールに以下のルールを一つ追加しましょう。

            $ sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

        


POSTROUTINGのチェーンにルールがちゃんと追加されているか確認するには以下のコマンドです。

            $ sudo iptables -t nat -vnL POSTROUTING
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   24  2675 MASQUERADE  all  --  *      wlan0   0.0.0.0/0            0.0.0.0/0 

        

では、クライアント側のコンソールで、外部のネットワークにアクセスできるか確かめてみましょう。

            $ ping -c 4 www.google.com
PING www.google.com (xxx.xxx.xxx.xxx) 56(84) bytes of data.
64 bytes from n********.*****0.net (xxx.xxx.xxx.xxx): icmp_seq=1 ttl=110 time=50.8 ms
64 bytes from n********.*****0.net (xxx.xxx.xxx.xxx): icmp_seq=2 ttl=110 time=62.3 ms
64 bytes from n********.*****0.net (xxx.xxx.xxx.xxx): icmp_seq=3 ttl=110 time=60.1 ms
64 bytes from n********.*****0.net (xxx.xxx.xxx.xxx): icmp_seq=4 ttl=110 time=59.0 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 50.821/58.041/62.262/4.333 ms

        

今度はちゃんとインターネットに接続されています。
ただしこのルールだけでは、
他のローカルネットワークからはアクセスできない一方方向の設定 になるので注意も必要です。
なお、iptablesのルールはラズパイの電源を落とすと消えてします。 永続化したい場合には、
rc.local にルールをハードコピーして対策しましょう。

参考|rc.localにiptablesコマンドを書き込む

ちょっと脇道〜ラズパイ・ルーターでIPv6は取り扱えるの?



今回のお話は
IPv4のみに対応した ルーター機能しか設定していません。
なのでラズパイに構築したローカルネットワークをIPv6にも対応させたければ、
「IPv6対応のDHCPサーバー」 を適切に構築する必要があります。
DHCPサーバーへのIPv6対応はIPv4ほど単純ではありません。
最近のブロードバンドルーターはほとんどがIPv6にも対応しているものがほとんどかと思いますので、そこにラズパイを接続しているならば、ラズパイ自体には
IPv6 のローカルアドレスが割り当てられていると思います。
なのでラズパイ自体は、問題なくIPv6が利用出来ているはずです。

            $ ping6 -c 4 www.google.com
PING www.google.com(n*******.*******0.net (2404:xxxx:xxxx:xxxx:xxxx:xxxx)) 56 data bytes
64 bytes from n*******.*******0.net (2404:xxxx:xxxx:xxxx:xxxx:xxxx): icmp_seq=1 ttl=113 time=48.3 ms
64 bytes from n*******.*******0.net (2404:xxxx:xxxx:xxxx:xxxx:xxxx): icmp_seq=2 ttl=113 time=57.7 ms
64 bytes from n*******.*******0.net (2404:xxxx:xxxx:xxxx:xxxx:xxxx): icmp_seq=3 ttl=113 time=55.8 ms
64 bytes from n*******.*******0.net (2404:xxxx:xxxx:xxxx:xxxx:xxxx): icmp_seq=4 ttl=113 time=53.8 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 9ms
rtt min/avg/max/mdev = 48.274/53.878/57.669/3.517 ms

        

しかし、ラズパイの下位側に接続したデバイスからping6してみると、

            $ ping6 -c 4 www.google.com
ping6: connect: ネットワークに届きません

        

ということが確認できると思います。
それもそのはずで、

            $ ip address show
...
2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    #👇無効なipv6
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link noprefixroute

        

つまり、下位デバイスのインターフェイスをIPv6対応させるには、ルーター側のDHCPサーバーからIPv6を接続したデバイスに割り当てられるようにする必要があります。
IPv6はIPv4ほど単純なものではなく、DHCPサーバーを設定するにはIPv6の通信仕様自体にもよく理解する必要があります。
面倒なことをする気力が起きない方は、ラズパイ自体も高価ですので、それなら「
」のようなハードウェア中継機(ネットワークコンバータ)製品を購入したほうが何かとお得と思います。
以降、余裕があればIPv6のDHCPサーバーの立ち上げ方法もこのブログで解説してみたいと思います。


ラズパイNATコンバータでiptablesコマンドを利用したファイアウォールを理解する



ここからはラズパイの単なるNATコンバーター化の内容をもっと発展させて、外部のネットワークからラズパイ側のネットワークに送られてくる通信に「もう人手間加えた」制限をかけることを念頭に、IoT機器のファイアウォールの設計例などをいくつか試してみましょう。

iptables-persistentでファイアウォールの設定を管理する



いきなり脇道に逸れますが、もっとiptablesのルールの管理をしやすくするために、
「iptables-persistent」 を別途導入します。
iptablesコマンドから打ち込んだ設定は次のサーバー起動時には消えてしまうため、ファイアウォールの設定を永続化したいなら、
rc.local に直接iptablesコマンドを初回実行させるのが、楽なやり方ではあります。
ただこれだと後々になってファイアウォールのルールが複雑化するほど、自分でrc.localのファイルの中身を細かく管理するのが大変です。
そこでiptablesに熟れてきたら、
「iptables-persistent」 をインストールしましょう。

            $ sudo apt install iptables-persistent -y

        

インストールの際に、IPv4とIPv6の設定を保存するかを個別に聞いてくるので、利用状況に併せて設定を選択します。

合同会社タコスキングダム|蛸壺の技術ブログ
合同会社タコスキングダム|蛸壺の技術ブログ



iptables-persistentは自動で現在のルールを保存してくれないので、iptablesコマンドを使って構築したルールに変更を加えた場合には、忘れずに以下のコマンドを実行しましょう。

            $ sudo netfilter-persistent save
#👇IPv4用Netfilterルール
run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
#👇IPv6用Netfilterルール
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save

        

なお、元々は
iptables-persistentnetfilter-persistent とは別々のパッケージだったようですが、現在は iptables-persistent の方に統合されています。
既存のルールを一旦すべてクリアしたい場合には、

            $ sudo netfilter-persistent flush

        

を利用します。
もしも誤って
netfilter-persistent flush をしてしまった場合や、ルールを追加したあとで挙動がおかしく繋がらなくなったときには、焦らずに、

            $ sudo netfilter-persistent reload

        

としてあげると、前回の
netfilter-persistent save した設定を復元できることも覚えておきましょう。
DebianOS系では、iptables-persistentをインストールするとsystemdに登録されて、OS起動時に自動で
netfilter-persistent start が実行され、ルールが自動でリストアされるようになっています。
起動時のサービスデーモンに頼らず、手動でnetfilterルールをリストアする場合には、

            $ sudo netfilter-persistent start

        

とすると、netfilter-persistentサービスが起動します。
またサービスの停止したい時には、

            $ sudo netfilter-persistent stop

        

を使います。

異なるネットワークが見えるように静的ルーティングを設定する



自分で独自に作成したネットワークは、そのままだと他のネットワークからは認識されません。 そこで、異なるネットワーク間を互いに見えるようにするために
「静的ルーティング」 を設定しましょう。
例えば
192.168.1.0 ネットワークに属するクライアント・ 192.168.1.123 から、直接 192.168.2.0 ネットワークをpingで叩けるように 静的ルーティング を手動で追加します。
ラズパイルーター・NATコンバーターのwlan0インターフェースが
192.168.1.124 である場合なら、 192.168.2.0/24 宛のルーティングをクライアント側( 192.168.1.123 )に設定します。

            $ sudo ip route add 192.168.2.0/24 via 192.168.1.124

        

静的ルーティングがきちんと追加されたか確認するには、
ip route shownetstat -nr を使います。

            $ netstat -nr
カーネルIP経路テーブル
受信先サイト    ゲートウェイ    ネットマスク   フラグ   MSS Window  irtt インタフェース
0.0.0.0         192.168.1.1   0.0.0.0         UG        0 0          0 wlan0
192.168.1.0   0.0.0.0         255.255.255.0   U         0 0          0 wlan0
☆192.168.2.0   192.168.1.124 255.255.255.0   UG        0 0          0 wlan0

        

☆のルーティングが追加されたので、クライアント・
192.168.1.123 から、 192.168.2.0 ネットワークへ直接パケットが飛ぶようになります。

            $ ping -c 4 192.168.2.1
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=5.04 ms
64 bytes from 192.168.2.1: icmp_seq=2 ttl=64 time=3.13 ms
64 bytes from 192.168.2.1: icmp_seq=3 ttl=64 time=3.45 ms
64 bytes from 192.168.2.1: icmp_seq=4 ttl=64 time=2.97 ms

--- 192.168.2.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 7ms
rtt min/avg/max/mdev = 2.966/3.647/5.040/0.824 ms

        

ただし、
ip-route コマンドは揮発的でOSを再起動すると消えてしまうため、設定を常時残しておくためには /etc/network/if-up.d フォルダ内に、 static-routes ファイルを新規作成し、以下の内容で編集しておきます。

            #!/bin/sh

ip route add 192.168.2.0/24 via 192.168.1.124

        

として実行権限を与えます。


            $ sudo chmod +x /etc/network/if-up.d/static-routes

        

これでOS起動時に自動実行され、静的ルーティングが永続化できます。


拡張ターゲット・「NETMAP」でアクセス可能なIPアドレス値で制限する



アクセス権限のあるユーザーのIPアドレスが静的に判別できる場合、個別にそのIPを許可するような「ホワイトリスト」をファイアウォールとして明示に指定するのが、シンプルかつセキュアな方法です。
個別のIPアドレスをピンポイントでフィルタするなら、一見
SNAT ターゲットが利用できそうです。

SNAT ターゲットは以前の記事の SNATの設定作法 で、比較的丁寧に取り上げましたでそちらを参考ください。
例えば、外部のローカルネットワーク(
192.168.1.0 )に属する送信元のクライアントのアドレスが 192.168.1.123 であったとします。
この
192.168.1.123 から送られてきたパケットを受けとったラズパイルーター(のeth0)が、そのパケットを自身の内部のローカルネットワーク( 192.168.2.0 )へ転送するときに、その送信元のアドレスを --to-source オプションから内部のローカルネットワーク側の 192.168.2.123 に書き換えるルールを作成しましょう。

            $ sudo iptables -t nat -A POSTROUTING \
    -o eth0 \
    -s 192.168.1.123 \
    -j SNAT --to-source 192.168.2.123

        

これで
192.168.1.0 ネットワークから 192.168.2.0 ネットワークへ通信を一方方向で行うことが可能になります。
一見これはUDPやICMP通信(1Way)だと上手くいくのですが、WebサーバーなどのTCP(つまり3Wayハンドシェイク)では通信の確立シグナルを返せないので接続がおかしいことになります。
この場合、TCP通信でも対応させたいなら、PREROUTINGかOUTPUTチェーンに
DNAT を使ってパケット送信元のアドレス変換を行うほうが良いでしょう。

            $ sudo iptables -t nat -A PREROUTING \
   -s 192.168.1.123 \
   -j DNAT --to-destination 192.168.2.123

        

ただし、パケットの送り先となるクライアントの数が多くなると、DNATではパケット発信元IPアドレスを指定して一つ一つ変換後のIPアドレスを指定しないといけなくなるのがとても苦痛です。
個別のクライアントではなく、もう少し範囲を広げて特定のネットワークを全て許可したいときに使えるのが、
「NETMAP」 ターゲットです。

NETMAP は異なるネットワーク間でIP値の一番したのビット列の表す番号1~254(/24マスクの場合)に対して、1:1対応(静的マッピング)とみなしてアドレスを変換してくれます。
つまり
NETMAP ターゲットを利用すると、例えば先程の例でいくと、 192.168.1.0 ネットワークに存在する 192.168.1.123 と、別のネットワーク 192.168.2.0 に存在する 192.168.2.123 を自動で変換してくれる便利なターゲットです。

            $ sudo iptables -t nat -A PREROUTING \
   -s 192.168.1.0/24 \
   -j NETMAP --to 192.168.2.0/24

        

これだけで、192.168.2.0ネットワークが根こそぎ172.26.16.0ネットワークへと一対一変換されるようになります。

拡張マッチングモジュール・macでアクセス可能なMACアドレス値を制限する



主題のようにラズパイに代表されるIoT機器はハードウェアですので、対象となるネットワークインターフェース1台につき最低1つの
「MACアドレス」 を有しています。
先ほどのように、固有のIPアドレスを持たない場合、MACアドレスを使ったファイアウォールというのも、場合によっては必要になってくるかもしれません。

iptables コマンドには、拡張モジュールである 「iptables-extensions」 という、様々な通信条件をキャプチャするための特殊なマッチング・モジュールが存在しています。

iptables-extensions モジュールは見慣れたものから滅多に使わないものまで、かなりの数が用意されているので、 man を呼び出して一度覗かれると良いと思います。

            $ man iptables-extensions

        

日本語化されたマニュアルを参照したい場合には、例えば
こちらのサイト などが邦訳されています。
この拡張マッチングモジュールは、適用先の対象チェーンの種類は個別に異なり、例えばルールを追加したい場合、

            $ iptables -A [適用チェーン] \
   -m [適用モジュール] [モジュールの固有オプション] [オプションのターゲット]

        

という感じで使います。

mac モジュールは、指定した送信元のMACアドレスでの条件で INPUT チェーンに対してフィルタリングすることができます。
各ネットワークインターフェースごとにMACアドレスが存在し、
link/ether の値に表示されます。

            $ ip addr | grep ether
    #...
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff
    #...

        

ここでは、送信元MACアドレスが00:11:22:33:44:55のパケットのみを通過させる場合、

            $ sudo iptables -A INPUT \
    -m mac ! --mac-source 00:11:22:33:44:55 \
    -j DROP

$ sudo iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 291 packets, 34123 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1        5   300 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            MAC ! 00:11:22:33:44:55

        

すると、MACアドレス・
00:11:22:33:44:55 のマシーン以外の通信を問答無用でパケットが棄却されていることが分かります。


まとめ



以上、ホームネットワークで使えるラズパイをルーター・NATコンバーターにして使う際のiptablesを利用したファイアウォールに関して解説してきました。
iptablesのターゲットやモジュールはかなり種類があり、アイディア次第でここでは紹介しきれないくらい複雑なルールでNAT間をフィルタリングする条件が存在すると思います。
お使いのネットワーク環境の状態によっても、最適なファイアウォールのルールが違ってきますので、ここではあくまでも参考とお考えください。