SEIL/x86 Ayame とミニPCでソフトウェアルータ入門
2023年12月13日 水曜日
CONTENTS
【IIJ 2023 TECHアドベントカレンダー 12/13の記事です】
初めまして。梅津です。2021年に新卒入社し、IIJ BootcampでGoの講義を担当したり、SMFsxやMPCといったSEILを管理するためのSMF系サービスの開発、運用を担当しています。
今年はIntel N100という省電力&高性能なCPUが発売され話題になりました。そんな時にネットショップを眺めているとNICが4Portついていてメモリやストレージを拡張できるミニPCを見つけました。面白そうなので2台購入し自宅や実家のGWとして活用してみました。また、複数台を温かみのある手作業でデプロイするのは面倒なのでAnsibleで構築しました。加えて、SEIL/x86 AyameとSA-W2による拠点間VPNの接続、OCIとのサイト間VPNの接続について紹介します。
背景
昨年の記事を書いた際、ありがたいことにSEIL/x86 Ayame スタンダード版半額のキャンペーンで後押ししてもらいました。
その際、せっかくだからと2ライセンス購入しましたがしばらく持て余していました。そのうち、Intel N100を積んだ面白いミニPCを見つけたりSEIL/x86 Ayame スタンダード版のCPU制限が1->2個に緩和されたりしたことや、昨年のネットワーク構成で使用していたSEIL /X1には存在せず、最新のSEIL/x86 Ayame,X4,CA10に搭載された機能が使いたくなるなど試さない理由がなくなりました。そのため、自宅ネットワーク更新の際に置き換えを実施し、ソフトウェアルータに入門してみました。
SEIL/x86 AyameをミニPCに導入する
ここからAyameを導入してソフトウェアルータとして準備していきます。
始めに使用する機材について説明します。
機材スペック
機材はAliexpressで購入しました。ファンレスかつ省電力のため使いやすいです。
サイズはSEIL /X1より小さいため、そのまま置き換えました。
半年ほど常用していますが安定しています。CPU温度は通常使用で60℃、負荷試験(20分)実施時に75℃程度でした。
今回は表の構成で母艦を構築します。
Ayameの対応ハイパーバイザはVMware,KVM,Hyper-Vです。
可能な限りOSSなもので自宅インフラを構築したいので今回のハイパーバイザはKVMを使用します。
Distroは初めて使ったサーバ用DistroがCentOSなのでCentOS9 streamを使用します。
また、SELinuxは有効とします。
CPU | RAM | Storage | NIC | Linux Distro | Hypervisor |
---|---|---|---|---|---|
Intel N100 4C4T | 16GB | SSD SATA 512GB | 2.5G *4 | CentOS9 stream | KVM |
NICの割り振り
物理NICとの通信を設計するにあたり以下の要件を求めました。
- 母艦もWANと管理用ネットワークに対する疎通を持つ
- Ayameの構築失敗時に制御できなくなることを防ぐため
- LAN/DMZ側との接続はLAGに対応する
本来はAyameに直接NICを触らせた方が性能は出ます。しかし、以下の要件を満たすために、母艦Linux側でbridgeやbondingを構成します。
この副次効果として内部ネットワーク構成を柔軟に変更可能となります。
Ansibleを使用したAyameのデプロイ
これまでの要件をもとにAnsibleを使用してAyameをデプロイしていきます。
なお、今後出現するアドレスは基本的に例示用アドレスを使用します。(例外:VPN設定における対向との通信に使用するアドレス)
実際に試す際には実環境のアドレスに読み替えてください。
アドレス | ネットワークの用途 |
---|---|
192.0.2.0/24 | 管理 |
198.51.100.0/24 | 通常使用 |
203.0.113.0/24 | DMZ |
2001:db8:316::1/56 | 任意の内部ネットワーク |
今回使用したAnsible のサンプルはこちらです。
NIC周りの設定変更について、特筆すべき事項はないため飛ばします。
注意すべき点として、初期状態からIPアドレスの付け替えが生じるため、NICの構成が完了するためにはSSH先アドレスを変更しながら2度実施する必要があります。
この問題に関しては今後の課題とします。
Ayameのデプロイと設定
この前に事前準備としてAyameのイメージファイルを取得する必要があります。今回はKVM形式を使用します。IIJマルチプロダクトコントローラサービスをご利用の方で試す場合は提供OVAイメージをKVM形式に変換する。またはKVMで構築している部分をVMwareの手順に読み替えてください。
今回は、SEIL公式サイトで配布しているKVM形式のイメージにプロダクトキーをインストールする方法で説明します。
入手したイメージはfiles/var/lib/libvirt/images/seilx86.qcow2に配置してください。
- name: distribute Ayame image copy: dest: "/var/lib/libvirt/images/seilx86.qcow2" src: "var/lib/libvirt/images/seilx86.qcow2" force: false when: ayame_product_key is defined
VMのデプロイは templates/ayame-vm-template.xml.j2 をもとに行います。
ポイントについて説明します。
まずはリソースの割り当てです。正式な動作環境、条件についてはこちらを参照してください。
SEIL/x86 Ayameスタンダードエディションはversion 3.00より割り当て可能なCPU制限が1 → 2に緩和されました(D00486)。
せっかくなので、2個割り当てます。
メモリについては2GB割り当てます。
これは、要件として1CPUでメモリが1GB、8CPUで3GBというものがあり、余裕をもって2GBとしました。この値は使用する機能によって変化します。
<memory unit='KiB'>2097152</memory> <currentMemory unit='KiB'>2097152</currentMemory> <vcpu placement='static'>2</vcpu> <!-- 特定のCPUに割り振る場合 <vcpu placement='static' cpuset='0-1'>2</vcpu> -->
次にプロダクトキーのインストールと動作モードに関する記述です。
これによってデプロイ後にプロダクトキーを入力する手間が省けます。
また、MPCサービスなどSMFv2に対応したサービスをお使いの場合は、MODE=smfv2とするだけでコンフィグの投入、ファームウェアバージョンの管理、サービス管理画面からの各種管理操作、メトリックの確認を行うことができます。
<sysinfo type='smbios'> <chassis> <entry name='asset'>PRODUCT_KEY={{ ayame_product_key }},MODE=standalone</entry> </chassis> </sysinfo>
次にイーサネットインタフェースの定義です。
実際に使用する構成に合わせて変更してください。
Ayameは最大10個まで認識可能です。本当はオプションを指定してきちんとチューニングすべきでしたが、その前に実通信に組み込んでしまったため適当です。
今後の課題とします。
// 7月中旬に機材がそろい、お盆に帰省した際に設置する都合から十分な検証期間を取れなかったのが敗因でした。
<interface type='bridge'> <source bridge='bridgeEx0'/> <model type='virtio'/> <driver name='vhost' queues='2'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <interface type='bridge'> <source bridge='bridgeMGMT0'/> <model type='virtio'/> <driver name='vhost' queues='2'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </interface> <interface type='bridge'> <source bridge='bridgeIn0'/> <model type='virtio'/> <driver name='vhost' queues='2'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </interface>
あとはAnsibleを実行してデプロイします。
デプロイ後の設定方法
SMFv2に対応したサービスをご利用の方はこの工程は不要です。各サービスより操作可能な状態になっています。
standaloneモードで起動した場合、工場出荷時のコンフィグが読み込まれます。
この場合、ge0に192.168.0.1/24のアドレスがついているため、ETH0からこれにアクセスすればSSHやtelnetで操作が可能です。
今回は楽をするため、cockpitに含まれる仮想マシン管理機能のVNCコンソールより初期設定を実行していきます。
一度ホストへSSHし、cockpitを起動してください。
今回の構成では、cockpitへの接続はeth1からのみ受け付けます。
詳細なスタンドアローンモードでの使用方法はこちらを参照ください。
SEILには以下のようなコンフィグの投入方法があります。
- CLI
- 対話的な設定
- 外部からコンフィグを読み込む
- コンフィグをedit コマンドで編集し適用
- Web UI
- 外部からコンフィグを読み込む
- 画面よりコンフィグを編集し適用
VNCコンソールではコピペが効かず検証には向いていないため、CLIで最小限の設定を行い、Web UIやSSH接続より本格的なコンフィグを投入します。
Web UIの起動まで
以下のコマンドを投入してWeb UIを立ち上げていきます。
ここでのWeb UIの立ち上げ手順は使えることを優先します。詳細はこちらを参照ください。
また、Web UI立ち上げに必要な手順がそろうまではエラーが出てひるみますが、気にせず進めてください。
Web UIの起動まで 以下のコマンドを投入してWeb UIを立ち上げていきます。 ここでのWeb UIの立ち上げ手順は使えることを優先します。詳細はこちらを参照ください。 また、Web UI立ち上げに必要な手順がそろうまではエラーが出てひるみますが、気にせず進めてください。
この状態で https://192.0.2.1へアクセスするとWeb UIを確認できます。
Web UIからの操作
Web UIからはCPU、メモリ、トラフィック流量、ログの確認とファームウェア更新、コンフィグの変更が行えます。
コンフィグ変更は図のように一般的な入力画面のほか、コンフィグをアップロードすることも可能です。
また、この画面ではコンフィグの形式確認ができます。エラー行と理由がわかるため、SEILのコンフィグ体系に慣れやすいです。
設定解説
ネットワーク設計
昨年の記事で紹介した構成から拡張を行いました。
機器が操作不能になったり、ネットワーク障害を起こしても距離があるためすぐに対処することはできません。
そのため、以下のコンセプトで設計を行いました。
- ネットワーク面
- 拠点間はIPsecにて接続する
- 拠点間経路はBGPで経路交換する
- 拠点内経路は可能な限りOSPFにより経路交換を行う
- 運用面
- 遠隔地のネットワークを管理できる
- 機器の設定を誤って締め出されても操作できる手段を提供する
昨年との差分としてはOracle Cloud Infrastructure(OCI)を利用し始めたため、サイト間VPNを追加しました。
エッジルータ解説
実家に配置されているDMZ AyameではONC直下に配置されていることからDHCPv6にて/60のアドレスを受け取ることができます。
一方、自宅常用系Ayame・実家常用系SA-W2にはRAによる/64のアドレスしか受け取れません。このため、両方の場合における設定例を解説します。
IPv4に関する設定は他の記事 でも紹介されているため省略します。
コンフィグやコマンドのうち、変数を示す場合は${変数}、コメントは ##コメント と表記します。
DHCPv6
この場合のアドレス情報の流れは以下の通りです。
- DHCPv6 Client にてアドレス、DNS情報を受け取る
- RAにてアドレスを配布する
- DHCPv6 Serverにて 1で受け取ったDNSアドレスを再配布する
ポイントとしてはge2につけるIPv6アドレスはDHCPv6にて委任されたアドレスである点です。
`router-advertisement.[].advertise.[].prefix : auto` を用いることで、RAで配るアドレスをインタフェースから自動で決めてくれます。 (便利ポイント)
dns-forwarder.service : enable dns-forwarder.0.address : dhcp6 dns-forwarder.1.address : dhcp dns-forwarder.listen.0.interface : ge1 dns-forwarder.listen.1.interface : ge2 interface.loopback0.ipv4.address : ${loopback}/32 interface.ge0.description : outer interface.ge0.ipv4.address : dhcp interface.ge0.ipv6.address : router-advertisement interface.ge1.description : MGMT interface.ge1.ipv4.address : 192.0.2.1/24 interface.ge2.description : inner interface.ge2.ipv4.address : 203.0.113.1/24 interface.ge2.ipv6.address : dhcp6 route.ipv4.0.destination : default route.ipv4.0.gateway : dhcp route.ipv6.0.destination : default route.ipv6.0.gateway : router-advertisement dhcp.server.service : enable dhcp.server.1.interface : ge1 dhcp.server.1.pool.address : 192.0.2.100/24 dhcp.server.1.pool.count : 100 dhcp.server.1.gateway : 192.0.2.1 dhcp.server.1.dns.0.address : 192.0.2.1 dhcp6.client.service : enable dhcp6.client.0.interface : ge0 dhcp6.client.0.prefix-delegation.2.subnet : ge2 dhcp6.client.0.prefix-delegation.2.sla-id : 0x2 dhcp6.server.service : enable dhcp6.server.0.interface : ge2 dhcp6.server.0.dns.0.address : dhcp6 router-advertisement.service : enable router-advertisement.2.interface : ge2 router-advertisement.2.advertise.0.prefix : auto router-advertisement.2.other-flag : enable nat.ipv4.napt.0.interface : ge0 nat.ipv4.napt.0.private : 192.0.2.0-192.0.2.254 nat.ipv4.napt.1.interface : ge0 nat.ipv4.napt.1.private : 203.0.113.0-203.0.113.254
RA+NAT66によるGW
RAで受け取ったアドレスは/64であるため分割できません。
ブリッジで素通しにすることも考えましたが、DMZ側のルーティングを考えると不適です。
今回はRAでULAを配布して、NAT66でケアすることにしました。
方法 | 利点 | 欠点 |
---|---|---|
ブリッジ | 手軽 | ルーティングしたい場合には向かない |
RA(ULA利用) | ルーティング前提の設計ができる | アドレス管理が面倒 |
RA周りはDHCPv6とほぼ変わらないため説明は省略します。
NAT系の設定はデフォルト値として、WANとの境界インターフェースのアドレス、プレフィクスがグローバル側のものとして指定されます。
私の環境では`nat.ipv6.[].ndproxy` を有効にしないと外部との通信ができなかったため有効としています。
dns-forwarder.service : enable dns-forwarder.0.address : dhcp dns-forwarder.1.address : dhcp6 dns-forwarder.listen.0.interface : ge1 dns-forwarder.listen.1.interface : ge2 interface.loopback0.ipv4.address : ${loopback}/32 interface.ge0.description : outer interface.ge0.ipv4.address : dhcp interface.ge0.ipv6.address : router-advertisement interface.ge1.description : MGMT interface.ge1.ipv4.address : 192.0.2.1/24 interface.ge2.description : inner interface.ge2.ipv4.address : 198.51.100.1/24 interface.ge2.ipv6.address : 2001:db8:316:316a::1/64 route.ipv4.0.destination : default route.ipv4.0.gateway : dhcp route.ipv6.0.destination : default route.ipv6.0.gateway : router-advertisement dhcp.server.service : enable dhcp.server.2.interface : ge2 dhcp.server.2.pool.address : 198.51.100.10/24 dhcp.server.2.pool.count : 200 dhcp.server.2.dns.0.address : ${loopback} dhcp.server.2.ntp.0.address : ${loopback} dhcp6.client.service : enable dhcp6.client.0.interface : ge0 router-advertisement.service : enable router-advertisement.2.interface : ge2 router-advertisement.2.advertise.0.prefix : auto router-advertisement.2.dns.0.address : 2001:db8:316:316a::1/64 nat.ipv4.napt.0.interface : ge0 nat.ipv4.napt.0.private : 192.0.2.0-192.0.2.254 nat.ipv4.napt.1.interface : ge0 nat.ipv4.napt.1.private : 198.51.100.0-198.51.100.254 nat.ipv6.0.type: ngn nat.ipv6.0.interface: ge0 nat.ipv6.0.internal : 2001:db8:316:316a::/64 nat.ipv6.0.ndproxy : enable
フロートリンクを使用したIPsec-VPN &BGP
フロートリンクはSEILが使用できるVPN自動設定機能の一つです。
これにより両方のアドレスが動的に変化しうる環境下でもVPNを構成できます。
仮にアドレスが変更された場合でも対向に変更後のアドレスがフロートリンクサーバを経由して通知されるため、アドレスの変更に追従できます。
私の環境ではこの条件に合致するため、フロートリンクを使用します。
注意点としてフロートリンクの利用にはフロートリンクサーバのアドレスが必要です。
ご利用のサービスにて提供されているアドレスをご利用ください。
今回のポイントとして `interface.ipsec1.nat-traversal : force`としている点があります。
本来、直接NGNに接続するため不要な設定です。
しかし、実家に配置されているONUはESPパケットの転送性能が低く、IPsec経由の通信が20MB/Sec程度しか出ません。
この問題を回避するためNATトラバーサルを強制的に有効とし、UDPでカプセル化しています。
通信性能は最後に評価します。
bgp.my-as-number : ${AS番号} bgp.router-id : ${loopback} bgp.ipv4.redistribute-from.ospf.redistribute : enable bgp.ipv4.redistribute-from.connected.redistribute: enable floatlink.auto-pass-filter : enable interface.ipsec1.floatlink.address-family : ipv6 interface.ipsec1.floatlink.my-address : ge0 interface.ipsec1.floatlink.key : ${floatlink-key} interface.ipsec1.floatlink.name-service : ${サービスより提供されたフロートリンクサーバのURL} interface.ipsec1.floatlink.my-node-id : ${自分の識別キー} interface.ipsec1.floatlink.peer-node-id : ${相手の識別キー} interface.ipsec1.preshared-key : ${preshared-key} interface.ipsec1.nat-traversal : force interface.ipsec1.ipv6.forward : pass interface.ipsec1.ipv4.address : ${自分のloopback}/32 interface.ipsec1.ipv4.remote : ${相手のloopback} bgp.neighbor.1.address : ${相手のloopback} bgp.neighbor.1.remote-as : ${相手のAS番号}
OCI サイト間VPNとのIPsec-VPN &BGP
OCIを使用し始めたため自宅ネットワークとサイト間VPNにて接続しました。
OCIのサイト間VPNはIPv4による接続のみ対応であること。私の環境のIPv4通信はNAT配下であることから設定にハマりどころがありました。
OCI側での構成
始めにCPEとして機器を登録します。
CPEとはOCIにおける顧客構内機器(今回はSEIL)です。
ベンダーはOtherとし、各機器のグローバル側から見えるIPv4アドレスを指定します。
最初はこれが分からず、プライベートアドレスを指定してつながらないなぁとかやっていました。
OCIのサイト間接続はCPEに紐づき、変更ができません。このため、グローバルアドレスが変更された場合にはCPEの登録からやり直す必要があります。
私の環境はIPアドレスが変わりうるため、復旧のためには全行程の再設定が必要となります。
続いてサイト間VPNのIPsec接続を作成していきます。
ここのポイントは以下の通りです。
- 「このCPEはNATデバイスの背後にあります」のチェックを入れること
- CPE IKE識別子タイプをIP アドレスとすること
- CPE IKE識別子としてIPsecを行うWANインタフェース(今回はge0)に付与されるアドレスを指定すること
- IKEv2を使用すること
- 経路交換にBGPを用いること
BGP ASNはSEILが所属するAS番号を入力してください。
IPv4 トンネルのアドレスはOCIで/32が使用できないため/30の適当なアドレスを使用します。
これ以外は初期値とします。
SEILの設定
上で設定したIPsec接続と実際に接続します。
特別に設定が必要な項目は各ライフタイムのみです。
この設定はSEIL,SA-W2共通で使用できます。
最後にOCIの設定名称とSEILのコンフィグキーの対応です。
interface.ipsec10.ipv4.source : ${自分の待受アドレス(ge0)} interface.ipsec10.ipv4.destination : ${Oracle VPN IPアドレス} interface.ipsec10.preshared-key : ${共有シークレット} interface.ipsec10.ipv4.address : 10.0.1.2/30 interface.ipsec10.ipv4.remote : 10.0.1.1 interface.ipsec10.nat-traversal : force interface.ipsec10.ike.version : 2 interface.ipsec10.ike.v2.proposal.ike-sa.lifetime : 8h interface.ipsec10.ike.v2.proposal.child-sa.lifetime-of-time : 1h bgp.neighbor.10.address : 10.0.1.1 bgp.neighbor.10.remote-as : ${Oracle BGP ASN}
OCI | SEIL |
---|---|
CPEのIPアドレス | interface.ipsec[].ipv4.source |
Oracle VPN IPアドレス | interface.ipsec[].ipv4.destination |
共有シークレット | interface.ipsec[].preshared-key |
IPv4 トンネル内インターフェース – CPE | interface.ipsec[].ipv4.address |
IPv4 トンネル内インターフェース – Oracle | interface.ipsec[].ipv4.remote |
NAT-T 有効 | interface.ipsec[].nat-traversal |
IKE バージョン | interface.ipsec[].ike.version |
IKE セッション・キー存続時間 | interface.ipsec[].ike.v2.proposal.ike-sa.lifetime |
IPSec セッション・キー存続時間 | interface.ipsec[].ike.v2.proposal.child-sa.lifetime-of-time |
BGP ASN | bgp.my-as-number |
IPv4 トンネル内インターフェース – Oracle | bgp.neighbor.[].address |
Oracle BGP ASN | bgp.neighbor.[].remote-as |
DDNS
自身のドメインを所有していますが、固定IP環境ではないためDDNSサービスを利用しています。この機能はSEILの中でも好きな機能です。
この機能の挙動は一定時間ごとやインタフェースのアドレスが変更された際に指定したURLを叩くものです。
<ipv4>、<ipv6>は指定したインタフェースのアドレスに展開されます。
そのため、グローバルアドレスを自動判別するDDNSサービスだけではなく、明示的に指定が必要なサービスでも利用できます。
ddns.service : enable ddns.0.url : "https://${user}:${pass}@example.com/api/endpoint?ip=<ipv4>&ip6=<ipv6>" ddns.0.trigger.periodic-timer : 5m ddns.0.trigger.interface : ge0
性能試験
Ayameには簡易的な通信測定としてiperf3が使用できます。
目安ですが、これを用いてVPN性能を測定してみました。
## DMZ Ayame側でiperf3の起動 # measure iperf3 server start ## 自宅常用系Ayameで測定 # measure iperf3 client ${DMZ-Ayame} duration 20 bandwidth 1000 direction upload length 8192
iperf3のUDPモードを使用して測定を実施しました。
自宅常用系AyameからDMZ Ayameへの通信をUpload、この逆をDownloadとします。
パケット長を変化させたとき、Uploadであれば長いパケットのほうが性能が出ていました。
一方、Downloadでは1378Byteよりも長い場合にはパケットが落ちる現象が見られました。
この境界となる1378Byte自体はNAT-Tを有効にしたIPsecのオーバーヘッドを考えれば妥当な値です。
速度が出なくなる原因としてフラグメントが発生していると推測していますが、Uploadで速度が出てしまっているのは予想外の結果でした。
また、長いLengthを指定したDownloadでは網のどこかでパケットが落ちてしまい、パケット自体が届いていないような挙動がありました。
詳しい挙動については今後見ていきたいです。
Length (Byte) | Upload |
Download
|
---|---|---|
8192 | 368 | 測定不能 |
4096 | 341 | 10 |
2048 | 271 | 30 |
1378 | 261 | 244 |
1024 | 219 | 224 |
512 | 132 | 128 |
256 | 75.9 | 80.1 |
128 | 40.3 | 48.1 |
終わりに
SEIL/x86 Ayameを自宅ネットワークに組み込んで使ってみました。今回は時間が取れずほとんどチューニングできませんでしたが十分実用できる性能が出て驚きました。
SEIL/X4はこの4倍以上の性能が出ますが、チューニングだけではなく仮想化のオーバーヘッドや暗号エンジンの搭載の有無といった点が影響しているようでした。やはり、ルータとして設計された製品と、汎用機器では違いがあることを実感できました。
この取り組みで仮想環境周りの知識のなさが浮き彫りになったので、今後学習していきたいと考えています。SEIL/x86 Ayameは手軽に使えるソフトウェアルータなので、皆さんもぜひ試してみてください。
Xのフォロー&条件付きツイートで、「IoT米」と「バリーくんシール」のセットを抽選でプレゼント!
応募期間は2023/12/01~2023/12/31まで。詳細はこちらをご覧ください。
今すぐポストするならこちら→ フォローもお忘れなく!