VyOS で WireGuardによるVPN接続

検証環境として構築した仮想ネットワークに対して、自身のローカル端末からアクセスするために、ゲートウェイとなるVyOSのルータとWireGuardによるVPN接続を構築した際のメモとなります。

設定

キーペアの生成

VyOS上でVPNサーバのキーペアを生成。今回はサーバ側、クライアント側双方のキーペアの生成をVyOS側で実施。

サーバ用キーペア

$ generate wireguard named-keypairs <KEY_NAME>

生成されたキーの確認。

$ show wireguard keypairs pubkey <KEY_NAME> 
RvTYdrnV+80IP/8uXDdWD/sXzv+1H+xurzy6ZrNXhAw=

クライアント用キーペア

$ generate wireguard named-keypairs client01

インタフェース設定

VyOS(サーバ)側

set interfaces wireguard wg0 address '172.23.255.1/24'
set interfaces wireguard wg0 peer client01 allowed-ips '172.23.255.0/24'
set interfaces wireguard wg0 peer client01 pubkey <CLIENT_PUBLIC_KEY>
set interfaces wireguard wg0 port 51820
set interfaces wireguard wg0 privatekey <KEY_NAME>
set protocols static interface-route 10.2.0.0/24 next-hop-interface wg0

クライアント側

今回はmacOSにてコマンドラインで設定。
/usr/local/etc/wireguard/wg0.conf

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 172.23.255.2

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
AllowedIPs = 172.23.0.0/16 #VPN経由とするネットワーク
EndPoint = <SERVER_ADDRESS>:51820

接続・確認

wg-quick でVPNのセッションを開始。

$ wg-quick up wg0

対向にping疎通が取れることを確認。

参考

docs.vyos.io

Photon OS でNFSサーバ構築

前ポストに引き続き、検証環境構築メモとなります。
今回はNFSサーバの構築です。

  • Install Package
# tdnf install -y nfs-utils
# mkdir /nfs
# chmod –R 777 /nfs
# echo “/nfs 172.23.0.0/24(rw)” > /etc/exports
# systemctl enable nfs-server
# systemctl start nfs-server

Photon OS でDNSサーバをDocker + CoreDNSで構築する

前ポストに引き続き、検証環境構築メモとなります。
今回は検証環境の内部向けドメインDNSサーバを構築します。
VyOSの構築メモ - passacaglia

Environment

  • PhotonOS
  • CoreDNS

add user

予めsudoグループに追加しておけば、sudersへの追加は不要。

# useradd -m -G sudo admin
# passwd admin

disable iptables

デフォルトでは 22/tcpのみポート開放されている。必要なポートのみ開放することが望ましいが、一旦停止。

# systemctl stop iptables
# systemctl disable iptables

docker

Photon OSでは、dockerが予めインストールされているが、無効化されているので、有効化して起動させる。

# systemctl enable docker
# systemctl start docker

CoreDNS

coredns/Corefile

. {
    #log
    reload
    forward . <RESOLVER>
}

<LOCAL_DOMAIN> {
    log
    file /etc/coredns/zones/<LOCAL_DOMAIN>.zone
}

BIND形式でのゾーンファイルを作成。
coredns/zones/zonefile

$ORIGIN <DOMAIN>.
@       3600 IN SOA mgmt.<DOMAIN>. admin.<DOMAIN>. (
        2020123100
        7200
        3600
        1209600
        3600
        )

        3600 IN NS mgmt.<DOMAIN>.

mgmt           IN      A       172.23.0.X

事前にCoreDNSのイメージをダウンロード。

docker pull coredns/coredns:1.7.0

今回はdocker-composeを利用してコンテナの管理を行うため、yamlファイルを用意。

version: '3'
services:
  coredns:
    image: coredns/coredns:1.7.0
    container_name: coredns
    restart: always
    network_mode: host
    command: -conf /etc/coredns/Corefile
    volumes:
      - coredns:/etc/coredns
volumes:
  coredns:
    driver_opts:
      type: none
      device: <PATH_TO_COREDNS_DIR>
      o: bind

systemd-resolvedを停止する。

systemctl stop systemd-resolved
systemctl disable systemd-resolved

corednsを起動

docker-compose up -d

設定したコンフィグで名前解決ができることを確認。

VyOSの構築メモ

仮想基盤の検証環境を構築するにあたり、仮想ネットワークのゲートウェイとしてVyOSをよく利用しています。複数の環境を構築することが多いため、定番の設定の流れを備忘録として残しておきます。

Install

isoから起動し、ディスクへインストール。

$ install image

Configuration

$ configure

Interface

初期状態ではネットワークが未設定のため、コンソールからインタフェースにIPアドレスゲートウェイを設定。

# set interface ethernet eth0 address <ADDRESS>

Default Gateway

# set protocols static route 0.0.0.0/0 next-hop <GATEWAY>

ssh

sshを有効にして、リモートからログインして以降の設定作業を可能に。

# set service ssh port <PORT>

以下、流し込み。

set system host-name <HOSTNAME>
set system name-server <NAMESERVER>
delete system ntp server
set system ntp server <NTPSERVER>
set system time-zone Asia/Tokyo

2nd interface

1stインタフェースは物理ネットワークと接続し、2ndインタフェースを仮想ネットワークとの接続としています。 複数の仮想ネットワークに対してVLANで分割して接続します。ManagementネットワークはNative VLANとして、untaggedでの利用としています。

# set interface ethernet eth1 address <ADDRESS>

SNAT

仮想ネットワーク内から物理ネットワークへ抜ける際にSNAT。

set nat source rule 1 outbound-interface eth0
set nat source rule 1 source address 172.23.0.0/16
set nat source rule 1 translation address masquerade

Option

以降、必要に応じて設定。

DNS forwarder

特定のドメインだけDNSサーバを変更したい場合。

set service dns forwarding allow-from 172.23.0.0/16
set service dns forwarding domain <DOMAIN> server <DNSSERVER>
set dns forwarding listen-address '172.23.0.1'
set dns forwarding name-server <DNSSERVER>

ESXiのインストール自動化(kickstartスクリプトの編集・ISO作成)

ESXiの構築で、何台もあるサーバーを1台ずつコンソール画面で設定するのは手間がかかってしまいます。 今回、kickstart インストールスクリプトによる作業の自動化と、それをデフォルトの動作とするためにカスタムISOの作成を行いました。

環境

  • 作業 ESXi バージョン: ESXi 7.0
  • 作業環境: Ubuntu 20.04 LTS

カスタムISOの作成

元のISOイメージのコピー

My VMware よりダウンロードしたESXiのインストールISOをマウントし、内容を編集するために任意のディレクトリへコピーします。コピーが完了すれば、元のイメージはもう利用しないのでアンマウントして大丈夫です。

# mount -o loop ./VMware-VMvisor-Installer-7.*.x86_64.iso /mnt/esxi
# cp -r /mnt/esxi /tmp/esxi
# umount /mnt/esxi

インストールスクリプト(ks.cfg)の用意

kickstart のインストールスクリプトである ks.cfg を、コピー先ディレクトリ内に用意します。 rootpw で指定するroot パスワードについては、平文でも可能ですが今回はISOに含めることもあり、暗号化して入力しました。パスワードの生成はmkpasswd コマンドで行っています。

$ mkpasswd --method=sha-512

/tmp/esxi/ks.cfg

# VMware license agreementへの同意
vmaccepteula

# root パスワードの設定
rootpw --iscrypted $6$zzw94gt90Tm$dtfuCgjBf1ambua0qxSnk/.rVVXOCM85iaJ/l384YYgMU.CZIP3OySqUQzQqaBGHqFUeNB1nfxv6W.EDzMP8n1

# 一番目のディスクへのインストールを行い、既にvmfsが作成されている場合は上書きする
install --firstdisk --overwritevmfs

# キーボードの配列設定
keyboard 'US Default'

# vmnic0のインタフェースをDHCPで自動設定する
network --bootproto=dhcp --device=vmnic0

# 完了後にリブートする
reboot

# 初回起動時の処理
%firstboot --interpreter=busybox

# SSHとShellを有効化し、起動
vim-cmd hostsvc/enable_ssh
vim-cmd hostsvc/start_ssh
vim-cmd hostsvc/enable_esx_shell
vim-cmd hostsvc/start_esx_shell

ブートパラメータの変更

起動時にこのインストールスクリプトを実行するため、boot.cfg を編集してブートパラメータの変更を行います。 BIOS環境とUEFI環境で boot.cfg の場所が異なるため、両方を修正します。

# sed -i -e "s/kernelopt=.*/kernelopt=ks=cdrom:\/KS.CFG/g" /tmp/esxi/boot.cfg
# sed -i -e "s/kernelopt=.*/kernelopt=ks=cdrom:\/KS.CFG/g" /tmp/esxi/efi/boot/boot.cfg

ks.cfg の配置と boot.cfg の編集が完了したので、改めてISOイメージ化します。

ISOの作成

# mkisofs -relaxed-filenames -J -R -b isolinux.bin -c boot.cat \
    -no-emul-boot -boot-load-size 4 -boot-info-table \
    -eltorito-alt-boot -e efiboot.img -boot-load-size 1 \
    -no-emul-boot -o custom_esxi.iso /tmp/esxi

動作確認

作成した ISOイメージをVMware Fusion で動作確認しました。BIOSでもUEFIでも起動してインストールスクリプトが実行され、記載した内容の設定が自動で行わていることを確認しています。

f:id:HichTakk:20200830142901p:plain

これで、最低限ネットワークとSSHの設定が済んだ状態で起動させることができるようになりました。SSHさえできるようになれば、後の細かい設定はコマンドラインや自動化ツールで行えるので、あまり複雑なことはせず利用しています。

参考

docs.vmware.com

Inkbird IBS-TH1 からBluetooth LEで温度・湿度情報を取得して可視化する

新型コロナウイルスの影響により、自宅で過ごす時間が長くなりました。これを機に生活環境の情報を取得・可視化しようと思い、Bluetooth対応の温湿度センサーを導入し、Linux機からPythonで収集したデータをPrometheusとGrafanaで可視化を行うことにしました。

環境

温湿度センサー / Inkbird IBS-TH1

今回、InkbirdのIBS-TH1を導入しました。Inkbird IBS-TH1シリーズはこれ以外にも液晶画面付きで単体でも利用可能なIBS-TH1 PlusやIBS-TH1 Miniがありますが、今回購入したのは標準版です。

https://www.amazon.co.jp/dp/B0774BGBHS

Bluetoothでのデータ取得

既に同様のことをされている方がいらっしゃるので、下記を参考にIBS-TH1のMACアドレス確認とgattoolでのデータ取得確認を行いました。しかし、私の環境ではhandleに0x002dを指定したところ、応答のバイト数が異なり温湿度のデータが含まれていないようでした。

qiita.com

handleに0x002dを指定した場合。7バイトの応答を期待しているところ、5バイトの応答が返ってきていました。

$ gatttool -b XX:XX:XX:XX:XX:XX --char-read --handle=0x002d
Characteristic value/descriptor: 02 2e 00 f4 ff 

他のケースを探してみると、handleに0x0028を利用している方もいて、今回はこちらでうまくいきました。

GitHub - viyh/inkbird: Script for interacting over bluetooth with an Inkbird IBS-TH1 temperature probe.

$ gatttool -b XX:XX:XX:XX:XX:XX --char-read --handle=0x0028
Characteristic value/descriptor: 0f 09 8e 19 00 18 26

1,2バイト目に温度、3,4バイト目に湿度がリトルエンディアンで格納されています。

Pythonからbluepyで温湿度を取得する

pythonからbluetoothバイスへの通信はbluepyを利用しました。取得したデータのバイト列から整数値への変換は、Python 3.2以降で利用可能な、整数型のfrom_bytesメソッドが簡単でした。

python3ならintとbytesの変換が楽勝になる - BlankTar

from bluepy.btle import Peripheral, ADDR_TYPE_PUBLIC

mac = "XX:XX:XX:XX:XX:XX"  # IBS-TH1のMACアドレス
peripheral = Peripheral(mac, addrType=ADDR_TYPE_PUBLIC)
characteristic = peripheral.readCharacteristic(0x0028)
temperature = int.from_bytes(characteristic[0:2], "little") / 100
humidity = int.from_bytes(characteristic[2:4], "little") / 100

print("Temperature: {} C".format(temperature))
print("Humidity:    {} %".format(humidity))

Prometheusへデータをエクスポート

取得できるようになったデータをPrometheusで時系列に保存し、可視化できるようにします。 prometheusの公式のクライアントライブラリであるprometheus_clientを利用し、簡易なエクスポーターを作成しています。

import time

from bluepy.btle import Peripheral, ADDR_TYPE_PUBLIC
from prometheus_client import start_http_server, Gauge
from prometheus_client import REGISTRY, PROCESS_COLLECTOR, PLATFORM_COLLECTOR, GC_COLLECTOR

# 温湿度以外のメトリクスを非表示にする
REGISTRY.unregister(PROCESS_COLLECTOR)
REGISTRY.unregister(PLATFORM_COLLECTOR)
REGISTRY.unregister(GC_COLLECTOR)

# 温湿度のメトリクスを作成
TEMPERATURE = Gauge("inkbird_temperature", "current temperature", ["device"])
HUMIDITY = Gauge("inkbird_humidity", "current humidity", ["device"])

MAC = "XX:XX:XX:XX:XX:XX" # IBS-TH1のMACアドレス


def run():
    peripheral = Peripheral(MAC, addrType=ADDR_TYPE_PUBLIC)
    characteristic = peripheral.readCharacteristic(0x0028)
    temperature = int.from_bytes(characteristic[0:2], "little") / 100
    humidity = int.from_bytes(characteristic[2:4], "little") / 100
    TEMPERATURE.labels(MAC).set(temperature)
    HUMIDITY.labels(MAC).set(humidity)


if __name__ == '__main__':
    start_http_server(8080) # サーバポートを指定してHTTPサーバを開始
    while True:
        run()
        time.sleep(60) # 60秒毎にデータ更新

実行し、ホストの8080ポートへブラウザでアクセスすると、下記のような温度と湿度の情報が表示されると思います。

# HELP inkbird_temperature current temperature
# TYPE inkbird_temperature gauge
inkbird_temperature{device="XX:XX:XX:XX:XX:XX"} 23.06
# HELP inkbird_humidity current humidity
# TYPE inkbird_humidity gauge
inkbird_humidity{device="F8:30:02:FF:45:3F"} 47.58

これをPrometheusのターゲットとして登録し、定期的なデータ取得と保存が可能となりました。

Grafanaで時系列データの可視化

あとはPrometheusのデータをGrafanaでグラフ化します。以上で温湿度情報を可視化することができました。

f:id:HichTakk:20200429181509p:plain

Linux bridge、Tapインタフェースとは

VMやコンテナ等、仮想化関連を度々扱っています。仮想ネットワーク周りでtapインタフェースの概念を確認していたのですが、以前にも同じことを調べたのを忘れていたので、備忘録として改めて整理しておきます。

基本的に以下Blog記事の簡単な訳となります。
[Linux Bridge and Virtual Networking | http://www.innervoice.in/blogs/2013/12/02/linux-bridge-virtual-networking/]
[Tap Interfaces and Linux Bridge | http://www.innervoice.in/blogs/2013/12/08/tap-interfaces-linux-bridge/]

Linux bridge

ブリッジと呼ばれているが、正確には仮想スイッチであり、2.2カーネル以降でカーネルモジュールとして提供され、brctlコマンドによって管理される。

物理ネットワークと仮想ネットワーク

ネットワーク上に流れるデータトラフィックは、物理マシン上の物理イーサネットポートによって処理される。それは仮想マシンでも同様であり、トラフィックは仮想イーサネットポートによって処理される必要がある。仮想化の目的は物理エンティティをソフトウェアでエミュレートすることであり、"仮想マシンの仮想イーサネットポートが仮想スイッチへ接続される"状態を構築することをサポートしなければならない。

スイッチポート

Linux bridgeはLinuxカーネルに組み込まれたスイッチであり、物理スイッチと同様に入出力されるトラフィックを運ぶポートやインタフェースが必要となる。Linux bridgeは物理ポートと仮想ポートのどちらもサポートしていて、ブリッジに追加することができる。仮想ポートはソフトウェアエンティティであり、イーサネットフレームをその先の処理のために仮想スイッチへ転送するために利用される。イーサネットトラフィックでは、このLinux仮想ポートをtapインタフェースと呼ぶ。tapインタフェースによるアプローチによって、Linux bridgeは仮想世界から物理世界へパケットを転送することが可能となる。

tapインタフェースはなぜ必要か

物理マシンの場合と同様、仮想マシンのネットワークポートはイーサネットフレームのみを処理できる。非仮想化環境では、物理ネットワークインタフェースはイーサネットフレームを受信し、処理する。それは受信したフレームからイーサネット関連のヘッダを取り除き、ペイロード(IPパケット)を先のOSへ向けて転送することである。しかし仮想化環境では、仮想ネットワークインタフェースはイーサネットフレームを期待しているために、物理ネットワークインタフェースで処理されたペイロードを受け渡されても正しく動作しない。tapインタフェースは特別なソフトウェアエンティティであり、Linux bridgeにイーサネットフレームをそのまま転送するように指示をする。つまり、tapインタフェースに接続された仮想マシンは生のイーサネットフレームを受信できるようになる。それによって、仮想マシンはネットワークの視点からは物理マシンであるかのように動作することが可能となる。

仮想RJ45ケーブルも同様に存在するのか

簡単に言うと無いが、仮想マシン仮想マシンイーサネットポートをLinux bridgeのtapインタフェースへ接続する必要はある。この接続はプログラム的に行われ、libvirtのようなアプリケーションがtapインタフェースの利用するファイルディスクリプタを作成する。Linux bridgeがイーサネットフレームをtapインタフェースへ送信するとき、正確にはファイルディスクリプタへバイトを送信している。QEMUのようなエミュレータでは、このファイルディスクリプタからバイトを読み込み、仮想マシンの仮想ネットワークインタフェースを通して内部のゲストOSへ渡している。