たぶん期待通りの動作になりました。
要は、IPaddr2に渡すパラメータを調整すれば良いわけで、いろいろ見て、clone-max=1 と言うパラメータを、crm 設定ファイルに追加すれば良さそうと。で

group g-storage dlm lvmlockd
group vip-group vip
clone cl-storage g-storage \
meta interleave=true target-role=Started
clone vip-clone vip-group \
meta clone-max=1

のように書き換えたところ、たぶん期待通りの動作になりました。ノードを standby 状態にすると、IP アドレスが相手側に移動します。
なので、IPaddr2 の動作としては問題ないし、heartbeat としての動作も問題ないけれど、パラメータの設定がミソ、という感じです。この辺 SLES のドキュメントには無いので、もう少し追記してもらえればありがたかったかなと。

IP_CIP が yes になる条件が分かりました。
ソースを見ると、IPaddr2 の ip_init() 関数の中で、

    if [ "$IP_INC_GLOBAL" -gt 1 ] && ! ocf_is_true "$OCF_RESKEY_unique_clone_address"; then
            IP_CIP="yes"
            IP_CIP_HASH="${OCF_RESKEY_clusterip_hash}"

という行があるのですね。IP_CIP を yes にしているのはここだけです。で、IP_CIP を yes にする条件は、IP_INC_GLOBAL が2以上の時(1より大きいとき)、で、IP_INC_GLOBAL を設定しているのは、やはり ip_init() の始まりの方で、

    IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1}
    IP_INC_NO=`expr ${OCF_RESKEY_CRM_meta_clone:-0} + 1`

という行の所。で、OCF_RESKEY_CRM_meta_clone_max を設定しているのは IPaddr2 内ではなくて、これを起動する heartbeat。情報は https://clusterlabs.org/projects/pacemaker/doc/2.1/Pacemaker_Administration/html/agents.html にあります。あとhttps://blog.drbd.jp/2017/12/advance_resource/ も参考になります。どうやら、OCF_RESKEY_CRM_meta_clone_max が2に設定されているみたいです。なので、iptables を動かすロジックに進んでしまったみたい。

openSUSE で動かないので、では、本家本元の Red Hat Enterprise Linux ではどうなのか、を調べてみました。RHEL では IPaddr2 からのメッセージは ocf_log という関数を使っているようで、add_interface() 関数内にある ocf_log に少し細工をして、実行コマンドと引数が表示されるようにしてみました。すると、

4月 04 21:38:17 rh9-a IPaddr2(ClusterIP)[467709]: INFO: Adding inet address 192.168.3.179/24 with broadcast address 192.168.3.255 to device ens18 ip -f inet addr add 192.168.3.179/24 brd 192.168.3.255 dev ens18
4月 04 21:38:17 rh9-a avahi-daemon[749]: Registering new address record for 192.168.3.179 on ens18.IPv4.
4月 04 21:38:17 rh9-a IPaddr2(ClusterIP)[467715]: INFO: Bringing device ens18 up ip link set ens18 up

と表示されていたんですね。つまり、RHEL では ip コマンドを使って IP アドレスを設定していると。
おそらく、どこかの引数で、ip コマンドを指定していると思うのですが、そこが分かれば RHEL と同じように openSUSE でも設定すれば、うまくいきそうです。

2025/4/5 追記
で、IPaddr2 にあっちこっち logger 入れて調べてみたら、

Apr 05 20:59:41 clvm-a root[7827]: ip_served cur_nic=,IP_CIP=yes,OCF_RESKEY_ip=192.168.3.169
Apr 05 20:59:41 clvm-a root[7828]: ip_saved return no
Apr 05 20:59:41 clvm-a root[7829]: ip_start: IP_CIP=yes,IP_STATUS=no
Apr 05 20:59:41 clvm-a root[7830]: IP_CIP=yes,ip_status=noq
Apr 05 20:59:41 clvm-a root[7833]: iptables-legacy -I INPUT -d 192.168.3.169 -i eth0 -j CLUSTERIP –new –

と言うトレース結果が。あれ、IP_CIP は IP アドレスじゃなくて yes が入っている! RHEL だとここには何も入らないか、IPアドレスのどちらか。なので、IP_CIP に yes が入ることがおかしい。でソースを読むと、IP_CIP が 空 じゃなくて、 ip_status が no だとすると、iptables を呼び出す模様。どうやら IP_CIP が yes なので誤動作した感じ。

openSUSE でSLES のマニュアルを見ながら、クラスタのテストをしています。clvm 環境ができたので、今度は仮想IP のテストをしようとしたらハマりました。

仮想IP の定義後、ノードを再起動すると、syslog に

Mar 23 12:28:31 clvm-a IPaddr2(vip)[3495]: ERROR: iptables failed
Mar 23 12:28:31 clvm-a pacemaker-execd[3148]: notice: vip start (call 25, PID 3394) exited with status 1 (iptables failed) (execution time 73ms)
Mar 23 12:28:31 clvm-a pacemaker-controld[3151]: notice: Result of start operation for vip on clvm-a: error (iptables failed)
Mar 23 12:28:31 clvm-a pacemaker-controld[3151]: notice: vip_start_0@clvm-a output [ iptables v1.8.7 (legacy): chain name not allowed to start with -'\n\nTryiptables -h’ or ‘iptables –help’ for more information.\nocf-exit-reason:iptables failed\n ]

調べて見たところ、crm から呼び出している IPaddr2モジュールの中で、iptables を呼び出し、それがエラーを出していました。どうも、openSUSE 15.6 にインストールされている iptables バージョン1.8.7 ではクラスタ関係の機能が無いのですね。それで動作しなかったという次第。

さて、どう対処したものか。

Proxmox VE を使い、openSUSE (だけじゃなくて、たぶん他のLinuxディストリビューションも)を動かし、ネットデバイスを再作成すると、OSから見たデバイス名が変わることがあります。たとえば今まで eth0 だったものが eth1 になるなどです。どうやら、MAC アドレスが変わると別デバイスと認識して、デバイス名を割り当て直すようです。これは、 /etc/udev/rules.d/70-persistent-net.rules を観て気がつきました。

対応としては、70-persistent-net.rules を観て、Proxmox VE で、ネットデバイスの MAC アドレスを eth0 として認識しているデバイスのものに変えてあげればうまくいきます。

この辺も参考になります。
https://syu-m-5151.hatenablog.com/entry/2017/11/03/162656

これまで自宅用の VPN はルーターの機能の L2TP/IPSec を使っていました。しかしながら、Android 12から L2TP/IPSec が使えなくなったり、もともと接続元のネットワーク環境次第で接続できないという制約もあり、VPN を WireGuard に乗り換えることにしました。

WireGuard は Linux カーネルに組み込まれている VPN プロトコルで、特にセキュリティとパフォーマンスを売りにしています。

構成はこんな感じです。VPN 接続用の IP アドレスは固定です。

  • 自宅側
    • ルーター
      • ヤマハのダイナミックDNSサービスで IP アドレスを引ける
    • VPNサーバー(ファイルサーバー)
      • openSUSE Leap 15.5
      • LAN側アドレス (eth1): 192.168.10.2(インターネットには直接つながってない)
      • VPN側アドレス: 192.168.2.1
      • ポート: 51820
  • リモート側
    • Android 14 スマートフォン
      • VPN側アドレス: 192.168.2.2
    • Android 13 タブレット
      • VPN側アドレス: 192.168.2.3
    • openSUSE Leap 15.6 ノートPC
      • VPN側アドレス: 192.168.2.4

秘密鍵・公開鍵を作成する

VPN に参加するサーバーとクライアントそれぞれの秘密鍵・公開鍵を作ります。openSUSE が動いている PC で以下のコマンドでそれぞれ作成します。

wg genkey | tee privatekey | wg pubkey > pubkey

privatekey と pubkey に秘密鍵と公開鍵それぞれが保存されます。合計3回実行することになります。

VPNサーバー側の設定

今回の説明の範囲外ですが、ルーターでポートフォワーディングの設定をしておきましょう。必要なポートはUDP 51820です。192.168.10.2 に転送するようにしておきます。

まずは VPN サーバー自体のネットワーク設定です。WireGuard そのものにはプロトコル上、サーバーとクライアントという関係はなく、どちらもサーバーでありクライアントなシンプルな構成です。後は WireGuard をどのような用途で使うかによって設定が変わってくるため、WireGuard の説明を見るときに、用途を意識してドキュメントを見ないと混乱するので注意が必要です。

今回は自宅で常時起動しているファイルサーバーを VPN サーバーの役割を持たせて、自宅のネットワークにつなぎたい端末からこのサーバーに繋いで使用することにします。ここからはいつもの Arch Wiki に感謝ですが、「特定のユースケース: VPN サーバー」 を見ることで、この用途の設定方法がわかります。

はじめに、転送とファイアウォールの設定です。YaST でやってしまいましょう。

  • YaST > 「ネットワーク設定」 > 「ルーティング」を開き、「IPv4 転送を有効にする」をON
  • YaST > 「ファイアウォール」 で使用しているゾーンで「ポート」 > 「UDP ポート」 に 51820 を追加

次に、WireGuard の設定をしていきます。wg コマンドで設定する方法もあるのですが、設定ファイルを直接作成するのが手っ取り早いです。

/etc/wireguard/wg0.conf

[Interface]
PrivateKey = VPNサーバーの秘密鍵
Address = 192.168.2.1/24
ListenPort = 51820
MTU = 1420

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth1 -j MASQUERADE

[Peer]
PublicKey = Android 14 スマートフォンの公開鍵
AllowedIPs = 192.168.2.2/32

[Peer]
PublicKey = Android 13 タブレットの公開鍵
AllowedIPs = 192.168.2.3/32

[Peer]
PublicKey = openSUSE Leap 15.6 ノートPCの公開鍵
AllowedIPs = 192.168.2.4/32

この設定から、VPN 用のインタフェース wg0 を作成します。Systemd を使用して、起動時に WireGuard インタフェースを立ち上げる便利スクリプトの wg-quick を使います。

systemctl enable --now wg-quick@wg0

クライアントの設定―openSUSE Leap 15.6

NetworkManager の接続の追加に WireGuard があり、これで接続できます。入力する必要の値は

  • openSUSE Leap 15.6 ノートPC 用の秘密鍵
  • ピア(接続相手: つまり、この場合サーバー)の設定
    • VPNサーバーの公開鍵
    • Allowed IPs (VPN を通して通信するアドレスレンジ)
      • LANとVPNのアドレスレンジ: 192.168.10.0/24, 192.168.2.0/24
      • 自宅LAN経由でインターネットに出たい場合は 0.0.0.0/0 を指定して、全部を VPN 経由にすることも可能

クライアントの設定―Android スマートフォン

Wireguard の接続アプリは Play Store から入手できます。NetworkManager と同様に手入力でもよいのですが、以下のようなファイルを PC 上で作成して QR コード化して送ることもできます。

[Interface]
Address = 192.168.2.3/24
PrivateKey = Android端末の秘密鍵
DNS = 192.168.10.1
MTU = 1420

[Peer]
PublicKey = VPNサーバーの公開鍵
AllowedIPs = 192.168.10.0/24, 192.168.2.0/24
Endpoint = 外からアクセスできるルーターのホスト名:51820

ファイル名を android-phone.conf とすると、QR コードは次のようにして作成できます。

qrencode -t ansiutf8  < android-phone.conf

qr コマンドでも同じことができます。

だいぶ前に秋葉原の中古携帯ショップで買った NEC のモバイルルーター、Aterm MR04LN。USB テザリング機能があるのですが、接続してもネットワークデバイスとして認識してくれません。

必要なカーネルモジュール (rndis_host) が自動で読み込まれていないようなので、以下のコマンドで手動で読み込み解決しました。

sudo modprobe rndis_host