サマリー
前回の記事 Proxmox VEでvJunos-switchを動かしてみる #proxmox - Qiita では、Proxmox VEでvJunos-switchが起動できることを確認しました。
しかし、これでは新しい仮想マシンを立ち上げるたびに手動で初期設定を入れることになります。
vJunos-switchにはcloud-initのような都合のいいものは無いので、別の方法を見つけなければなりません。
今回はProxmox VEのcloud-init機能を参照したフックスクリプトで初期コンフィグドライブを生成することで、sshでログイン可能な状態でvJunos-switchを起動するように仮想マシンテンプレートをカスタマイズしてみます。
仕組み
vJunos-Switch側
vJunos-Switchのドキュメント KVM での vJunos スイッチの導入と管理 |ジュニパーネットワークス によると、以下のように書かれています。
vJunos スイッチは、構成が含まれている VM インスタンスに 2 台目のディスクを接続することで、初期構成を受け入れます。提供されたスクリプト make-config.sh を使用してディスク イメージを作成します。
つまり、2台目のディスクに初期コンフィグを入れさえすれば、初回起動時からsshでログインできる構成を作れそうです。
しかし make-config.sh とは一体何なのでしょうか?
vJunos-Switchのドキュメントには直接的な解決方法が書かれていませんでしたが、仮想化されてある程度経過している同社のvSRXのドキュメントを見るとKVMやOpenStackにおけるブートストラップISOイメージについての記載が見つかりました。
KVM を使用した vSRX 仮想ファイアウォールでの初期構成の読み込み |vSRX |ジュニパーネットワークス
Junos OSのことですから、おそらく同じ仕組みで動作すると予測されます。
今回は上記の通り mkisofs コマンドを使用して ISO イメージを作成する方法でトライしてみます。
Proxmox VE側
Proxmox VEには、仮想マシン起動前後でスクリプトを起動する Hook Scripts 機能があります。
hookscriptにはbashやperlなどのスクリプトが指定できます。おそらく、コマンド的に実行可能であれば何で書かれていても良いはずです。
これを使用して、Proxmox VEがデフォルトで持っているcloud-init設定(ユーザー名や公開鍵設定※)を読み込んでvJunos-switchに対するブートストラップISOイメージを生成することで、sshでログイン可能な状態でvJunos-switchを起動できるのではないでしょうか。
※たとえcloud-init driveを設定しなくとも、cloud-init用のユーザー名(ciuser)などはCLI経由で設定できます。
WebUIでは No CloudInit Drive found
となってしまいますが...
設定
それでは、仕組みを実現するためのファイルを準備していきます。
1. hookscript
hookscriptの動きは以下の通りです。
-
Proxmox VEの設定から以下のパラメータを抽出
Param Description HOSTNAME 仮想マシンの名前(name)をそのまま使用 USERNAME cloud-initで指定されたユーザー名(ciuser)をログインユーザー名として使用 PUBLIC_KEY cloud-initで指定された公開鍵(sshkeys)を使用。URLエンコードされているので変換を挟む FXP_ADDRESS ipconfig0のアドレスを管理ポート(fxp0)に割り当てるアドレスとして採用 FXP_GATEWAY ipconfig0のゲートウェイアを管理ポート(fxp0)が所属するMgmt-VRFのゲートウェイとして採用 -
取得した変数でテンプレートを置換して、ブートストラップISOファイルを生成する
-
起動しようとしている仮想マシンにブートストラップISOをアタッチする
Proxmox VEではhookscriptが起動する時点で仮想マシン設定は固定されており変更できません。
そのため、一時的にロックファイルを削除して仮想マシン設定を変更した後、ロックファイルを再設置する必要があります。
また、ブートストラップISOがアタッチされた後はhookscriptも不要になりますので、設定から取り除いておきます。 -
仮想マシンを再起動します
前述の 3. と同様に、Proxmox VEではhookscriptが起動する時点で仮想マシン設定は固定されています。
そのため、hookscriptでブートストラップISOをアタッチしただけでは起動時のqemuコマンドに2台目のディスクが存在しません。
そこで、hookscript設定を外して再度電源を入れ直すコマンドを3秒後に実行するようにsystemd-run
でセットしています。
今のところ問題は見つかっていませんが環境に合わせたチューニングが必要になる部分だと思われます。
📒 vjunos-hook.sh の全文はこちら
#!/bin/bash
set -eu
vmid=$1
phase=$2
storage="ceph_datastore"
if [[ "$phase" == "pre-start" ]]; then
echo "=== Generate vJunosSwitch bootstrap ISO ==="
export HOSTNAME=$(qm config $vmid | grep -o "name: .*" | cut -d " " -f2-)
export USERNAME=$(qm config $vmid | grep -o "ciuser: .*" | cut -d " " -f2-)
PUBLIC_KEY_ENC=$(qm config $vmid | grep -o "sshkeys: .*" | cut -d " " -f2-)
export PUBLIC_KEY=$(printf '%b' "${PUBLIC_KEY_ENC//%/\\x}")
export FXP_ADDRESS=$(qm config $vmid | grep 'ipconfig0:' | grep -oE 'ip=([0-9]+\.){3}[0-9]+/[0-9]+' | sed 's/ip=//')
export FXP_GATEWAY=$(qm config $vmid | grep 'ipconfig0:' | grep -oE 'gw=([0-9]+\.){3}[0-9]+' | sed 's/gw=//')
echo "=== Parameters ==="
echo "HOSTNAME: ${HOSTNAME}"
echo "USERNAME: ${USERNAME}"
echo "PUBLIC_KEY: ${PUBLIC_KEY}"
echo "FXP_ADDRESS: ${FXP_ADDRESS}"
echo "FXP_GATEWAY: ${FXP_GATEWAY}"
if [[ -z "$HOSTNAME" || -z "$USERNAME" || -z "$PUBLIC_KEY" || -z "$FXP_ADDRESS" || -z "$FXP_GATEWAY" ]]; then
echo "One or more parameters are empty. Exiting..."
exit 1
fi
echo
echo "=== Configuration ==="
tmppath=$(mktemp -d)
cat /mnt/pve/cephfs/snippets/vjunos_template.conf | envsubst | tee $tmppath/juniper.conf
tar -zcvf $tmppath/juniper.conf.tgz $tmppath/juniper.conf
rm $tmppath/juniper.conf
mkisofs -l -o vjunos_${vmid}.iso $tmppath
echo "=== Attach disk force (remove lock file tempolary) ==="
# NOTE: https://git.geco-it.net/GECO-IT-PUBLIC/fedora-coreos-proxmox/src/branch/master/hook-fcos.sh
# Can not set config locked.
rm -f /var/lock/qemu-server/lock-${vmid}.conf
qm importdisk ${vmid} vjunos_${vmid}.iso ${storage}
qm set ${vmid} --virtio1 ${storage}:vm-${vmid}-disk-1,iothread=1
qm set ${vmid} --delete hookscript
touch /var/lock/qemu-server/lock-${vmid}.conf
rm vjunos_${vmid}.iso
rm -r $tmppath
# hack for boot with new attached device
systemd-run --on-active=3 bash -c "qm stop ${vmid} && qm wait ${vmid} && qm start ${vmid}"
echo "done!"
fi
2. bootstrap config template
テンプレートについては、前回記事 Proxmox VEでvJunos-switchを動かしてみる #proxmox - Qiita で起動した後の設定を抜き出してきて、一部を変数で置き換えたものです。
また、ついでにfxp0インターフェースを管理用VRFに所属させるようにしています。
これについては説明範囲外なので デフォルト以外のインスタンスの管理インターフェイス |Junos OS |ジュニパーネットワークス を参照ください。
root-authentication -> encrypted-password については固定で準備しました。
現在のバージョン(23.1R1.8)にあわせるなら、sha-512にソルト付きで生成すると丁度いいでしょう。
$ mkpasswd -m sha-512 qiita-passowrd 99999999
$6$99999999$X4Cdz5u1wKdxEh0sE3SX5q4irCIxvAebu02dYIO6Q3FeUPaLJZA2kCB/w9lV0bU6tL7wiD14TO8glvu/HztAK0
左記のhookscriptで自動生成しても良いかもしれませんね。
📒 vjunos_template.conf の全文はこちら
system {
host-name ${HOSTNAME};
root-authentication {
encrypted-password "$6$99999999$/Eea5D4QA7wqx.NPg0WD/VzHc6y0Htokd9DcEZUWAdOffUwleA4WzdDyBe0D68whleSgU7tL13KE1Qoa72O6x."; ## SECRET-DATA
}
login {
user ${USERNAME} {
uid 2000;
class super-user;
authentication {
ssh-rsa "${PUBLIC_KEY}"; ## SECRET-DATA
}
}
}
services {
ssh;
netconf {
ssh {
port 830;
}
}
}
arp {
aging-timer 5;
}
management-instance;
syslog {
file interactive-commands {
interactive-commands any;
}
file messages {
any notice;
authorization info;
}
}
}
interfaces {
fxp0 {
unit 0 {
family inet {
address ${FXP_ADDRESS};
}
}
}
}
multi-chassis {
mc-lag {
consistency-check;
}
}
routing-instances {
mgmt_junos {
routing-options {
static {
route 10.0.0.0/8 next-hop ${FXP_GATEWAY};
route 172.16.0.0/12 next-hop ${FXP_GATEWAY};
route 192.168.0.0/16 next-hop ${FXP_GATEWAY};
}
}
description ManagementInstance;
}
}
protocols {
lldp {
interface all;
}
lldp-med {
interface all;
}
}
3. テンプレートへの適用
それでは、hookscriptに実行権限を付与してvJunos-Switchの仮想マシンテンプレートに設定します。
chmod +x /mnt/pve/cephfs/snippets/vjunos_hook.sh
qm set 9051 -hookscript=cephfs:snippets/vjunos_hook.sh
前回作成したvJunos-Switchと同様の設定の仮想マシンテンプレート(9051)が作成されているものとします(手順外)
起動確認
それでは、作成したテンプレートを元に仮想マシンを作成して起動してみましょう。
# qm clone 9051 1101 --name vjunos-sample --full --storage local-lvm
# qm set 1101 --ipconfig0 ip=192.168.0.11/24,gw=192.168.0.1
# qm set 1101 --ciuser qiita --sshkeys sample01-rsakey.pub
# qm start 1101
起動するとhookscriptの出力が出てきます。
=== Generate vJunosSwitch bootstrap ISO ===
=== Parameters ===
HOSTNAME: vjunos-sample
USERNAME: qiita
PUBLIC_KEY: ssh-rsa ...
hookscriptでセットされたコマンドに従って、仮想マシンは停止した後に再度起動します。
今度は qm terminal
コマンドで起動時のログを確認します。
パッケージのマウントが一通り完了した後に /dev/vtbd1
をマウントして /mnt/juniper_conf.tgz
を見つけてくれたら、先ほど用意したブートストラップコンフィグが読み込まれているはずです。
# qm terminal 1101
...
Verified dsa-x86-64-20230319 signed by PackageProductionECP256_2023 method ECDSA256+SHA256
Mounting dsa-x86-64-20230319.041703_builder_junos_231_r1
@ 1705123641 [2024-01-13 05:27:21 UTC] mount done
Removing /etc/malloc.conf
Sat Jan 13 05:27:22 UTC 2024 vmguest: setup ...
Sat Jan 13 05:27:22 UTC 2024 vmguest: mounted /dev/vtbd1
Sat Jan 13 05:27:22 UTC 2024 vmguest: found /mnt/juniper_conf.tgz
vm_retype=""RE-VMX""
...
最後に、sshでログインできることを確認して完了です。
> ssh qiita@192.168.0.11
Last login: Sat Jan 13 05:51:48 2024 from 192.168.0.101
--- JUNOS 23.1R1.8 Kernel 64-bit JNPR-12.1-20230307.3e7c4b6_buil
qiita@vjunos-sample>
SSHログイン可能な設定が入った状態でvJunos-Switchが起動できました。
終わり
これで沢山のvJunos-Switchを準備することになっても、初期設定の面倒さから逃れることが出来ました。
Proxmox VEのhookscriptは自由度があるように見えるものの、思いのほか使いどころが限られますね。
複数台の仮想NW装置を用意できると、設定用のコントローラー開発や管理系ソフトウェアの検証などに大いに役立ちます。
最近はBGPの拡張も目覚ましいところですので、相互接続検証や調査に役立てていきましょう。