令和のLinuxルーター作成手順
TL;DR
だいたい15000円くらいで作れる
Path MTU Discovery問題でハマった
流れ
だいたい以下のことをやります。
- OSのインストール
- PPPoEの接続(フレッツ網への接続)
- DHCP,DNSの構築
- ローカルネットワークの作成(有線、無線をブリッジ接続させる)
- iptablesの構成
- uPNP対応させる
必要な道具
must (約\14000)
- RasberryPi4 (\6600)
- 有名通販サイトではボッタクリ価格の転売が横行しているので、正規代理店( https://raspberry-pi.ksyic.com/ )から買いましょう。
- 他の使っていないPC等でも問題ありませんが、電気代的にRaspiを買ってもお釣りがくるんじゃないかと思います。(未検証)
- RasberryPi4用の電源(\1320)
- raspi4からは5V/3Aじゃないと動かないので、適当な電源だと動かない可能性が高いです。専用電源を買いましょう。
- MicroSDカード(\4000)
- Raspi4のストレージ用です。
- 記事を書くタイミングではやすかったので、シリコンパワーの256GBの物( https://www.amazon.co.jp/gp/product/B07SPVT24H )にしました。
- USBの有線LANアダプタ(\1000)
- 何でもいいんですが、ELECOM推しなので今回はELECOMのにしました。
- https://www.amazon.co.jp/gp/product/B07FD47QGV
- MicroHDMI-HDMIケーブル(\1000)
- ないと画面がうつらないので
- https://www.amazon.co.jp/dp/B014I8TVLI/
may
- raspi4用ケース(\660)
- むき出しでも支障はありませんが、何かしらのケースはあったほうがいいです。
OSのインストール
- Archlinux信者なのでArchlinuxをインストールしました。
- インストール手順( https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-4 )を参考に実施します。
編集時点での手順
sdXのXの部分は環境に応じて適宜読み替えてください。
# SDカードのフォーマット
fdisk /dev/sdx
oを押してパーティションを削除
nを押してパーティションを作成
pを押してその後1を押し、基本パーティションを作成
何も入力せずEnterを押し先頭セクタを指定し、+128MBで先頭から128MBを第一パーティションに
tを押してcを押すことで、第一パーティションをFAT32に変更
pを押してその後2を押し、基本パーティションを作成
何も入力せずEnterを押し先頭セクタを指定し、何も入力せずEnterを押し、残りの領域全てを第二パーティションに
wを押して変更内容を書き込み
# パーティションのフォーマットとマウント
mkfs.vfat /dev/sdx1
mkfs.ext4 /dev/sdx1
mkdir {boot,root}
mount /dev/sdx1 boot
mount /dev/sdx2 root
# OSのインストール
wget http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-4-latest.tar.gz
bsdtar -xpf ArchLinuxARM-rpi-4-latest.tar.gz -C root
sync
# bootのファイルを第一パーティションに移動
mv root/boot/* boot
# SDカードを取り出してOSを起動
umount boot root
# OSの初期化
## ※RHELの「subscription-manager register」みたいなコマンドなので
## 初期化はニュアンスが違う気がしますが、こういう時のinitialize,populateの
## 適切な日本語訳がわかる人はコメントください
pacman-key --init
pacman-key --populate archlinuxarm
デフォルトではless,vimが入っておらず、あまりに不便なためインストールします。
pacman -S vim less
フレッツ網への接続
Linuxからpppの接続を行う際は、pppを使うと簡単に接続させることができます。
pppのインストール
pacman -S ppp
pppの設定
設定ファイルは/etc/ppp/peers/<適当なファイル名>で作成します。設定内容は以下のとおりです。
詳細についてはpppd(8)を見てください。
vim /etc/ppp/peers/hogehoge
plugin rp-pppoe.so
# WAN側のインターフェースを指定します。eth0はほとんどの場合RaspiについているLANの口です。
eth0
# プロバイダに指定された接続IDを記載します。@<ドメイン>も必要です。
name "hogehoge@foo.bar"
# ISPの提示するDNSサーバを利用します
usepeerdns
# 常時接続します。都度切断<demand>させることもできますが、
# 電源切ったほうが早いのでほとんどの場合persistで問題ありません。
persist
# pppの接続が成立時に自動的にルーティングテーブルに追加
defaultroute
#ログにパスワードを出力するのを抑止します。
hide-password
# ISPに自己証明を要求しない。基本的にはauthすべきですが、ISP相手の時はnoauthらしいです
noauth
# IPV6の接続を有効にします
+ipv6
PPP接続時のパスワードは、「/etc/ppp/pap-secrets」または「/etc/ppp/chap-secrets」の物を利用し、ISPに接続方式が指定されている場合は、指定されている方法の側にパスワードを記載します。
両方に書いても問題なく動くので、よくわからなかったら両方に書けば問題ありません。
# /etc/ppp/pap-secrets
<nameで指定した値> * <ISP指定のパスワード>
(例)
hogehoge@foo.bar * password
接続確認のため、手動でPPP接続を開始してみます。
pppd call /etc/ppp/peers/hogehoge
正常に動作していれば、ppp0が作成されます。
ip a
...
ppp0:
OSの起動時にサービスを自動起動するよう設定します。
systemctl enable ppp@hogehoge.service
ローカルネットワークの作成
systemd-networkdで家庭内ネットワークを作成します。
まずは端末に存在するネットワークインターフェースを確認します。
ip a
だいたい以下のような出力がされるはずです。
lo:
eth0:
enp1s0u1:
wlan0:
ppp0:
無線と有線で疎通がとれたほうが何かと便利(Bonjour等)なので、ブリッジアダプタを作成し、有線のネットワークインターフェースと、無線のネットワークインターフェースを、仮想的にL2のネットワークで接続させることにします。
ブリッジアダプタの作成
以下のファイルを作成し、ブリッジアダプタを作成します。
# /etc/systemd/network/br0.netdev
[NetDev]
Name=br0
Kind=bridge
ファイルの作成後、systemd-netwokrdを再起動します。
systemctl restart systemd-networkd
もう一度、ネットワークインターフェースの一覧を表示すると、ネットワークデバイスが作成されています。
令和なのでbrctlとか面倒なことをする必要はありません。
ip a
lo:
...
br0: (←増える)
次は、ローカルネットワーク用のデバイスをブリッジに接続します。eth0はPPP接続に利用しているため、変更は行いません。
# /etc/systemd/network/enp1s0u1.network (新規作成)
[Match]
Name=enp1s0u1
[Network]
Bridge=br0
# /etc/systemd/network/wlan0.network (新規作成)
[Match]
Name=wlan0
[Network]
Bridge=br0
これで、ネットワークインターフェースはブリッジに接続されました。
令和なので簡単です。
次は、ローカルネットワーク上で認識できるようにするため、IPアドレスを静的に指定します。今回は192.168.0.1/24にしましたが、ローカルアドレスとして指定されているアドレス( https://tools.ietf.org/html/rfc1918 )であれば何でも問題ありません。
結論から言うと以下のアドレスです。
- 10.0.0.0 - 10.255.255.255 (10/8 prefix)
- 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
- 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
# /etc/systemd/network/br0.network (新規作成)
[Match]
Name=br0
[Network]
Address=192.168.0.1/24
Gateway=192.168.0.1
設定の完了後、再度systemd-networkdを再起動します。
systemctl restart systemd-networkd
以下のようにbr0が192.168.0.1として起動し、enp1s0u1とwlan0はL2で接続されているためIPアドレスを持ちません。(MACアドレス等は環境により異なるので適宜読み替えてください。)
enp1s0u2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether 04:ab:18:3c:d4:6b brd ff:ff:ff:ff:ff:ff
br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ae:52:e3:c8:67:ca brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global br0
valid_lft forever preferred_lft forever
wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether dc:a6:32:71:0b:9e brd ff:ff:ff:ff:ff:ff
DHCP,DNSの構築
今までの手順でローカルネットワークが作成され、手動で192.168.0.0/24のアドレスを入力すれば正常に動作しますが、いちいちやるのはあまりに面倒です。そのため、DHCP,DNSサーバを構築します。
DHCPサーバの構築
dhcpdをインストールします。
pacman -S dhcpd
dhcpdの設定ファイルは「/etc/dhcpd.conf」です。以下のように設定ファイルを作成します。
option domain-name-servers 192.168.0.1;
option subnet-mask 255.255.255.0;
option routers 192.168.0.1;
subnet 192.168.0.0 netmask 255.255.255.0 {
range 192.168.0.10 192.168.0.200;
}
この設定を行うことで、ローカルネットワークでは自動的に以下の設定が行われるようになります。
設定 | 値 |
---|---|
IPアドレス | 192.168.0.10〜192.168.0.200の空いているIP |
デフォルトゲートウェイ | 192.168.0.1 |
サブネットマスク | 255.255.255.0 |
DNSサーバ | 192.168.0.1 |
作成後は、DHCPサーバを起動し、自動起動も有効にします。
systemctl enable dhcpd4
systemctl start dhcpd4
なお、上のセットは以下の一行でも同じことが出来ます。(蛇足)
systemctl enable --now dhcpd4
DNSサーバの作成
ローカルPCの名前解決は行わず、インターネットのDNS情報を保持するだけのDNSキャッシュサーバを作成します。
まずはbindをインストールします。
pacman -S bind
設定ファイルは「/etc/named.conf」です。DNSキャッシュサーバとして構築する場合の設定は以下のとおりです。
options {
// 諸々のファイルを生成するディレクトリ
directory "/var/named";
// PIDファイルのパス
pid-file "/run/named/named.pid";
// DNS問い合わせを待ち受けるネットワーク
listen-on { 127.0.0.1; 192.168.0.0/24; };
// DNS問い合わせに応答するネットワーク
allow-recursion { 127.0.0.1; 192.168.0.0/24; };
// DNSゾーン転送を許可するネットワーク
allow-transfer { none; };
// DNSゾーン情報の書き換えを許可するネットワーク
allow-update { none; };
// forwardersからのみ名前解決を行う
forward only;
// このDNSサーバが利用する親DNS (8.8.8.8,8.8.4.4はgoogleのDNS)
forwarders{ 8.8.8.8; 8.8.4.4; };
// サーバー情報を隠す
version none;
hostname none;
server-id none;
};
設定の完了後、以下のコマンドでbindを起動し、自動起動も有効にします。
systemctl enable named
systemctl start named
iptablesの構成
ここまで実際に試した方はわかるとおもいますが、Raspiからインターネットには接続できますが、ローカルネットワークからインターネットに接続することは、いま時点ではできません。ローカルネットワークからppp0のネットワークに出られるように許可を行います。
まずは、OSでIP転送が無効になっているため、許可します。
cat /proc/sys/net/ipv4/ip_forward
0(無効)
# /etc/sysctl.d/ipforward.conf (新規作成)
net.ipv4.ip_forward=1
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
設定の作成後、OSを再起動します。
再度確認すると、IP転送が有効になっているはずです。
cat /proc/sys/net/ipv4/ip_forward
1
その後、iptablesをインストールします。
pacman -S iptables
本来は、iptablesコマンドで一つ一つ追加するべきですが、「/etc/iptables/iptables.rule」を以下のように編集します。
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:MINIUPNPD - [0:0]
-A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o ppp0 -j TCPMSS --clamp-mss-to-pmtu
COMMIT
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -i br0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i br0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i br0 -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i br0 -p tcp -m tcp --dport 67 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
-A FORWARD -i br0 -j ACCEPT
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -j REJECT --reject-with icmp-host-unreachable
-A OUTPUT -o ppp0 -d 192.168.0.0/24 -j DROP
-A TCP -p tcp -m tcp --dport 22 -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o ppp0 -j MASQUERADE
COMMIT
個々の設定の意味は以下のとおりです。
# mangleテーブルという、ToSフィールドというIPネットワークのネットワーク品質に関わる設定のテーブルです
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Path MTU Discovery問題の対策を行います。
# Qiitaやstackoverflow等では一度に送るパケットサイズが"""なぜか"""大きく設定されているため、
# 既定値の1500だと、ネットワークの想定よりも大きいパケットサイズとなり、途中でデータが
# 破棄されてしまいます。以下の設定でMSSパケットというパケットサイズをリモート側に
# 伝える機能を有効にし、受け取れないパケットが送られてくることを抑止します。
-A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o ppp0 -j TCPMSS --clamp-mss-to-pmtu
# 接続の許可拒否を行うテーブルです。
*filter
# どのルールにもひっかからない外からのパケットは拒否します
:INPUT DROP [0:0]
# どのルールにもひっかからないサーバを経由するパケットは拒否します
:FORWARD DROP [0:0]
# raspiから別ネットワークにでるパケットは許可します。
:OUTPUT ACCEPT [0:0]
# TCP/UDP用に作成したテーブルです。
:TCP - [0:0]
:UDP - [0:0]
# ローカルネットワーク(br0)からのDNS問い合わせを許可します。
-A INPUT -i br0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i br0 -p tcp -m tcp --dport 53 -j ACCEPT
# ローカルネットワーク(br0)からのDHCP問い合わせを許可します。
-A INPUT -i br0 -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i br0 -p tcp -m tcp --dport 67 -j ACCEPT
# raspi自身の全ての通信を許可します。
-A INPUT -i lo -j ACCEPT
# 何らかの方法で既に確立された接続を許可します。
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# ICMPのechoを許可します。
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
# UDP接続はUDPテーブルの処理に移譲します
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
# TCP接続はTCPテーブルの処理に移譲します
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
# 状態の不明な接続は破棄します。
-A INPUT -m conntrack --ctstate INVALID -j DROP
# UDPテーブルに一致しなかったパケットを、ホストが存在しないエラーで拒否します。
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
# TCPテーブルに一致しなかったパケットを、TCP RESETで拒否します。
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
# その他の接続を、対応するプロトコルがないエラーで拒否します。
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# ローカルネットワークからインターネットに出る通信を許可します。
-A FORWARD -i br0 -j ACCEPT
# ローカルネットワーク側から確立したセッションが、サーバーを通過することを許可します。
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# 上2個のどちらでもない通信を拒否します。
-A FORWARD -j REJECT --reject-with icmp-host-unreachable
# 192.168.0.0/24向けの通信がインターネットに出ていくのを遮断します。
-A OUTPUT -o ppp0 -d 192.168.0.0/24 -j DROP
# SSH接続を許可します。
-A TCP -p tcp -m tcp --dport 22 -j ACCEPT
# ローカルネットワークがインターネットに出る時のテーブルです
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# ローカルネットワークからインターネットに出る通信は、iptablesで動的に割り当てして通信します。
# SNATにした場合、常に一定の値が利用されます。
-A POSTROUTING -o ppp0 -j MASQUERADE
設定の完了後、以下のコマンドでiptablesを起動し、自動起動も有効にします。
systemctl enable iptables
systemctl restart iptables
uPNP対応させる
令和なので、ネットワーク機器もuPNPで動的にポート開放してほしい時がまあまああります。そこで、miniupnpdをインストールし、ローカルネットワークからの要求に応じてルーターで動的にポート開放が行えるようにします。
pacman -S miniupnpd
設定ファイルは「/etc/miniupnpd/miniupnpd.conf」です。
# 外側インターフェースを指定します
ext_ifname=ppp0
# 内側インターフェースを指定します
listening_ip=br0
# ソケットファイル(内部通信用ファイル)の場所を指定します。
minissdpdsocket=/var/run/minissdpd.sock
secure_mode=no
system_uptime=yes
notify_interval=60
clean_ruleset_interval=600
uuid=00000000-0000-0000-0000-000000000000
# upnpを許可するネットワークを指定します
allow 1024-65535 192.168.0.0/24 1024-65535
deny 0-65535 0.0.0.0/0 0-65535
設定の完了後、以下のコマンドでminiupnpdを起動し、自動起動も有効にします。
systemctl enable miniupnpd
systemctl restart miniupnpd