strongSwan を使って iOS(もちろん macOS も)対応の VPN を作成します。strongSwan 5.8.0 で設定方法が大きく変わりましたので、新しい方(charon-systemd)で設定します。残念ながら、執筆時点で最新バージョンの strongSwan 5.9.10 は buggy なため、この記事の設定例では、ローカルで rpmbuild した strongswan-5.9.11-dr3.x86_64.rpm を使用しています。5.9.11 の正式リリースを待つか、自力でビルドしましょう。

全体像

モバイル端末から接続可能な VPN を作成する、リモートアクセススタイルです。

以下のような設定の VPS(IP アドレスは仮に 12.34.56.78 とします)に VPN サーバを建てます。VPN 用のプライベートネットワークとして、192.168.2.0/24 を作成します。それぞれ、firewalld のゾーンとして、public と trusted を割り当てます。VPS は 12.34.56.78 と 192.168.2.1 の 2つのアドレスを持ちます。

  • eth0(public ゾーン, 12.34.56.78, 1234::5678)
  • eth1(trusted ゾーン, 192.168.2.1, fd00:1234:5678::192:168:2:1)

ユニークローカルアドレスは RFC 4193 で規定された方式で、各自ランダムな値を生成してください。

モバイル端末からの通信は、全通信が VPN を経由する(外部サーバからは 12.34.56.78 からの通信に見える)方式か、trusted ゾーンの 192.168.2.0/24 宛の通信のみが VPN を経由する方式か、いずれかお好みの方法を選択できます。

前提

前提として、SSLサーバ証明書は取得済みとします。パスワード認証の場合は、Let's Encrypt のものでも OK です。クライアント証明書で認証する場合は、自前の認証局を建てて、サーバ証明書も発行しましょう。別の記事(strongSwan 用の証明書を作成する)も参照ください。

Let's Encryptでは、--key-type オプションで RSA と ECDSA から cipher を選べるようになりました(デフォルトは ECDSA ですが、一度 RSA で取得したものは更新しても RSA のままなので、ECDSA を使いたい場合は手動で取り直す必要があります)。

インストール

説明する間でもないですが、例によってこうです。

# dnf install strongswan strongswan-sqlite tpm2-abrmd

strongswan-sqlite は入れておかないと、swanctl コマンドで一部のオプションを指定した時にエラーになります。VPN サーバとして使うだけなら must ではないです。VPS には TPM(Trusted Platform Module)はないのですが、strongSwan が TPM を利用しようとするので、stub として tpm2-abrmd を入れておきます。

strongSwan の設定

/etc/strongswan/swanctl/conf.d/my-vpn-ikev2.conf を以下のように作成します(ファイル名は何でもいいです)。

一応、IPv6 対応です。

VPN にアクセスしたクライアントには、192.168.2.32-192.168.2.127 と fd00:1234:5678::192:168:2:32-fd00:1234:5678::192:168:2:127 の仮想アドレスを振ります。

DNS は VPN 用のローカル DNS サーバとして 192.168.2.1(fd00:1234:5678::192:168:2:1)と、グローバルな DNS サーバとして Google Public DNS を指定しています。適宜変更してください。

設定の一覧の詳細は、公式のドキュメントを参照してください。デフォルトで問題ない部分は触っていません。

/etc/strongswan/swanctl/ 以下にサーバ証明書を配置します。秘密鍵と公開鍵の両方が必要です。ここでは、Let's Encrtypt の証明書を使用する場合の配置例を挙げます。以下のように、シンボリックリンクを作るのが良いでしょう。

# tree /etc/strongswan/swanctl/
.
├── bliss
├── conf.d
│   └── my-vpn-ikev2.conf
├── ecdsa
├── pkcs12
├── pkcs8
├── private
│   └── example.com.key.pem -> /etc/letsencrypt/live/example.com/privkey.pem
├── pubkey
├── rsa
├── swanctl.conf
├── x509
│   └── example.com.cert.pem -> /etc/letsencrypt/live/example.com/fullchain.pem
├── x509aa
├── x509ac
├── x509ca
├── x509crl
└── x509ocsp

15 directories, 3 files
# 
        

/etc/strongswan/swanctl/my-vpn-ikev2.conf は以下のように書きます。

connections {
    my-vpn-ikev2 {
        version = 2
        unique = never
        encap = yes
        dpd_delay = 30s
        send_cert = always
        pools = pool-zone-trusted-ipv6, pool-zone-trusted-ipv4
        local {
            id = example.com
            certs = example.com.cert.pem
        }
        remote {
            auth = eap-mschapv2
            eap_id = %any
        }
        children {
            my-vpn-ikev2 {
                local_ts = ::/0, 0.0.0.0/0
                #local_ts = fd00:1234:5678::192:168:2:1/64, 192.168.2.0/24
            }
        }
    }
}

pools {
    pool-zone-trusted-ipv4 {
        addrs = 192.168.2.32-192.168.2.127
        dns = 192.168.16.1, 8.8.8.8, 8.8.4.4
    }
    pool-zone-trusted-ipv6 {
        addrs = fd00:1234:5678::192:168:2:32-fd00:1234:5678::192:168:2:127
        dns = fd00:1234:5678::192:168:2:1, 2001:4860:4860::8888, 2001:4860:4860::8844
    }
}

secrets {
    eap-alice {
        id=alice
        secret="********"
    }
    eap-bob {
        id=bob
        secret="********"
    }
}
        

/etc/strongswan/swanctl/ 以下に証明書を配置します。サーバ証明書の秘密鍵と公開鍵、認証局の公開鍵は最低限必要です。CRL は必要な場合は配置しましょう。シンボリックリンクでも、実体のファイルでも、どちらでも構いません。クライアント証明書は、ここで登録した認証局から発行されているものであれば使えます。

# tree /etc/strongswan/swanctl/
.
├── bliss
├── conf.d
│   └── my-vpn-ikev2.conf
├── ecdsa
├── pkcs12
├── pkcs8
├── private
│   └── example.com.key.pem -> /etc/pki/myCA/servers/example.com.key.pem
├── pubkey
├── rsa
├── swanctl.conf
├── x509
│   └── example.com.cert.pem -> /etc/pki/myCA/servers/example.com.cert.pem
├── x509aa
├── x509ac
├── x509ca
│   └── root.cert.pem -> /etc/pki/myCA/cacerts/root.cert.pem
├── x509crl
│   └── crl.pem -> /etc/pki/myCA/crls/crl.pem
└── x509ocsp

15 directories, 6 files
# 

各種証明書は /etc/strongswan/swanctl/x509 からの相対パスだったり、フルパスだったり、URI だったりして、全く統一感のない設定ファイルです。

connections {
    my-vpn-ikev2 {
        version = 2
        unique = never
        encap = yes
        dpd_delay = 30s
        send_cert = always
        pools = pool-zone-trusted-ipv6, pool-zone-trusted-ipv4
        local {
            id = example.com
            certs = example.com.cert.pem
        }
        remote {
            auth = eap-tls
            eap_id = %any
        }
        children {
            my-vpn-ikev2 {
                local_ts = ::/0, 0.0.0.0/0
                #local_ts = fd00:1234:5678::192:168:2:1/64, 192.168.2.0/24
            }
        }
    }
}

pools {
    pool-zone-trusted-ipv4 {
        addrs = 192.168.2.32-192.168.2.127
        dns = 192.168.16.1, 8.8.8.8, 8.8.4.4
    }
    pool-zone-trusted-ipv6 {
        addrs = fd00:1234:5678::192:168:2:32-fd00:1234:5678::192:168:2:127
        dns = fd00:1234:5678::192:168:2:1, 2001:4860:4860::8888, 2001:4860:4860::8844
    }
}

authorities {
    my-root-ca {
        file = /etc/pki/myCA/cacerts/root.cert.pem
        crl_uris = file:///etc/pki/myCA/crls/crl.pem
    }
}
        

firewalld の設定

strongSwan で使用するポートを開けます。500/UDP、4500/UDP、4500/TCP が開きます。

# firewall-cmd --permanent --zone=public --add-service=ipsec
success
# firewall-cmd --reload
success
#  

すべてのパケットを VPN 経由にする場合は、NAT の設定をします。以下では、eth0 (ゾーン public)がインターネット側、eth1(ゾーン trusted) が VPN 側です。

# firewall-cmd --permanent --zone=trusted --change-interface=eth1
success
# firewall-cmd --permanent --zone=trusted --add-masquerade
success
# firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -o eth0 -j MASQUERADE
success
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i eth1 -o eth0 -j ACCEPT
success
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
success
# firewall-cmd --reload
success
#  

MSS と MTU は /etc/strongswan/strongswan.d/charon/kernel-netlink.conf に設定があるので、適宜変更します。

    # MSS to set on installed routes, 0 to disable.
    # mss = 0
    mss = 1280

    # MTU to set on installed routes, 0 to disable.
    # mtu = 0
    mtu = 1280
 

とりあえず、上記の例では最低値の 1280 に設定していますが、VPN の疎通確認後、各自の環境に合わせて適宜増加させてください。

一度でも旧設定を使った場合

stringswan-starter.service を使うと、古い設定のままで動かすことができます。一旦、古い設定を使ってしばらく運用し、後から stringswan.service に移行しようとお考えの方! ハマりポイントがあります。

stringswan-starter.servicestringswan.service には、ディレクトリ /run/strongswan の扱いについて、以下の差異があります。

  • stringswan-starter.service は起動時に /run/strongswan を作成し、終了時に /run/strongswan を削除する。
  • stringswan.service は起動時に /run/strongswan が存在することを前提としている。存在しない場合は、ソケットの作成に失敗し、エラーを吐いて起動しない。

このことからお分かりのように、一度でも旧設定で stringswan-starter.service を動かすと、stringswan.service が正常に動作するための前提が崩れてしまうのです。乱暴な仕様だなあ、という感じもします。

仕方がないので、stringswan.service に drop-in の仕組みを使って、パッチを充てることします。以下のように入力すると、エディタが起動します。

$ sudo systemctl edit strongswan.service

色が付いている部分を入力します。(2023-01-15 修正)

### Editing /etc/systemd/system/strongswan.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
RuntimeDirectory=strongswan
RuntimeDirectoryMode=0755

### Lines below this comment will be discarded

(2023-01-15 修正)systemd では、PID ファイルや UNIX ソケットの置き場所のディレクトリを作成する場合、RuntimeDirectory で指定することになりました。記事執筆時点では、この方法に移行していないパッケージも残っており、順次切り替わっていくものと思います。紹介している設定のパーミッションおよびオーナーは /usr/lib/tmpfiles.d/strongswan.conf に準拠しています。

これで起動するようになります。

おわりに

書きかけです。