LoginSignup
26
30

More than 3 years have passed since last update.

令和のLinuxルーター作成手順〜raspi4を添えて〜

Last updated at Posted at 2020-01-14

令和のLinuxルーター作成手順

TL;DR

だいたい15000円くらいで作れる
Path MTU Discovery問題でハマった

流れ

だいたい以下のことをやります。

  1. OSのインストール
  2. PPPoEの接続(フレッツ網への接続)
  3. DHCP,DNSの構築
  4. ローカルネットワークの作成(有線、無線をブリッジ接続させる)
  5. iptablesの構成
  6. uPNP対応させる

必要な道具

must (約\14000)

  • RasberryPi4 (\6600)
    • 有名通販サイトではボッタクリ価格の転売が横行しているので、正規代理店( https://raspberry-pi.ksyic.com/ )から買いましょう。  
    • 他の使っていないPC等でも問題ありませんが、電気代的にRaspiを買ってもお釣りがくるんじゃないかと思います。(未検証)
  • RasberryPi4用の電源(\1320)
    • raspi4からは5V/3Aじゃないと動かないので、適当な電源だと動かない可能性が高いです。専用電源を買いましょう。
  • MicroSDカード(\4000)
  • USBの有線LANアダプタ(\1000)
  • MicroHDMI-HDMIケーブル(\1000)

may

  • raspi4用ケース(\660)
    • むき出しでも支障はありませんが、何かしらのケースはあったほうがいいです。

OSのインストール

編集時点での手順

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
26
30
2

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
26
30