- 投稿日:2019-02-11T23:45:37+09:00
VXLAN/EVPN on Linux の検証
モチベーション
データセンタ内のVXLAN/EVPN環境において、VXLAN終端をleafスイッチでなく配下の物理サーバ(Linux)でやりたい.
メリット
- サーバとスイッチ間の接続がL2からL3になる
- bondingやMLAGが不要
- leafスイッチの設定が簡素化される
- スイッチはMP-BGPの通信に徹する
- ネットワークデザインがシンプル化
- leafスイッチのメンテナンスが楽になる
- BGP graceful shutdown community (使ったことないけど)
- スイッチのアップグレードを人知れず実施したい
デメリット
- ネットワーク性能の低下?(本記事では未検証)
- leafスイッチのASICで高速処理されていたVXLANのカプセル処理が物理サーバに委譲されるため
- サーバ用のVXLANオフロードNICがあったような...
前回の記事で Cumulus-VX を使用して VXLAN/EVPNのルーティングを検証し、CumulusはDebianとFRRを使っていることがわかった。ということはノーマルなlinuxでも、設定を適切に投入すればVXLAN/EVPNを動作させられるはずである. 結論として動作を確認できた.
背景
データセンタネットワークの歴史的な経緯はこちらの資料を参照.
(JANOG41) データセンターでのルーティングプロトコルIP-fabric デザインにするとスケーラビリティを得る代わりにToR内でVLANが閉じるため、L2-fabricと異なり他のラックに同じVLANをもつマシンを分散配置できない. ラック内のネットワーク設計によるがネットワーク設定と物理ラックが密結合になるため、管理が必要だったりリソースを有効活用しにくくなる.
これを解決するためにオーバレイネットワークやルーティングプロトコルを駆使してラックを越境するわけだがVXLAN以外にも様々なアプローチがとられている.
- (LINE) 大規模サービスを支えるネットワークインフラの全貌
- (Cybozu) Neco のネットワーク - 実装編
- (Yahoo) データセンタネットワークの取り組みとFacebook Backpack
サーバエンドでVXLANを終端させるアプローチ自体は既に考えられている.
Network 概要
今回構築するネットワークはこちら. VMが2台あれば容易に検証できるようにした.
Linux Networkの詳細な設定は実際の Cumulus-VX の内部設定を参考にした. NCLUは無いので、L2やVNIの設定は
ip
コマンド、L3の設定はFRR
を直接叩く必要がある.
Cumulus だとVLAN-aware BridgeというひとつのbridgeにすべてのVNIを接続させてVLANでサブネットを分離していた。今回はLXCとの親和性からサブネットごとにbridgeを作成してL2分離した.
Cumulus本家も、本検証とは異なるアプローチ(vlan-aware Bridge + nodeはnamespaceで代用)でサーバVTEPを検証している.VTEPの構築
Ubuntu18.04-LTS
(bionic)を2台用意する. 互いにping疎通可であるとする.
ネットワークの設定方法がnetplan
を使う方法になっているので注意する.Linux Kernel のアップグレード
ip
コマンドのVRF
機能やFRR
の要件より、デフォルトより新しめのカーネルにする必要がある.sudo su - #------------------------------------------------------- # Update repository #------------------------------------------------------- cat > /etc/apt/sources.list <<EOF deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic main restricted deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-updates main restricted deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic universe deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-updates universe deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic multiverse deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-updates multiverse deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-backports main restricted universe multiverse deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-security main restricted deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-security universe deb http://ftp.jaist.ac.jp/pub/Linux/ubuntu/ bionic-security multiverse EOF apt-get -y update #------------------------------------------------------- # Upgrade linux kernel to 4.19 #------------------------------------------------------- mkdir linux4.19/ && cd $_ wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-headers-4.19.0-041900_4.19.0-041900.201810221809_all.deb wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-headers-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-image-unsigned-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-modules-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb dpkg -i *.deb reboot # Check > 4.19.0-041900-generic uname -rsysctl の調整
linux内のbridge間でパケットを転送し合ったりVRFを有効にするために
sysctl
の設定も入れておく. 新しいことをやろうとするとよく嵌まるポイント.
- CentOS7 の ip_forward 有効化で rp_filter が邪魔してる話
- Linux VRF with L3 Master Device
- VRF for Linux — a contribution to the Linux Kernel
CumulusからLinuxをルータ/スイッチとして使うにあたり、ARPの挙動をどのように変更したかの解説記事が出ている. 本記事ではLinuxのデフォルトの設定を利用しているが、いつか設定を見直すかもしれない.
#------------------------------------------------------- # sysctl #------------------------------------------------------- sysctl -w net.ipv4.ip_forward=1 sysctl -w net.ipv4.tcp_l3mdev_accept=1 sysctl -w net.ipv4.udp_l3mdev_accept=1 sysctl -pFRR のインストール
インストールの仕方は色々あるようだ. 今回は
dpkg
でインストール.# Install wget https://github.com/FRRouting/frr/releases/download/frr-6.0.2/frr_6.0.2-0.ubuntu18.04.1_amd64.deb dpkg -i dpkg -i frr_6.0.2-0.ubuntu18.04.1_amd64.deb # Enable routing daemons sed -i -e "s/zebra=no/zebra=yes/g" /etc/frr/daemons sed -i -e "s/bgpd=no/bgpd=yes/g" /etc/frr/daemons cat /etc/frr/daemons | grep yes # Restart systemctl enable frr systemctl restart frr systemctl status frr # Check version vtysh -c "show version"LXD の設定
Bridge接続ができればいいので
KVM
でも問題ないが取り回しが重いのと、UbuntuなのでLXDを使う.#------------------------------------------------------- # LXD 3.0 #------------------------------------------------------- # Install LXD apt-get install -y lxd lxc --version # > 3.0.3 # Initialization lxd init # Would you like to use LXD clustering? (yes/no) [default=no]: # Do you want to configure a new storage pool? (yes/no) [default=yes]: # Name of the new storage pool [default=default]: # Name of the storage backend to use (btrfs, ceph, dir, lvm) [default=btrfs]: dir # Would you like to connect to a MAAS server? (yes/no) [default=no]: # Would you like to create a new local network bridge? (yes/no) [default=yes]: no # Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: # Would you like LXD to be available over the network? (yes/no) [default=no]: # Would you like stale cached images to be updated automatically? (yes/no) [default=yes] # Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: yes # config: {} # networks: [] # storage_pools: # - config: {} # description: "" # name: default # driver: dir # profiles: # - config: {} # description: "" # devices: # root: # path: / # pool: default # type: disk # name: default # cluster: null # Get new container image lxc init ubuntu:18.04 ubuntu FINGER_PRINT=$(lxc image list --format json | jq -r .[].fingerprint) lxc image alias create ubuntu1804 $FINGER_PRINT lxc image list #------------------------------------------------------- # Create original container image for PoC #------------------------------------------------------- # Deploy a container lxc launch ubuntu1804 test lxc exec test bash # SSH sed -i -e "s/PasswordAuthentication no/PasswordAuthentication yes/g" /etc/ssh/sshd_config sed -i -e "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config systemctl restart ssh systemctl status ssh # Change default root password echo root:ubuntu | chpasswd # Stop DHCP DISCOVER traffic # 今回は静的に各コンテナにIPアドレスを設定する sed -i -e "s/dhcp4: true/dhcp4: false/g" /etc/netplan/50-cloud-init.yaml # Generate new container image lxc stop test lxc publish test --alias demo lxc list lxc remove testLinux Network の設定
bridge や VNIを作る. ここから混乱しやすくなるので上図を見ながら設定を投入していく.
各VRFのルーティングテーブルに宛先エントリがない場合、
unreachable
にしてルックアップを終了する設定を入れる必要がある. この設定がないとip rule show
にしたがって物理サーバのルーティングテーブルを使って、オーバレイネットワークのパケットがVRFから漏れ出してしまうので蓋をしておく.#------------------------------------------------------- # Underlay IF MTU (+50 due to VXLAN overhead) #------------------------------------------------------- NETWORK_IF=ens38 ip link set $NETWORK_IF mtu 1550 #------------------------------------------------------- # VRF #------------------------------------------------------- # Create VRF ip link add vrf-1 type vrf table 101000 ip link add vrf-2 type vrf table 102000 # Up VRF IF ip link set dev vrf-1 up ip link set dev vrf-2 up # Check (state UP / vrf table 10XXXX) ip -d link show vrf-1 ip -d link show vrf-2 # Check Lookup table ip rule show # > 0: from all lookup local # > 1000: from all lookup [l3mdev-table] <--- Check # > 32766: from all lookup main # > 32767: from all lookup default # Tenant Isolation ip route add table 101000 unreachable default metric 4278198272 ip route add table 102000 unreachable default metric 4278198272 # Check ip route show vrf vrf-1 ip route show vrf vrf-2 # > unreachable default metric 4278198272 # VRF: rp_filter # ここを設定しないとVRFを跨ぐ時にL3VNIからbridgeにパケットが転送されない sysctl -w net.ipv4.conf.vrf-1.rp_filter=0 sysctl -w net.ipv4.conf.vrf-2.rp_filter=0 sysctl -pSubnet と L2VNI の設定
NETWORK_IF=ens38 VTEP_IPADDR=$(ip -4 addr show $NETWORK_IF | grep -oP '(?<=inet\s)\d+(\.\d+){3}') echo $VTEP_IPADDR #-------------------------------------------- # L2VNI (vrf-1/subnet1-1) #-------------------------------------------- L2VNI=101001 SUBNET=10.1.1.0/24 SUBNET_GATEWAY=10.1.1.254 SUBNET_GATEWAY_PREFIX=${SUBNET_GATEWAY}/24 VRF=vrf-1 ip link add L2VNI${L2VNI} type vxlan id ${L2VNI} local $VTEP_IPADDR dstport 4789 nolearning ip link set L2VNI${L2VNI} mtu 9000 ip link set L2VNI${L2VNI} up bridge link set dev L2VNI${L2VNI} neigh_suppress on brctl addbr br${L2VNI} brctl stp br${L2VNI} off brctl addif br${L2VNI} L2VNI${L2VNI} ip addr add dev br${L2VNI} ${SUBNET_GATEWAY_PREFIX} ip link set br${L2VNI} up ip link set br${L2VNI} master $VRF ip route add ${SUBNET} via ${SUBNET_GATEWAY} dev br${L2VNI} # Check brctl show ip -d link show L2VNI${L2VNI} vtysh -c "show interface L2VNI${L2VNI}" ip addr show vrf $VRF ip route show vrf $VRF #-------------------------------------------- # L2VNI (vrf-1/subnet1-2) #-------------------------------------------- L2VNI=101002 SUBNET=10.1.2.0/24 SUBNET_GATEWAY=10.1.2.254 SUBNET_GATEWAY_PREFIX=${SUBNET_GATEWAY}/24 VRF=vrf-1 ip link add L2VNI${L2VNI} type vxlan id ${L2VNI} local $VTEP_IPADDR dstport 4789 nolearning ip link set L2VNI${L2VNI} mtu 9000 ip link set L2VNI${L2VNI} up bridge link set dev L2VNI${L2VNI} neigh_suppress on brctl addbr br${L2VNI} brctl stp br${L2VNI} off brctl addif br${L2VNI} L2VNI${L2VNI} ip addr add dev br${L2VNI} ${SUBNET_GATEWAY_PREFIX} ip link set br${L2VNI} up ip link set br${L2VNI} master $VRF ip route add ${SUBNET} via ${SUBNET_GATEWAY} dev br${L2VNI} # Check brctl show ip -d link show L2VNI${L2VNI} vtysh -c "show interface L2VNI${L2VNI}" ip addr show vrf $VRF ip route show vrf $VRF #-------------------------------------------- # L2VNI (vrf-2/subnet2-1) #-------------------------------------------- L2VNI=102001 SUBNET=10.2.1.0/24 SUBNET_GATEWAY=10.2.1.254 SUBNET_GATEWAY_PREFIX=${SUBNET_GATEWAY}/24 VRF=vrf-2 ip link add L2VNI${L2VNI} type vxlan id ${L2VNI} local $VTEP_IPADDR dstport 4789 nolearning ip link set L2VNI${L2VNI} mtu 9000 ip link set L2VNI${L2VNI} up bridge link set dev L2VNI${L2VNI} neigh_suppress on brctl addbr br${L2VNI} brctl stp br${L2VNI} off brctl addif br${L2VNI} L2VNI${L2VNI} ip addr add dev br${L2VNI} ${SUBNET_GATEWAY_PREFIX} ip link set br${L2VNI} up ip link set br${L2VNI} master $VRF ip route add ${SUBNET} via ${SUBNET_GATEWAY} dev br${L2VNI} # Check brctl show ip -d link show L2VNI${L2VNI} vtysh -c "show interface L2VNI${L2VNI}" ip addr show vrf $VRF ip route show vrf $VRF #-------------------------------------------- # Sample output #-------------------------------------------- # root@vtep-1:~# ip route show vrf vrf-1 # unreachable default metric 4278198272 # 10.1.1.0/24 dev br101001 proto kernel scope link src 10.1.1.254 # 10.1.2.0/24 dev br101002 proto kernel scope link src 10.1.2.254 # root@vtep-1:~# ip route show vrf vrf-2 # unreachable default metric 4278198272 # 10.2.1.0/24 dev br102001 proto kernel scope link src 10.2.1.254 # root@vtep-1:~# brctl show # bridge name bridge id STP enabled interfaces # br101001 8000.e2a8f93a7056 no L2VNI101001 # br101002 8000.ea09e6adfcce no L2VNI101002 # br102001 8000.6e4881804f2b no L2VNI102001 # Check VXLAN IF are UP # root@vtep-1:~# ip link show type vxlan | grep L2VNI # 13: L2VNI102001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br102001 state UNKNOWN mode DEFAULT group default qlen 1000 # 19: L2VNI101001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br101001 state UNKNOWN mode DEFAULT group default qlen 1000 # 21: L2VNI101002: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br101002 state UNKNOWN mode DEFAULT group default qlen 1000 # Check bridge are UP # root@vtep-1:~# ip link show type bridge | grep mtu # 34: br101001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-1 state UP mode DEFAULT group default qlen 1000 # 36: br101002: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-1 state UP mode DEFAULT group default qlen 1000 # 38: br102001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-2 state UP mode DEFAULT group default qlen 1000L3VNI の設定
NETWORK_IF=ens38 VTEP_IPADDR=$(ip -4 addr show $NETWORK_IF | grep -oP '(?<=inet\s)\d+(\.\d+){3}') echo $VTEP_IPADDR #------------------------------------------------------- # L3VNI (vrf-1) #------------------------------------------------------- L3VNI=101000 VRF=vrf-1 ip link add L3VNI${L3VNI} type vxlan id ${L3VNI} local $VTEP_IPADDR dstport 4789 nolearning ip link set L3VNI${L3VNI} mtu 9000 ip link set L3VNI${L3VNI} up bridge link set dev L3VNI${L3VNI} neigh_suppress on ip -d link show L3VNI${L3VNI} brctl addbr br${L3VNI} brctl stp br${L3VNI} off brctl addif br${L3VNI} L3VNI${L3VNI} ip link set br${L3VNI} up ip link set br${L3VNI} master $VRF brctl show ip -d link show L3VNI${L3VNI} vtysh -c "show interface L3VNI${L3VNI}" ip addr show vrf $VRF #------------------------------------------------------- # L3VNI (vrf-2) #------------------------------------------------------- L3VNI=102000 VRF=vrf-2 ip link add L3VNI${L3VNI} type vxlan id ${L3VNI} local $VTEP_IPADDR dstport 4789 nolearning ip link set L3VNI${L3VNI} mtu 9000 ip link set L3VNI${L3VNI} up bridge link set dev L3VNI${L3VNI} neigh_suppress on ip -d link show L3VNI${L3VNI} brctl addbr br${L3VNI} brctl stp br${L3VNI} off brctl addif br${L3VNI} L3VNI${L3VNI} ip link set br${L3VNI} up ip link set br${L3VNI} master $VRF brctl show ip -d link show L3VNI${L3VNI} vtysh -c "show interface L3VNI${L3VNI}" ip addr show vrf $VRF #------------------------------------------------------- # Sample output #------------------------------------------------------- # root@vtep-1:~# ip addr show vrf vrf-1 # 16: br101000: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-1 state UP group default qlen 1000 # link/ether 92:7c:04:3f:38:8f brd ff:ff:ff:ff:ff:ff # inet6 fe80::907c:4ff:fe3f:388f/64 scope link # valid_lft forever preferred_lft forever # 20: br101001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-1 state UP group default qlen 1000 # link/ether e2:a8:f9:3a:70:56 brd ff:ff:ff:ff:ff:ff # inet 10.1.1.254/24 scope global br101001 # valid_lft forever preferred_lft forever # inet6 fe80::e0a8:f9ff:fe3a:7056/64 scope link # valid_lft forever preferred_lft forever # 22: br101002: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-1 state UP group default qlen 1000 # link/ether ea:09:e6:ad:fc:ce brd ff:ff:ff:ff:ff:ff # inet 10.1.2.254/24 scope global br101002 # valid_lft forever preferred_lft forever # inet6 fe80::e809:e6ff:fead:fcce/64 scope link # valid_lft forever preferred_lft forever # root@vtep-1:~# ip addr show vrf vrf-2 # 14: br102001: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-2 state UP group default qlen 1000 # link/ether 6e:48:81:80:4f:2b brd ff:ff:ff:ff:ff:ff # inet 10.2.1.254/24 scope global br102001 # valid_lft forever preferred_lft forever # inet6 fe80::6c48:81ff:fe80:4f2b/64 scope link # valid_lft forever preferred_lft forever # 18: br102000: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master vrf-2 state UP group default qlen 1000 # link/ether 02:ed:fb:0d:81:e0 brd ff:ff:ff:ff:ff:ff # inet6 fe80::ed:fbff:fe0d:81e0/64 scope link # valid_lft forever preferred_lft forever # root@vtep-1:~# ip link show type vxlan | grep L3VNI # 15: L3VNI101000: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br101000 state UNKNOWN mode DEFAULT group default qlen 1000 # 17: L3VNI102000: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br102000 state UNKNOWN mode DEFAULT group default qlen 1000 # root@vtep-1:~# brctl show # bridge name bridge id STP enabled interfaces # br101000 8000.d202cf1bf95f no L3VNI101000 # <-- NEW # br101001 8000.bef545052a7c no L2VNI101001 # br101002 8000.7678fc174cb3 no L2VNI101002 # br102000 8000.365d009e5a9f no L3VNI102000 # <-- NEW # br102001 8000.ae8e8ac6fba4 no L2VNI102001FRR の設定
FRR
にBGPとEVPNの設定を投入する.
FRRでBGPピアを設定する際、対向への接続がeBGPマルチホップだとスタティックルートを設定していないとBGP OPEN
を送信しようとしない(BIRDだと静的ルートなしでピアを張れた). したがってデフォルトルートでping疎通できるのにBGPピアが張れない場合はスタティックルートを入れるようにする.FRR VTEP-1 の設定
# Static route for eBGP multi-hop # ポイントツー接続なら不要 UNDERLAY_GATEWAY=$(ip -4 route show | grep default | cut -d ' ' -f3) echo $UNDERLAY_GATEWAY BGP_PEER_IP_ADDRESS=192.168.0.2 ip route add $BGP_PEER_IP_ADDRESS via $UNDERLAY_GATEWAY ip route show # Underlay eBGP Neighbor vtysh conf t router bgp 65001 bgp router-id 1.1.1.1 neighbor 192.168.0.2 remote-as external neighbor 192.168.0.2 ebgp-multihop address-family ipv4 unicast redistribute connected end write memory exit # Enable EVPN vtysh conf t router bgp 65001 address-family l2vpn evpn neighbor 192.168.0.2 activate advertise-all-vni end write memory exit # Overlay BGP (vrf-1) vtysh conf t router bgp 65001 vrf vrf-1 bgp router-id 1.1.1.1 neighbor 192.168.0.2 remote-as external neighbor 192.168.0.2 ebgp-multihop address-family ipv4 unicast redistribute connected address-family l2vpn evpn advertise ipv4 unicast end write memory exit # Overlay BGP (vrf-2) vtysh conf t router bgp 65001 vrf vrf-2 bgp router-id 1.1.1.1 neighbor 192.168.0.2 remote-as external neighbor 192.168.0.2 ebgp-multihop address-family ipv4 unicast redistribute connected address-family l2vpn evpn advertise ipv4 unicast end write memory exit #------------------------------------------------------- # Mapping VRF and L3VNI for vrf-1 and vrf-2 #------------------------------------------------------- vtysh conf t vrf vrf-1 vni 101000 vrf vrf-2 vni 102000 end write memory exitFRR VTEP-2 の設定
# Static route for eBGP multi-hop UNDERLAY_GATEWAY=$(ip -4 route show | grep default | cut -d ' ' -f3) echo $UNDERLAY_GATEWAY BGP_PEER_IP_ADDRESS=192.168.0.1 ip route add $BGP_PEER_IP_ADDRESS via $UNDERLAY_GATEWAY ip route show # Underlay eBGP Neighbor vtysh conf t router bgp 65002 bgp router-id 2.2.2.2 neighbor 192.168.0.1 remote-as external neighbor 192.168.0.1 ebgp-multihop address-family ipv4 unicast redistribute connected end write memory exit # Enable EVPN vtysh conf t router bgp 65002 address-family l2vpn evpn neighbor 192.168.0.1 activate advertise-all-vni end write memory exit # Overlay BGP (vrf-1) vtysh conf t router bgp 65002 vrf vrf-1 bgp router-id 2.2.2.2 neighbor 192.168.0.1 remote-as external neighbor 192.168.0.1 ebgp-multihop address-family ipv4 unicast redistribute connected address-family l2vpn evpn advertise ipv4 unicast end write memory exit # Overlay BGP (vrf-2) vtysh conf t router bgp 65002 vrf vrf-2 bgp router-id 2.2.2.2 neighbor 192.168.0.1 remote-as external neighbor 192.168.0.1 ebgp-multihop address-family ipv4 unicast redistribute connected address-family l2vpn evpn advertise ipv4 unicast end write memory exit #------------------------------------------------------- # Mapping VRF and L3VNI for vrf-1 and vrf-2 #------------------------------------------------------- vtysh conf t vrf vrf-1 vni 101000 vrf vrf-2 vni 102000 end write memory exitFRR コマンド出力結果
# Check vtysh -c "show running-config" vtysh -c "show bgp summary" vtysh -c "show bgp l2vpn evpn route" vtysh -c "show evpn mac vni all" vtysh -c "show evpn vni" vtysh -c "show evpn vni 101001" vtysh -c "show evpn vni 101002" vtysh -c "show evpn vni 102001" vtysh -c "show evpn vni 101000" vtysh -c "show evpn vni 102000" # Sample output # root@vtep-1:~# vtysh -c "show bgp summary" # # IPv4 Unicast Summary: # BGP router identifier 1.1.1.1, local AS number 65001 vrf-id 0 # BGP table version 3 # RIB entries 5, using 800 bytes of memory # Peers 1, using 21 KiB of memory # # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd # 192.168.0.2 4 65002 23 23 0 0 0 00:05:27 2 # # Total number of neighbors 1 # # L2VPN EVPN Summary: # BGP router identifier 1.1.1.1, local AS number 65001 vrf-id 0 # BGP table version 0 # RIB entries 19, using 3040 bytes of memory # Peers 1, using 21 KiB of memory # # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd # 192.168.0.2 4 65002 23 23 0 0 0 00:05:27 6 # # Total number of neighbors 1 # root@vtep-1:~# vtysh -c "show evpn vni" # VNI Type VxLAN IF # MACs # ARPs # Remote VTEPs Tenant VRF # 102001 L2 L2VNI102001 1 1 1 vrf-2 # 101001 L2 L2VNI101001 1 1 1 vrf-1 # 101002 L2 L2VNI101002 1 1 1 vrf-1 # 101000 L3 L3VNI101000 1 1 n/a vrf-1 # 102000 L3 L3VNI102000 1 1 n/a vrf-2 # root@vtep-1:~# vtysh -c "show evpn vni 101001" # VNI: 101001 # Type: L2 # Tenant VRF: vrf-1 # VxLAN interface: L2VNI101001 # VxLAN ifIndex: 7 # Local VTEP IP: 192.168.0.1 # Remote VTEPs for this VNI: # 192.168.0.2 # Number of MACs (local and remote) known for this VNI: 1 # Number of ARPs (IPv4 and IPv6, local and remote) known for this VNI: 2 # Advertise-gw-macip: No # root@vtep-1:~# vtysh -c "show evpn vni 101000" # VNI: 101000 # Type: L3 # Tenant VRF: vrf-1 # Local Vtep Ip: 192.168.0.1 # Vxlan-Intf: L3VNI101000 # SVI-If: br101000 # State: Up # VNI Filter: none # Router MAC: f6:b1:a7:d6:84:f6 # L2 VNIs: 101001 101002 # root@vtep-1:~# vtysh -c "show evpn vni 102000" # VNI: 102000 # Type: L3 # Tenant VRF: vrf-2 # Local Vtep Ip: 192.168.0.1 # Vxlan-Intf: L3VNI102000 # SVI-If: br102000 # State: Up # VNI Filter: none # Router MAC: b2:e2:2d:18:e5:9b # L2 VNIs: 102001LXD の起動
# Network Profile lxc profile create subnet1-1 lxc profile create subnet1-2 lxc profile create subnet2-1 lxc profile device add subnet1-1 root disk path=/ pool=default lxc profile device add subnet1-1 eth0 nic name=eth0 nictype=bridged parent=br101001 lxc profile device add subnet1-2 root disk path=/ pool=default lxc profile device add subnet1-2 eth0 nic name=eth0 nictype=bridged parent=br101002 lxc profile device add subnet2-1 root disk path=/ pool=default lxc profile device add subnet2-1 eth0 nic name=eth0 nictype=bridged parent=br102001 lxc profile listLXC の起動 (VTEP1)
cat > boot_lxc.sh <<EOF # Boot Container lxc list lxc launch ubuntu1804 lxc-1 -p subnet1-1 lxc launch ubuntu1804 lxc-3 -p subnet1-1 lxc launch ubuntu1804 lxc-5 -p subnet1-2 lxc launch ubuntu1804 lxc-7 -p subnet1-2 lxc launch ubuntu1804 lxc-9 -p subnet2-1 lxc launch ubuntu1804 lxc-11 -p subnet2-1 lxc list # IP address setting lxc exec lxc-1 -- ip link set eth0 up lxc exec lxc-1 -- ip addr add 10.1.1.1/24 dev eth0 lxc exec lxc-1 -- ip route add default via 10.1.1.254 dev eth0 lxc exec lxc-3 -- ip link set eth0 up lxc exec lxc-3 -- ip addr add 10.1.1.3/24 dev eth0 lxc exec lxc-3 -- ip route add default via 10.1.1.254 dev eth0 lxc exec lxc-5 -- ip link set eth0 up lxc exec lxc-5 -- ip addr add 10.1.2.5/24 dev eth0 lxc exec lxc-5 -- ip route add default via 10.1.2.254 dev eth0 lxc exec lxc-7 -- ip link set eth0 up lxc exec lxc-7 -- ip addr add 10.1.2.7/24 dev eth0 lxc exec lxc-7 -- ip route add default via 10.1.2.254 dev eth0 lxc exec lxc-9 -- ip link set eth0 up lxc exec lxc-9 -- ip addr add 10.2.1.9/24 dev eth0 lxc exec lxc-9 -- ip route add default via 10.2.1.254 dev eth0 lxc exec lxc-11 -- ip link set eth0 up lxc exec lxc-11 -- ip addr add 10.2.1.11/24 dev eth0 lxc exec lxc-11 -- ip route add default via 10.2.1.254 dev eth0 lxc list EOF cat > delete_lxc.sh <<EOF # Delete container lxc delete -f lxc-1 lxc delete -f lxc-3 lxc delete -f lxc-5 lxc delete -f lxc-7 lxc delete -f lxc-9 lxc delete -f lxc-11 EOF chmod 755 boot_lxc.sh delete_lxc.sh sh -x boot_lxc.sh # root@vtep-1:~# lxc list # +--------+---------+------------------+------+------------+-----------+ # | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | # +--------+---------+------------------+------+------------+-----------+ # | lxc-1 | RUNNING | 10.1.1.1 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-11 | RUNNING | 10.2.1.11 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-3 | RUNNING | 10.1.1.3 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-5 | RUNNING | 10.1.2.5 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-7 | RUNNING | 10.1.2.7 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-9 | RUNNING | 10.2.1.9 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # Enter lxc lxc exec lxc-1 bash ifconfig ping 10.1.1.254 exitLXC の起動 (VTEP2)
cat > boot_lxc.sh <<EOF #!/bin/bash # Boot Container lxc list lxc launch ubuntu1804 lxc-2 -p subnet1-1 lxc launch ubuntu1804 lxc-4 -p subnet1-1 lxc launch ubuntu1804 lxc-6 -p subnet1-2 lxc launch ubuntu1804 lxc-8 -p subnet1-2 lxc launch ubuntu1804 lxc-10 -p subnet2-1 lxc launch ubuntu1804 lxc-12 -p subnet2-1 lxc list # IP address setting lxc exec lxc-2 -- ip link set eth0 up lxc exec lxc-2 -- ip addr add 10.1.1.2/24 dev eth0 lxc exec lxc-2 -- ip route add default via 10.1.1.254 dev eth0 lxc exec lxc-4 -- ip link set eth0 up lxc exec lxc-4 -- ip addr add 10.1.1.4/24 dev eth0 lxc exec lxc-4 -- ip route add default via 10.1.1.254 dev eth0 lxc exec lxc-6 -- ip link set eth0 up lxc exec lxc-6 -- ip addr add 10.1.2.6/24 dev eth0 lxc exec lxc-6 -- ip route add default via 10.1.2.254 dev eth0 lxc exec lxc-8 -- ip link set eth0 up lxc exec lxc-8 -- ip addr add 10.1.2.8/24 dev eth0 lxc exec lxc-8 -- ip route add default via 10.1.2.254 dev eth0 lxc exec lxc-10 -- ip link set eth0 up lxc exec lxc-10 -- ip addr add 10.2.1.10/24 dev eth0 lxc exec lxc-10 -- ip route add default via 10.2.1.254 dev eth0 lxc exec lxc-12 -- ip link set eth0 up lxc exec lxc-12 -- ip addr add 10.2.1.12/24 dev eth0 lxc exec lxc-12 -- ip route add default via 10.2.1.254 dev eth0 lxc list cat > delete_lxc.sh <<EOF # Delete container #!/bin/bash lxc delete -f lxc-2 lxc delete -f lxc-4 lxc delete -f lxc-6 lxc delete -f lxc-8 lxc delete -f lxc-10 lxc delete -f lxc-12 EOF chmod 755 boot_lxc.sh delete_lxc.sh sh -x boot_lxc.sh # root@vtep-2:~# lxc list # +--------+---------+------------------+------+------------+-----------+ # | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | # +--------+---------+------------------+------+------------+-----------+ # | lxc-10 | RUNNING | 10.2.1.10 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-12 | RUNNING | 10.2.1.12 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-2 | RUNNING | 10.1.1.2 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-4 | RUNNING | 10.1.1.4 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-6 | RUNNING | 10.1.2.6 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # | lxc-8 | RUNNING | 10.1.2.8 (eth0) | | PERSISTENT | 0 | # +--------+---------+------------------+------+------------+-----------+ # Check Ping lxc exec lxc-2 bash ifconfig ping 10.1.1.254 exit動作検証
今回は
Route Leaking
設定をしていないので異なるVRF配下のネットワーク(Case-3)には到達できない.# VTEP-1 lxc exec lxc-1 bash ping 10.1.1.2 # Case-1: 同じsubnet --> OK ping 10.1.2.6 # Case-2: 異なるsubnet --> OK ping 10.2.1.10 # Case-3: 異なるVRF --> Destination Unreachable exit確認コマンド
#----------------------------------------- # common #----------------------------------------- tcpdump -i any port 4789 # VXLAN のパケット監視 tcpdump -i any port 179 # BGP/EVPN のパケット監視 #----------------------------------------- # L2 #----------------------------------------- bridge fdb show # bridge の Forwarding DB の確認 bridge monitor link # bridge のイベント監視 bridge monitor fdb ip neighbor # ARPテーブル確認 ip neighbor show dev br101001 ip -s neigh flush all # ARPテーブル初期化 #----------------------------------------- # L3 #----------------------------------------- ip route show vrf vrf-1 # ルーティングテーブルの確認 vtysh -c "show running-config" # FRR の設定 vtysh -c "show bgp summary" # BGP 情報 vtysh -c "show bgp evpn route" # EVPN 情報考察: EVPN で Type-2 ルートが広報される条件
cumulus がleafスイッチの場合、配下のコンテナやVMで通信を発生させれば自動的に送信元と宛先のType-2ルート(/32)がルーティングテーブルに載ってくるのだが今回は挙動が異なる.
例えば上記動作検証のCase-1において、対向のVTEP配下のノードに通信を発生させても、送信元と宛先両方ともEVPNで経路広報の対象にならない. Case-2の場合は送信元10.1.1.1@VTEP1
のType-2がVTEP-2に広報され、10.1.2.6@VTEP-2
の経路はVTEP-1に広報されていない.# Case-2 実施後のルーティングテーブル root@vtep-1:~# ip route show vrf vrf-1 unreachable default metric 4278198272 10.1.1.0/24 dev br101001 proto kernel scope link src 10.1.1.254 10.1.2.0/24 dev br101002 proto kernel scope link src 10.1.2.254 # 10.1.2.6/32 のエントリが存在しない root@vtep-2:~# ip route show vrf vrf-1 unreachable default metric 4278198272 10.1.1.0/24 dev br101001 proto kernel scope link src 10.1.1.254 10.1.1.1 via 192.168.0.1 dev br101000 proto bgp metric 20 onlink # <-- ホストルート 10.1.2.0/24 dev br101002 proto kernel scope link src 10.1.2.254結論から言うと、FRRが稼働する物理サーバ側の
ip neighbor
(ARPテーブル)にREACHABLE
で一度エントリが載らないとEVPNで経路が広報されないということがわかった.root@vtep-1:~# ip neighbor show dev br101001 10.1.1.1 lladdr 00:16:3e:c9:30:44 STALE # <-- エントリが存在 root@vtep-1:~# ip neighbor show dev br101002 10.1.2.6 lladdr 00:16:3e:e0:59:6b STALE # エントリが存在しない root@vtep-2:~# ip neighbor show dev br101001 root@vtep-2:~# ip neighbor show dev br101002
ip neighbor
にエントリを載せるにはそのサブネットのgatewayに対してARP Request/Reply
の通信を発生させる必要があるようだ.Case-1
の場合、同サブネット間の通信の通信はgatewayを経由しないので物理ホスト側のARPテーブルにはエントリが載らない.Case-2
の場合、10.1.1.1
から10.1.1.254
へARPリクエストが発生するためARPテーブルにエントリが載る.これによりEVPN Type-2で対向VTEPに10.1.1.1/32
の経路が載るようだ. しかしこの理屈だと、戻りの通信(10.1.2.6@VTEP2
-->10.1.1.1@VTEP1
)でも10.1.2.254
へのARPリクエストが発生するので10.1.2.6/32
の経路もEVPN Type2でVTEP1に広報されてもいい気がするのだが、なぜそうならないのかはコードをよく読まないとわからない.このEVPN Type2がスムーズに広報されない問題を回避する最も簡単な方法は、コンテナ起動時に物理ホストから各コンテナに対して通信を意図的に発生させることである. Cumulus本家のPoC ではハイパーバイザ側からVMにpingしてEVPN Type2を強制的に広報させているように見える. 別のアプローチでこの問題をスマートに解決する有効なオプションや設定を現時点では見つけることができなかった(ので知ってる方いたらおしえてほしい).
LXCコンテナを削除した際には、
BGP UPDATE(Withdrawn Routes)
が広報されることにより、該当コンテナのホストルートはルーティングテーブルから削除される.この辺りの実装を見るとbridge fdb show
やip neighbor
を監視することでコンテナやVMの削除を検知していることがわかる.Source NAT
VXLAN/EVPN に関係ないが GitLab: evpn-to-the-host の Future work にあった NAT もついでに検証する. LinuxでVRFを前提にNATするのは初ケースなので動作する設定を見つけ出すのに苦労した. 説明しづらいので図も添付する.
まず物理ホストにて外部との接続を持つインターフェースをブリッジ(br0)に所属させる. 次にOverlay NetworkのパケットをどうにかしてUnderlay Networkに持っていくために、UnderlayのVRF(VRF-PUBLIC)を作成し仮のインターフェースを作成する(vrf_br0). FRRでRoute Leakingを設定し、デフォルトルートをVRF-PUBLICに向ける. あとはiptablesでSource NATをしてやれば、物理ホストのインターフェースを使用して外に出ることができる.
デフォルトルートでRoute LeakingをするのはCumulus のマニュアルによると非推奨だがとりあえず動作はした. デフォルトルートで無闇にリークしてしまうとVRFによる隔離が弱くなる可能性があるため、実際に使う際はよく考えてから設定する必要がある.#---------------------------------------------------- # br0 #---------------------------------------------------- # Create a bridge brctl addbr br0 brctl stp br0 off ip link set dev br0 up ip addr add 172.16.25.100/24 dev br0 sysctl -w net.ipv4.conf.br0.rp_filter=0 sysctl -p # 管理インターフェースの ens33 を br0 に参加させる # ens33 で接続していた通信は切れるので注意 ip addr flush dev ens33 brctl addif br0 ens33 ip link set dev ens33 up ip route add default via 172.16.25.254 dev br0 #---------------------------------------------------- # VRF-PUBLIC #---------------------------------------------------- # Set value VRF_TABLE_ID=103000 PUBLIC_GATEWAY=172.16.25.254 VETH_PUBLIC_IP=172.16.25.101/24 # Create VRF ip link add vrf-public type vrf table $VRF_TABLE_ID ip link set vrf-public up # Check (VRF status is UP) ip -d link show vrf-public ip route show vrf vrf-public ip vrf show # Create a Virtual cable (veth) ip link add name vrf_br0 type veth peer name br0_vrf # Connect (br0)*<-- (br0_vrf) brctl addif br0 br0_vrf ip link set br0_vrf up # Connect (vrf_br0) -->*(VRF) ip addr add $VETH_PUBLIC_IP dev vrf_br0 ip link set dev vrf_br0 master vrf-public ip link set vrf_br0 up # Set default route in VRF ip route add table $VRF_TABLE_ID default via $PUBLIC_GATEWAY dev vrf_br0 # Check brctl show ip addr show vrf_br0 ip addr show vrf vrf-public ip link show br0_vrf ip route show vrf vrf-public ping -I vrf_br0 $PUBLIC_GATEWAY #------------------------------------------------------- # (FRR) RouteLeaking # vrf-1 && vrf-2 --> vrf-public #------------------------------------------------------- vtysh conf t vrf vrf-1 ip route 0.0.0.0/0 vrf-public nexthop-vrf vrf-public vrf vrf-2 ip route 0.0.0.0/0 vrf-public nexthop-vrf vrf-public end write memory exit # Check vtysh -c "show ip route vrf vrf-1" vtysh -c "show ip route vrf vrf-2" ip route show vrf vrf-1 ip route show vrf vrf-2 #-------------------------------------------------------- # SNAT (overlay --> public) #-------------------------------------------------------- SNAT_IPADDR=$(ip -4 addr show br0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') echo $SNAT_IPADDR iptables -t nat -A POSTROUTING -p all -s 10.1.1.0/24 -j SNAT --to-source $SNAT_IP_ADDR iptables -t nat -A POSTROUTING -p all -s 10.1.2.0/24 -j SNAT --to-source $SNAT_IP_ADDR iptables -t nat -A POSTROUTING -p all -s 10.2.1.0/24 -j SNAT --to-source $SNAT_IP_ADDR iptables -t nat -L # ping check lxc list lxc exec lxc-1 bash ping 8.8.8.8 exit物理サーバでルーティングできるので、任意のVRFをBGPでサーバまで伝搬させれば分散NATが可能となる.
おわり
結論として、ただのLinuxにFRRをインストールすることで、従来leafスイッチでやっていたEVPN+VXLANの処理をサーバエンドにオフロードすることができた. 数年前にOpenStack NeutronとNamespaceを駆使して実現できたことをBGPやVRFで置き換えているだけなのだが、SPOFが存在しないのでより洗練されたアーキテクチャになった. VXLANのオーバヘッドに懸念があるのでネットワーク性能は試験する必要がある. またセキュリティ(ACL)や細かい動作などを考慮していないので実用にはまだ遠い.
- 投稿日:2019-02-11T23:35:32+09:00
アリババCloudを使って見ました。(hello worldまで)
環境:
・OS:Linux Ubuntu 14.04 64位
・Nginx
・Gunicorn
・supervisorアリババcloudでECSを購入して、ssh接続方法でウェブサーバに接続できます。
ssh root@[IPアドレス]
パースワードを入力すれば、こんな画面表示されます。
step1: pythonの環境の用意
・ウェブサーバの方は、python2があ李ますが、python3ないので、必要な場合はダウンロードするべき。
・自分のプロジェクトをFTP ソフトとか scpでコマンドでウェブサーバーアプロードします。
$ scp -r [localのpath] root@abc.com:[サーバーのpath]
step2: Gunicornのインストール
Gunicornは仮想環境を使うべき(virtualenv)
(venv) $ pip install gunicornGunicornを実行する
gunicorn -w 4 -b 127.0.0.1:8000 「ファイル名」:app
ここでは簡単なhello.pyを実行してみます。hello.pyfrom flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'hello world' if __name__ == '__main__': from werkzeug.contrib.fixers import ProxyFix app.wsgi_app = ProxyFix(app.wsgi_app) app.run()実行成功したら、 webサーバで訪問できます。
curl http://127.0.0.1:8000/
上のコマンドで、terminalで確かめることができます。step3: nginxのインストール&環境設定
・nginxをインストール
$ sudo apt-get update $ sudo apt-get install nginx $ sudo chmod 777・ngixnの環境設定
sudo cp /etc/nginx/site-avalidable/default /etc/nginx/site-avalidable/default.bak sudo vim /etc/nginx/sites-avalidable/default・相応な部分を編集します。
server { listen 80; server_name example.org; # ドメインまたは、IPアドレス location / { proxy_pass http://127.0.0.1:8080; # ここは gunicorn hostアドレス proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }設定した後、再実行します。
sudo service nginx restart
これで、OK。
ドメインとかIPアドレスで、自分のホームページを訪問できます。備考
・nginxコマンド
起動: sudo service nginx start restart: sudo service nginx restart stop: sudo service nginx stop テスト: sudo service nginx configtest
- 投稿日:2019-02-11T16:14:57+09:00
wpa_supplicantのコード解説 ①起動
はじめに
LinuxのWi-Fi機能はwpa_supplicantを使用します。このwpa_supplicantはユーザー空間上で動作するデーモンプロセスであり、アプリケーションからのコマンドを受け付けたり、ドライバを制御したりします。その内部構造を知りたいと思い、ソースコードを調べてみました。今後もし余力があればスキャン、接続などの動作について調べようと思いますが、まずはwpa_supplicantの起動処理について記述します。
wpa_supplicantの起動方法
私のRaspberry Piではデフォルトでwpa_supplicantが起動しているので、一旦プロセスを落としてから起動しています。手順とコマンドは以下です。
- LinuxのWiFiをOFFにする
- wpa_supplicantを再起動し、CLIツールを起動
# sudo kill PID #wpa_supplicantのPIDを指定 # sudo wpa_supplicant -i wlan0 -c ./wpa_supplicant.conf & # sudo wpa_cli -i wlan0 -p /var/run/wpa_supplicant主に以下の起動オプションを設定します。
オプション 内容 -B バックグラウンドで起動(&を付けるのと同じ) -c 設定ファイルのパス -i インターフェース(wlan0等) -d デバッグ出力を増やす(-ddでさらに増える) -D ドライバを指定(nl80211等) -N 複数の-i,-cを指定してConcurrent動作をするときに使う 設定ファイルであるwpa_supplicant.confは以下のものを使っています。
wpa_supplicant.confctrl_interface=/var/run/wpa_supplicant update_config=1起動処理の一連の流れ
起動処理は以下のように進みます。各々は以降で説明していきます。
- コマンドラインオプションの読み取り・設定
- 共通部分の初期化(イベントループ、乱数生成、制御I/Fの初期化)
- 各Network I/Fの初期化(ドライバの設定、ステートマシンの初期化、制御I/Fの初期化)
- 動作開始(アプリとの接続、イベントループの開始)
コマンドラインオプションの読み取り・設定
main()の中で各オプションを読み取っています。
共通の設定はparamsを経由してwpa_supplicant_init()で行われます。インターフェースごとの設定はifaceを経由してwpa_supplicant_add_iface()で行われます。
-Nで二つ目のインターフェースがあることを指定すると、ifacesをreallocして、ifaceに二つ目のポインタを設定してオプションを読み取ります。paramsに代入するオプションはインターフェースごとに指定できません。wpa_supplicant/main.cint main(int argc, char *argv[]) { // struct wpa_interface *ifaces, *iface; // intarfaceの設定 // struct wpa_params params; // 共通設定(一時変数) struct wpa_global *global; // 共通設定(グローバル変数) // for (;;) { c = getopt(argc, argv, "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW"); if (c < 0) break; switch (c) { // case 'c': iface->confname = optarg; // 設定ファイルのパス break; // case 'D': iface->driver = optarg; // ドライバ名(nl80211など) break; case 'd': // params.wpa_debug_level--; // デバッグレベル break; // case 'i': iface->ifname = optarg; // Network I/F名 break; // case 'N': iface_count++; iface = os_realloc_array(ifaces, iface_count, sizeof(struct wpa_interface)); // 追加するinterface分のメモリを拡張 if (iface == NULL) goto out; ifaces = iface; // ifacesは最初のinterfaceを指す iface = &ifaces[iface_count - 1]; // ifaceは追加したinterfaceを指す(この後、-i,-d等で設定) os_memset(iface, 0, sizeof(*iface)); break; // global = wpa_supplicant_init(¶ms); // paramsの内容をglobalにコピー。共通部分の初期化(後述) // for (i = 0; exitcode == 0 && i < iface_count; i++) { // 各Network I/Fごとにループ // wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL); // 各Network I/Fの初期化(後述) // } // if (exitcode == 0) exitcode = wpa_supplicant_run(global); // イベントループを起動共通部分の初期化
共通部分の初期化はmain()から呼ばれるwpa_supplicant_init()で行います。
イベント処理、乱数生成、制御I/F(イベントを送受信するためのソケット)の初期化などを行います。wpa_supplicant/wpa_supplicant.cstruct wpa_global * wpa_supplicant_init(struct wpa_params *params) { // if (eloop_init()) { // イベントループの初期化 // random_init(params->entropy_file); // 乱数生成の初期化 // global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); // 制御I/Fの初期化 // }wpa_supplicant_global_ctrl_iface_init()ではアプリと通信するために、ソケットを取得して接続しています。
ctrlにソケットのパスが入っています。wpa_supplicant/ctrl_iface_unix.cstatic int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, struct ctrl_iface_global_priv *priv) { // priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); // ソケットの取得 // os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path)); // パス設定 if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { // 接続 //各Network I/Fの初期化
次はwlan0などのNetwork I/Fごとの設定を見ていきます。
Network I/Fの状態変数を取得して、DISCONNECTED状態として初期化します。wpa_supplicant/wpa_supplicant.cstruct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent) { struct wpa_supplicant *wpa_s; // I/Fごとの状態変数 struct wpa_interface t_iface; // wpa_s = wpa_supplicant_alloc(parent); // t_iface = *iface; // if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { // I/Fの初期化(後述) // wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); // 未接続状態にして追加完了 // return wpa_s; }次はwpa_supplicant_init_iface()の中身です。
-cで指定した設定ファイルの読み込み、-Dで指定したドライバの設定、状態の初期化などをしています。wpa_supplicant/wpa_supplicant.cstatic int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, const struct wpa_interface *iface) { // wpa_s->conf = wpa_config_read(wpa_s->confname, NULL); // -cで指定した設定ファイルの読み込み // if (wpas_init_driver(wpa_s, iface) < 0) // ドライバの設定(後述) // if (wpa_supplicant_init_wpa(wpa_s) < 0) // WPAステートマシン、コールバック(ctx)の設定 // if (wpa_supplicant_driver_init(wpa_s) < 0) // 初期状態を設定 // wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); // Network I/Fごとのソケットの初期化 // }ドライバの設定についてもう少し詳しく見ていきます。
wpa_driver[]にwpa_driver_xxx_ops(各ドライバのコールバック関数)の一覧があり、各opsの中身はdriverフォルダ以下のソースコードで実装されています。-Dオプションの内容から適切なopsを探し出して設定しています。wpa_supplicant/wpa_supplicant.cstatic int wpas_init_driver(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { // if (wpa_supplicant_set_driver(wpa_s, driver) < 0) // driverは-Dで指定した内容(nl80211など) // wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); // wpa_s->driver->init2を呼び出す(後述) // } static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, const char *name) { // for (i = 0; wpa_drivers[i]; i++) { if (os_strlen(wpa_drivers[i]->name) == len && os_strncmp(driver, wpa_drivers[i]->name, len) == // wpa_driversとdriver(nl80211等)を照合 0) { /* First driver that succeeds wins */ if (select_driver(wpa_s, i) == 0) // 見つかれば関数を登録 return 0; } } // } static int select_driver(struct wpa_supplicant *wpa_s, int i) { // wpa_s->driver = wpa_drivers[i]; // 関数を登録 // }wpa_driver[]は以下で定義されています。
デフォルトだと一番上のwpa_driver_nl80211_opsが指定されます。
前述したwpas_init_driver()でwpa_s->driver->init2()が呼ばれていますが、このinit2()もopsで定義されています。src/drivers/drivers.cconst struct wpa_driver_ops *const wpa_drivers[] = { #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, #endif /* CONFIG_DRIVER_NL80211 */ // };動作開始
最後にアプリと結合(ATTACH)して、イベントループを開始します。
main()から呼ばれるwpa_supplicant_run()で処理を行います。
wpa_cliなどのアプリからの結合が終わればイベントループを開始します。wpa_supplicant/wpa_supplicant.cint wpa_supplicant_run(struct wpa_global *global) { // if (global->params.daemonize && (wpa_supplicant_daemon(global->params.pid_file) || // -Bオプションがあればデーモンとする // if (global->params.wait_for_monitor) { for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt) wpa_supplicant_ctrl_iface_wait( // アプリとの結合待ち(後述) wpa_s->ctrl_iface); } // eloop_run(); // イベントループの開始 // }以下のwpa_supplicant_ctrl_iface_wait()にてアプリからのATTACHコマンドを待ちます。ソケットから最初のコマンドを受け取り、ATTACHであればアプリと結合します。
wpa_supplicant/ctrl_iface_unix.cvoid wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { // for (;;) { // eloop_wait_for_read_sock(priv->sock); res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, // ソケットからコマンドを受け取る (struct sockaddr *) &from, &fromlen); // if (os_strcmp(buf, "ATTACH") == 0) { // ATTACH以外受け付けない /* handle ATTACH signal of first monitor interface */ if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from, fromlen, 0)) { if (sendto(priv->sock, "OK\n", 3, 0, // ATTACH成功ならOKを返す (struct sockaddr *) &from, fromlen) < // }まとめ
基本的にwpa_supplicantはアプリ又はドライバからのイベント駆動のプロセスなので、起動中にイベントループの初期化、アプリ・ドライバとの結合部分の初期化をするものだと思えば良いかもしれません。起動の処理中で出てきたイベントループ、wpa_sといった状態変数はスキャン、接続などの動作でも今後出てくるかと思います。
以上がwpa_supplicantの起動処理の説明となります。
記事をお読み頂きありがとうございました。
- 投稿日:2019-02-11T12:16:32+09:00
Linux(Ubuntu18.04.1)でGitBucketを実行してみる(Linux初心者)(その①)
Gitでプルリクエストを使いたいのでLinux初心者が順に環境構築してみた。
プルリクエストをするにはGitHubを使えるようにしたい。
- 社内で使えるか確認したところ、部課長を納得させる説明が必要とのこと。(ソースを社外に置くリスクやら、社内ポリシーに沿った運用方法等…)
- Gitすら知らないステークホルダーたちに対して利点とリスク、はたまた運用方法まで縛られるのは時間の無駄と判断
- ほかにやりようがないか調査すると、社内LAN内で動かせるタイプもあるのを確認。
社内に環境構築できるタイプにしよう
- 候補はGitLab,GitBucket
- 両方ともLinux上で動くソフトウェア
- Linux触ったことないな、GitLabは何やら構築が難しい?GitBucketはJavaがあれば簡単に動く?なるほど。教育コストが少ないLinux-GitBucketでやってみよう。
Linuxの選定
- CentOS?RedHat??
- Ubuntu?
- Windows10のHyper-Vのクイック作成機能にUbuntuってのがある
- UbuntuはデスクトップPCとしてLinuxを使う場合にオススメ
- CentOSはサーバとしてLinuxを立てる場合にオススメ
- よし、Ubuntuってのにしてみよう
Hyper-Vクイック作成でUbuntuをセットアップ
- 全部英語...
- 日本語にするには...パッチをインストールして...
⇒なぜか知らないがコマンドを見様見真似してるうちにUbuntuが動かなくなる。- 日本のチームが作ったUbuntuがある!?
「Ubuntu Desktop 日本語 Remix」「ubuntu Japanese Team」- https://www.ubuntulinux.jp/home
GitBucketを見様見真似でインストールしてみる
Javaをインストールする必要がある。以下のコマンドを実行する。
add-apt-repository ppa:webupd8team/java
apt-get update
apt-get install oracle-java8-installer
いきなりわからないので調べながら実施。
- [add-apt-repository]
ubuntuはaptitude(apt)を使ってパッケージ管理している。その公式リポジトリは「etc/apt/sources.list」 非公式なリポジトリは「add-apt-repository」を使って追加することができる。 追加したリポジトリは「etc/apt/sources.list.d」にある。- [apt-get update]
apt-get で パッケージを取得してインストール/アップデートすること。
update で サーバからパッケージリストを入手する。
- [apt-get install oracle-java8-installer]
install で パッケージをインストールする。じゃあ、実行。
エラー: root で実行する必要があります
管理者ってことかと思ってみると
ubuntuはrootを初期構築時に作らないとのこと。
CentOS等だと[su rootのパスワード]でrootに昇格できるとか。(rootになってると $ が # になるとか)
ubuntuの場合はsudoを使って毎回実行するという記載を発見。試しに以下構文を実行
[sudo add-apt-repository]
ユーザのパスワードを聞かれたのでうまくいってると判断。続行する。apt-get install oracle-java8-installerが途中で止まって焦った
72%で10分くらい停止。あああああ。って思って対処法を調べまくってたらいつの間にか失敗を検知して再ダウンロードを始めてた。すごい。次はGitBucketをダウンロードする。以下のコマンドを実行する。
wget https://github.com/gitbucket/gitbucket/releases/download/4.30.1/gitbucket.war
※releasesのページに飛んでバージョンがいくつになってるか確認してから実行すること。わからないので調べながら実施。
- [wget]
HTTP/HTTPSなどを介してデータをまとめて取得するコマンド。windowsにもあるみたい。
もしかしてこれ使えば自動ダウンロードのソフトウェアとか作れるのかな?
面白そう。後で調べてみよう。- 好きなフォルダに配置するのだがフォルダが作れない。
権限がないのだとわかりsudo mkdirする。
そのフォルダにも権限がない。
よくわからないので少し調査。
(今回はダウンロードフォルダにダウンロード)次はGitBucketを単体で実行する。以下のコマンドを実行する。
java -jar gitbucket.war
起動を確認するためにhttpで接続する。
http://localhost:8080.gitbucket
今回はここまで。
とりあえず動くところまで行ったので一旦終了。
- 投稿日:2019-02-11T03:05:42+09:00
Elasticsreachのデータ暗号化
はじめに
Elasticsearchのデータ暗号化について、検討する機会があったので、記事にまとめてみました^^:
利用した環境
product version cryptsetup 1.7.4 Elasticsearch 6.6.0 OS Amazon Linux2 (4.14.88) Instance Type m5.2xlarge EBS ルート(8GB)、追加(30GB) AMI ami-04677bdaa3c2b6e24 【ディスクの状態】
nvme0n1
が8GBのルートボリューム、nvme1n1
が追加の30GBボリュームです。
※m5はNitro世代のEC2インスタンスでEBS NVMeボリュームのため、/dev/nvmeXnX
という名称[root@ip-172-31-34-49 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT nvme1n1 259:0 0 30G 0 disk nvme0n1 259:1 0 8G 0 disk ├─nvme0n1p1 259:2 0 8G 0 part / └─nvme0n1p128 259:3 0 1M 0 part [root@ip-172-31-34-49 ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 16G 0 16G 0% /dev tmpfs 16G 0 16G 0% /dev/shm tmpfs 16G 388K 16G 1% /run tmpfs 16G 0 16G 0% /sys/fs/cgroup /dev/nvme0n1p1 8.0G 1.2G 6.9G 15% / tmpfs 3.1G 0 3.1G 0% /run/user/1000基本的な考え方
Elasticsearchのデータ(=Index)暗号化では、日立ソリューションズ社のCredeon Secure Full-text Searchがありますが、あくまで全文検索のためのIndex暗号化であって、Kibanaが利用する
aggregation
機能には対応していません。ElasticStackでは5.3.0から保存データの暗号化に対応しています。
サブスクリプションのPlatinum Editionで対応していますが、あくまでdm-crypt
を用いたボリュームの暗号化を利用した場合でもElasticsreachとしてサポートするというものです。暗号化機能が付いている訳ではありません。dm-cryptとは
Device-Mapperの機能の1つでLinuxにおけるブロックデバイス暗号の仕組みです。
Kernel 2.6以降のdevice-mapperモジュールに標準で組み込まれていて、cyptsetup
という暗号化管理ツールを使ってストレージデバイスの暗号化を行います。
【参考】
・LUKSとは?設定手順
- Java 1.8.0 インストール
- Elasticsearch 6.6.0 インストール
- JVMヒープサイズ設定
- Elasticsreach設定
- 暗号化ボリュームの設定
- Index保存領域の作成
- 設定確認
1. Java 1.8.0 インストール
Javaがインストールされていないことを確認した上でJava 1.8.0をインストールします。
[root@ip-172-31-34-49 ~]# java -version -bash: java: command not found [root@ip-172-31-34-49 ~]# yum install -y java-1.8.0-openjdk <インストールの経過は省略> [root@ip-172-31-34-49 ~]# java -version openjdk version "1.8.0_191" OpenJDK Runtime Environment (build 1.8.0_191-b12) OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)2. Elasticsearch 6.6.0 インストール
Elastic社公式のリポジトリを登録し、Elasticsearch 6.6.0をインストールします。
[root@ip-172-31-34-49 ~]# vi /etc/yum.repos.d/elastic.repo [root@ip-172-31-34-49 ~]# cat /etc/yum.repos.d/elastic.repo [elasticsearch-6.x] name=Elasticsearch repository for 6.x packages baseurl=https://artifacts.elastic.co/packages/6.x/yum gpgcheck=1 gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch enabled=1 autorefresh=1 type=rpm-md [root@ip-172-31-34-49 ~]# yum install -y elasticsearch <インストールの経過は省略> [root@ip-172-31-34-49 ~]# systemctl status elasticsearch ● elasticsearch.service - Elasticsearch Loaded: loaded (/usr/lib/systemd/system/elasticsearch.service; disabled; vendor preset: disabled) Active: inactive (dead) Docs: http://www.elastic.co3. JVMヒープサイズ設定
Elasticsreachには物理メモリの半分を上限にJVMヒープを割り当てます。
m5.2xlargeのメモリは32GBなので、今回は15GBのメモリを割り当てます。[root@ip-172-31-34-49 ~]# vi /etc/elasticsearch/jvm.options -Xms15g -Xmx15g # 以下、設定の確認です。 [root@ip-172-31-34-49 ~]# cat /etc/elasticsearch/jvm.options | grep 154. Elasticsreach設定
ElasticsearchのIndex保存領域を
path.data
で指定します。今回は/mnt/es/elasticsearch
とします。[root@ip-172-31-34-49 ~]# vi /etc/elasticsearch/elasticsearch.yml path.data: /mnt/es/elasticsearch # 以下、設定の確認です。 [root@ip-172-31-34-49 ~]# cat /etc/elasticsearch/elasticsearch.yml | grep path.data5. 暗号化ボリュームの設定
cryptsetupコマンドで
/dev/nvme1n1
にAES256/SHA1暗号化ボリューム(esvol
)を作成し、/mnt/es
にマウントします。[root@ip-172-31-34-49 ~]# cryptsetup --key-size 512 --hash sha512 luksFormat /dev/nvme1n1 WARNING! ======== This will overwrite data on /dev/nvme1n1 irrevocably. Are you sure? (Type uppercase yes): YES Enter passphrase: #パスワードを設定 Verify passphrase: #パスワードの確認 [root@ip-172-31-34-49 ~]# cryptsetup luksOpen /dev/nvme1n1 esvol Enter passphrase for /dev/nvme1n1: #上記で設定したパスワードを入力 [root@ip-172-31-34-49 ~]# ls /dev/mapper control esvol [root@ip-172-31-34-49 ~]# mkfs.ext4 /dev/mapper/esvol mke2fs 1.42.9 (28-Dec-2013) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 1966080 inodes, 7863808 blocks 393190 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=2155872256 240 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done [root@ip-172-31-34-49 ~]# mount /dev/mapper/esvol /mnt/es [root@ip-172-31-34-49 ~]# ll /mnt total 4 drwxr-xr-x 3 root root 4096 Feb 10 18:29 es暗号化ボリュームが作成されていることを確認します。
[root@ip-172-31-34-49 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT nvme1n1 259:0 0 30G 0 disk └─esvol 253:0 0 30G 0 crypt /mnt/es nvme0n1 259:1 0 8G 0 disk ├─nvme0n1p1 259:2 0 8G 0 part / └─nvme0n1p128 259:3 0 1M 0 part [root@ip-172-31-34-49 ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 16G 0 16G 0% /dev tmpfs 16G 0 16G 0% /dev/shm tmpfs 16G 392K 16G 1% /run tmpfs 16G 0 16G 0% /sys/fs/cgroup /dev/nvme0n1p1 8.0G 1.5G 6.6G 19% / /dev/mapper/esvol 30G 45M 28G 1% /mnt/es tmpfs 3.1G 0 3.1G 0% /run/user/10006. Index保存領域の作成
/mnt/es配下に
elasticsearch
ディレクトリを作成し、適切な権限を付与します。[root@ip-172-31-34-49 elasticsearch]# cd /mnt/es [root@ip-172-31-34-49 es]# mkdir elasticsearch [root@ip-172-31-34-49 es]# chown elasticsearch:elasticsearch elasticsearch/ [root@ip-172-31-34-49 es]# chmod 750 elasticsearch/ [root@ip-172-31-34-49 es]# ll total 20 drwxr-x--- 2 elasticsearch elasticsearch 4096 Feb 10 21:47 elasticsearch drwx------ 2 root root 16384 Feb 10 18:29 lost+foundElasticsearchをサービス起動し、
path.data
配下にNodeIDファイル(node-0.st
)が生成されていることを確認します。[root@ip-172-31-34-49 es]# systemctl start elasticsearch [root@ip-172-31-34-49 es]# cat elasticsearch/nodes/0/_state/node-0.st ?lstate:) node_idUWTgg6zqmS2Cq72YdCOdCpA(7. 設定確認
- 暗号化した30GBの追加EBSボリュームのSnapshotを作成します。
- 作成したSnapshotから別途EBSボリュームを作成し、別EC2にアタッチします。
EBSをアタッチしたEC2にログインし、追加EBSとして
nvme1n1
が存在していることを確認します。[root@ip-172-31-6-22 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT nvme0n1 259:0 0 100G 0 disk ├─nvme0n1p1 259:1 0 100G 0 part / └─nvme0n1p128 259:2 0 1M 0 part nvme1n1 259:3 0 30G 0 disk [root@ip-172-31-6-22 ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 3.8G 0 3.8G 0% /dev tmpfs 3.8G 0 3.8G 0% /dev/shm tmpfs 3.8G 380K 3.8G 1% /run tmpfs 3.8G 0 3.8G 0% /sys/fs/cgroup /dev/nvme0n1p1 100G 2.2G 98G 3% / tmpfs 763M 0 763M 0% /run/user/1000/mnt/esにマウントし、内容を確認してみます。
[root@ip-172-31-6-22 ~]# mkdir /mnt/es [root@ip-172-31-6-22 ~]# mount -o discard /dev/nvme1n1 /mnt/es mount: /mnt/es: unknown filesystem type 'crypto_LUKS'.まとめ
LUKSで暗号化したボリュームを他マシンにマウントしても認識しませんでした。
これでElasticsreachノードにログインされない限り、OSレベルでのIndexデータの機密性は確保できました。ElasticsreachのData Nodeをi3系インスタンスで構築し、NVMe SSDにIndexを配置することで暗号化することも可能です。
NVMe インスタンスストレージのデータは、インスタンスのハードウェアモジュールに実装されている XTS-AES-256 ブロック暗号を使用して暗号化されます。暗号化キーは、ハードウェアモジュールで作成され、NVMe インスタンスストレージデバイスごとに固有です。すべての暗号化キーは、インスタンスが停止または終了して復元できないときに破棄されます。この暗号化を無効にしたり、独自の暗号キーを指定したりすることはできません。
以上、いかがでしたでしょうか。
不明な点、誤植などありましたら、コメントをお願いします!!