今回はUbuntuからYAMAHA RTX830に設定したIKEv2に接続する方法を紹介します。
IKEv2と現在のVPN事情
今日日、VPN接続自体があまり使われなくなりました。そもそもインターネット接続がIPv6主体になったこと、VPN自体があまりセキュアとは認められなくなったのが主な原因です。
しかし、現実的にゼロトラストを実現するのは難しく、可能な限りセキュアな方法でVPNを実現したくなります。そのうちの1つはIKEv2によるVPNです。従来より使われてきていたL2TP/
筆者が所有するYAMAHA RTX830では、2022年12月26日にリリースされたRev.
UbuntuからIKEv2に接続するにはstrongSwanのNetwork Managerプラグインを使用します。Ubuntuではnetwork-manager-strongswanというパッケージ名です。
とはいえUbuntuのリポジトリにあるnetwork-manager-strongswanとそのバックエンドであるstrongswanでは接続できるようにならず、この問題を生成AIによって解決しました。
動作確認はRTX830で行っていますが、ほかのYAMAHAのルーターでも接続できると期待しています。
Ubuntuのバージョンは26.
インストールと設定
解説はあとにして、まずはインストールと設定を実施します。次のコマンドを実行してください。
$ sudo add-apt-repository ppa:ikuya-fruitsbasket/strongswan $ sudo apt install network-manager-strongswan
「設定」
「IPsec/
続けて
以下のように設定します。
- 「名前」
は任意に入力します。 - 「Address」
はYAMAHAルーターのホスト名を入力します。通常はネットボランチDNSサービスで割り当てられたホスト名になることでしょう。 - Serverの
「Identity」 は 「Address」 と同じにします。 - 「Authentication」
は 「Pre-shared key」 にします。 - Clientの
「Identity」 はYAMAHAのルーターに設定した 「クライアントのID」 です。 - 「Password」
はYAMAHAのルーターに設定した 「認証鍵」 です。 - 「Options」
は、 「Request an inner IP address」 と 「Enforce UDP encapsulation」 と 「Mobike」 にチェックを入れます。 - 「Send Certificate request」
のチェックを外します。 - 「Server port」
は 「4500」 を入力します。
「適用」
変更点
これから提示する差分はUbuntu 26.
認証鍵の文字数
strongSwanの認証鍵の文字数は20文字以上である必要がありますが、これはちょっと長すぎるということで10文字以上にするパッチを作成しました。これは生成AIを使用せず、自力で作成しています。
Index: network-manager-strongswan-1.6.4/auth-dialog/main.c
===================================================================
--- network-manager-strongswan-1.6.4.orig/auth-dialog/main.c
+++ network-manager-strongswan-1.6.4/auth-dialog/main.c
@@ -139,9 +139,9 @@ static gboolean get_secrets(const char *
}
else if (!strcmp(type, "psk"))
{
- prompt = g_strdup_printf (_("Pre-shared key required to establish VPN connection '%s' (min. 20 characters)."),
+ prompt = g_strdup_printf (_("Pre-shared key required to establish VPN connection '%s' (min. 10 characters)."),
name);
- minlen = 20;
+ minlen = 10;
}
else /* certificate auth of some kind */
{
Index: network-manager-strongswan-1.6.4/properties/nm-strongswan-dialog.ui
===================================================================
--- network-manager-strongswan-1.6.4.orig/properties/nm-strongswan-dialog.ui
+++ network-manager-strongswan-1.6.4/properties/nm-strongswan-dialog.ui
@@ -439,7 +439,7 @@
<object class="GtkEntry" id="passwd-entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
- <property name="tooltip-text" translatable="yes">The password to use for authentication against the server (min. 20 characters for PSKs).</property>
+ <property name="tooltip-text" translatable="yes">The password to use for authentication against the server (min. 10 characters for PSKs).</property>
<property name="hexpand">True</property>
<property name="visibility">False</property>
<property name="placeholder-text" translatable="yes">(Use icon to change password storage policy)</property>
Index: network-manager-strongswan-1.6.4/properties/nm-strongswan.c
===================================================================
--- network-manager-strongswan-1.6.4.orig/properties/nm-strongswan.c
+++ network-manager-strongswan-1.6.4/properties/nm-strongswan.c
@@ -128,7 +128,7 @@ check_validity (StrongswanPluginUiWidget
case NM_SETTING_SECRET_FLAG_NONE:
case NM_SETTING_SECRET_FLAG_AGENT_OWNED:
str = (char *) gtk_editable_get_text (GTK_EDITABLE (widget));
- if (str && strlen (str) < 20) {
+ if (str && strlen (str) < 10) {
g_set_error (error,
STRONGSWAN_PLUGIN_UI_ERROR,
STRONGSWAN_PLUGIN_UI_ERROR_INVALID_PROPERTY,
Index: strongswan-6.0.4/src/charon-nm/nm/nm_service.c
===================================================================
--- strongswan-6.0.4.orig/src/charon-nm/nm/nm_service.c
+++ strongswan-6.0.4/src/charon-nm/nm/nm_service.c
@@ -767,7 +767,7 @@ static bool add_auth_cfg_pw(NMStrongswan
str = nm_setting_vpn_get_secret(vpn, "password");
if (streq(method, "psk"))
{
- if (strlen(str) < 20)
+ if (strlen(str) < 10)
{
g_set_error(err, NM_VPN_PLUGIN_ERROR,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
認証用パケットを小さく
接続すると即座に切れてしまうため、Qwen 3.
ここではnetwork-manager-strongswan-1.
Index: network-manager-strongswan-1.6.2/properties/nm-strongswan-dialog.ui
===================================================================
--- network-manager-strongswan-1.6.2.orig/properties/nm-strongswan-dialog.ui
+++ network-manager-strongswan-1.6.2/properties/nm-strongswan-dialog.ui
@@ -569,6 +569,23 @@
</packing>
</child>
<child>
+ <object class="GtkCheckButton" id="send-certreq-check">
+ <property name="label" translatable="yes">Send certificate _request</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="tooltip-text" translatable="yes">Send certificate request payloads to offer trusted root CA certificates to the peer. Uncheck this if too many trusted root CAs are installed, as each certreq increases the size of initial IKE packets.</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
<!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid">
<property name="visible">True</property>
@@ -941,4 +958,4 @@
<action-widget response="-3">userkey-chooser-accept</action-widget>
</action-widgets>
</object>
-</interface>
+</interface>
\ No newline at end of file
Index: network-manager-strongswan-1.6.2/properties/nm-strongswan.c
===================================================================
--- network-manager-strongswan-1.6.2.orig/properties/nm-strongswan.c
+++ network-manager-strongswan-1.6.2/properties/nm-strongswan.c
@@ -516,6 +516,18 @@ init_plugin_ui (StrongswanPluginUiWidget
}
g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (settings_changed_cb), self);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "send-certreq-check"));
+ value = nm_setting_vpn_get_data_item (settings, "send-certreq");
+ if (!value || strcmp(value, "yes") == 0)
+ {
+ gtk_check_button_set_active(GTK_CHECK_BUTTON(widget), TRUE);
+ }
+ else if (strcmp(value, "no") == 0)
+ {
+ gtk_check_button_set_active(GTK_CHECK_BUTTON(widget), FALSE);
+ }
+ g_signal_connect (G_OBJECT (widget), "toggled", G_CALLBACK (settings_changed_cb), self);
+
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "proposal-check"));
value = nm_setting_vpn_get_data_item(settings, "proposal");
if (value && strcmp(value, "yes") == 0)
@@ -732,6 +744,10 @@ update_connection (NMVpnEditor *iface,
active = gtk_check_button_get_active(GTK_CHECK_BUTTON(widget));
nm_setting_vpn_add_data_item (settings, "ipcomp", active ? "yes" : "no");
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "send-certreq-check"));
+ active = gtk_check_button_get_active(GTK_CHECK_BUTTON(widget));
+ nm_setting_vpn_add_data_item (settings, "send-certreq", active ? "yes" : "no");
+
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "proposal-check"));
active = gtk_check_button_get_active(GTK_CHECK_BUTTON(widget));
nm_setting_vpn_add_data_item (settings, "proposal", active ? "yes" : "no");
@@ -840,4 +856,4 @@ strongswan_editor_new (NMConnection *con
}
return object;
-}
+}
\ No newline at end of file
Index: strongswan-6.0.4/src/charon-nm/nm/nm_service.c
===================================================================
--- strongswan-6.0.4.orig/src/charon-nm/nm/nm_service.c
+++ strongswan-6.0.4/src/charon-nm/nm/nm_service.c
@@ -897,6 +897,12 @@ static gboolean connect_(NMVpnServicePlu
NM_TYPE_SETTING_CONNECTION));
vpn = NM_SETTING_VPN(nm_connection_get_setting(connection,
NM_TYPE_SETTING_VPN));
+
+ str = nm_setting_vpn_get_data_item(vpn, "send-certreq");
+ if (str && streq(str, "no"))
+ {
+ ike.no_certreq = TRUE;
+ }
free(priv->name);
priv->name = strdup(nm_setting_connection_get_id(conn));
DBG1(DBG_CFG, "received initiate for NetworkManager connection %s",
@@ -1398,4 +1404,4 @@ NMStrongswanPlugin *nm_strongswan_plugin
}
return plugin;
-}
+}
\ No newline at end of file
ポート4500を使用するように
これで接続はできるようになりましたが、ルーターのローカルIPアドレス
こちらはQwen 3.
Index: strongswan-6.0.4/src/charon-nm/nm/nm_service.c
===================================================================
--- strongswan-6.0.4.orig/src/charon-nm/nm/nm_service.c
+++ strongswan-6.0.4/src/charon-nm/nm/nm_service.c
@@ -18,6 +18,8 @@
#include <fcntl.h>
#include <inttypes.h>
#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
#include "nm_service.h"
@@ -932,6 +934,11 @@ static gboolean connect_(NMVpnServicePlu
ike.force_encap = streq(str, "yes");
str = nm_setting_vpn_get_data_item(vpn, "ipcomp");
child.options |= streq(str, "yes") ? OPT_IPCOMP : 0;
+ str = nm_setting_vpn_get_data_item(vpn, "mobike");
+ if (str && streq(str, "no"))
+ {
+ peer.options |= OPT_NO_MOBIKE;
+ }
/**
* Register credentials
Index: strongswan-6.0.4/src/libcharon/sa/ike_sa.c
===================================================================
--- strongswan-6.0.4.orig/src/libcharon/sa/ike_sa.c
+++ strongswan-6.0.4/src/libcharon/sa/ike_sa.c
@@ -1221,7 +1221,25 @@ METHOD(ike_sa_t, update_hosts, void,
(!has_condition(this, COND_NAT_HERE) ||
!has_condition(this, COND_ORIGINAL_INITIATOR)))
{
- new_other = other;
+ if (other->ip_equals(other, this->other_host) &&
+ other->get_port(other) == IKEV2_UDP_PORT &&
+ this->other_host->get_port(this->other_host) ==
+ IKEV2_NATT_PORT)
+ { /* ignore a remote endpoint announced on the default IKE
+ * port while we use the NAT-T port. some peers (e.g.
+ * certain YAMAHA routers) send their periodic keepalive
+ * from port 500 although the IKE_SA is registered on
+ * the NAT-T port. applying that endpoint would switch
+ * the UDP-encap port of the child SAs to 500, where
+ * the peer does not process ESP-in-UDP, breaking the
+ * tunnel */
+ DBG1(DBG_IKE, "ignoring remote endpoint %H, keeping %H",
+ other, this->other_host);
+ }
+ else
+ {
+ new_other = other;
+ }
}
}
}
@@ -3369,4 +3387,4 @@ linked_list_t *ike_sa_get_dynamic_hosts(
}
}
return list;
-}
+}
\ No newline at end of file
Index: strongswan-6.0.4/src/charon-nm/charon-nm.c
===================================================================
--- strongswan-6.0.4.orig/src/charon-nm/charon-nm.c
+++ strongswan-6.0.4/src/charon-nm/charon-nm.c
@@ -194,6 +194,12 @@ int main(int argc, char *argv[])
/* default to random ports to avoid conflicts with regular charon */
lib->settings->set_default_str(lib->settings, "charon-nm.port", "0");
lib->settings->set_default_str(lib->settings, "charon-nm.port_nat_t", "0");
+ /* never reuse an existing IKE_SA for a new connection. If the previous
+ * connection was deleted only moments ago (quick disconnect/reconnect),
+ * the IKE_SA is still locked while being destroyed and initiating on it
+ * would deadlock until the lock is released by the thread waiting for our
+ * plugin (3 min timeout). A fresh IKE_SA per connection avoids that. */
+ lib->settings->set_default_str(lib->settings, "charon-nm.reuse_ikesa", "no");
/* install VIPs on lo as NM might modify the physical interface (this seems
* to affect IPv6 in particular), it actually installs the VIPs on the
@@ -289,4 +295,3 @@ deinit:
library_deinit();
return status;
}
-
まとめ
さらにもう1つパッチを追加していますが、そちらは今回の問題とは直接関係しないので省略します。
UbuntuからRTX830で設定したIKEv2に接続できるようにするのはおそらく世界初の試みで、かといって筆者はstrongSwanに手を加えるような知識はないので、生成AIの力を借りて解決しました。
今回使用したGPUはSAPPHIRE AMD Radeon AI PRO R9700 32GB GDDR6 LITEであってなかなか手の出しづらい価格ではあるものの、ローカルLLMでもここまではできるという証明になりましたし、正直なところフロンティアモデルであればもっと早く簡単に解決していたと予想しています。
自分の手の届かないところを掻くための猫の手としての生成AIに可能性を感じました。