---
# System prepended metadata

title: IKEv2/IPsec 架設紀錄
tags: [Tutorial]

---

# IKEv2/IPsec 架設紀錄

[TOC]

### 概要

- IPsec 的目地是保護兩個節點之間的資料傳輸，使用 UDP。
- IPsec 在開始傳輸之前需要先進行資料交換和身份驗證，而實作方法常見的有 L2TP 或 IKEv2，因此常常可以在手機看到 L2TP/IPsec 或 IKEv2/IPsec，而本文使用 IKEv2。
- 伺服器架設選在基於 Debian 的 Linux Mint 上，並且使用 strongSwan 架設，測試終端使用 Linux Mint、Android 13、Windows 10 做為連接測試。
- 實際上 IPsec 是對等連接，但為了解說方便，因為 Linux Mint 稱為伺服器，而手機等連接設備稱為終端。

#### IKEv2
- IKEv2 分為兩步
    1. 第一步是兩個節點會協商出保護第二步的密碼，和後續必要的資訊。
    2. 第二步會開始進行身份驗證，這裡的封包都是加密的，並且會在這一步確定雙方的身份。
- 而在 IKEv2 完全執行完之後會協商出共同的金鑰，後續的資訊皆使用該金鑰加密。
- IKEv2 可以同時保證機密性和完整性，也就是說傳輸的資料有加密並且有認證。
- 身份驗證有許多方式可以選擇
    1. `PSK`：兩個節點預先準備好同一組密碼，並也因為只有密碼，因此有中間人攻擊的可能。
    2. `PKI`：兩個節點使用憑證的方式，並且會有 CA 的參與，這也是推薦的方法，但需要有 PKI 基礎知識，[參考影片](https://www.youtube.com/watch?v=vVbLSba6vOI)。
    3. `EAP`：使用 RADIUS 等方式由第三方服務驗證，但 strongSwan 有些簡單的憑證也可以直接在 strongSwan 內完成。

### 架設流程

#### 套件安裝
- strongswan 需要在 ipsec 這個套件上面運作，但在安裝 strongswan 的時候會自動連同 ipsec 一起安裝。
- 新版的 strongswan 使用 swanctl 來管理各種設定，網路上的教學經使用無 swanctl 的設定檔，但我覺得那個不太好理解。
- Linux 架設需要安裝套件，下方列出的套件皆可使用 apt 安裝。
    - `iptables`：流量轉發用，不裝僅能連線但沒有網路。
    - `strongswan`：核心
    - `libstrongswan-extra-plugins`：加解密等等的算法套件，必裝。
    - `strongswan-swanctl`：管理 strongswan 的設定檔等，強烈建議使用。
    - `strongswan-pki`：用於製作憑證，如果沒有要使用到憑證可以不用安裝。

#### 憑證的生成與簽發
**注意事項**
1. 這裡主要教學如何製作憑證，如果不是使用 PKI 做為驗證方式，可以直接跳過。
2. 因為需要簽發多張憑證，因此會先自簽一張憑證，而這張憑證需要給所有的終端安裝才能使用。
3. 網路上的免費憑證使用上相當不方便，而且網路上的免費憑證大部份也不能直接被終端信任（如 Let's Encrypt 等），也需要另外新增。
4. 憑證工具需要使用到 openssl 或 strongswan 自帶的 pki 工具，下方僅列簽發功能，其它功能請參考官方文件。
5. 憑證製作過程中會有很多檔案的產生，建議開一個資料夾保存，並且該資料夾的權限要加以控管，因為裡面會有私鑰等機密資訊。
6. 憑證的保存有 PEM 和 DER 兩種格式，這邊推薦 PEM，否則有些系統會有相容性問題。
7. 憑證的算法有非常多的選擇，而常用的 ED25519 在這裡不太建議，因為在 Android 和 Windows 上面都可能會遇到問題，這邊建議使用 RSA。
8. 憑證的簽發一般會使用簽發請求，但這裡為了方便則會直接簽發。
9. 根憑證要先自簽才能去簽別張。
10. 識別名稱在伺服器端非常重要，CN 一定要設成域名，否則在身份驗證時會有錯誤。

**常用指令**
- 生成私鑰
    - `pki --gen --type rsa --size 4096 --outform pem > <私鑰路徑>.pem`：使用 strongswan-pki 套件生成
    - `openssl genpkey -outform pem -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -quiet > <私鑰路徑>.pem`：使用 OpenSSL 套件生成

- 憑證簽發
    - 使用 strongswan-pki 套件簽發
    - 識別名稱格式：`C=國家, O=組織名, CN=域名或其它識別代號`
    ```
    pki --pub --in <私鑰路徑> | \
    pki --issue --cakey <根憑證私鑰路徑> --cacert <根憑證路徑> \
    --dn "<識別名稱>" --lifetime <有效天數> \
    --outform pem > <憑證路徑>.pem
    ```
    - 使用 OpenSSL 套件簽發
    - 識別名稱格式：`/C=國家/O=組織名/CN=域名或其它識別代號/`
    ```
    openssl req -outform PEM -x509 \
    -key <私鑰路徑> -days <有效天數> \
    -CA <根憑證路徑> -CAkey <根憑證私鑰路徑> \
    -subj "<識別名稱>" \
    > <憑證路徑>.pem
    ```

**根憑證**
- 憑證的出發點就是製作根憑證，下面有幾點注意事項。
1. 根憑證的私鑰的生成建議使用 OpenSSL，因為可以將私鑰加密，在使用時需要再次輸入密碼，這樣相對比較安全。
    - 如果要啟用加密則在生成私鑰的指令內加入 `-aes256` 即可。
2. 根憑證簽發的有效天數一般都會比較長，並且識別名稱可以取好懂的就行了。

- 根憑證的簽發是使用自己的私鑰進行的（自簽），而 strongswan-pki 和 OpenSSL 的自簽方式如下：
    - 使用 strongswan-pki 套件簽發
    ```
    pki --self --in <根憑證私鑰路徑> \
    --type priv --outform pem --lifetime 3650  \
    --dn "<識別名稱>" > <憑證路徑>.pem
    ```
    - 使用 OpenSSL 套件簽發
    ```
    openssl req -key <根憑證私鑰路徑> \
    -outform pem -x509 -days 3650 \
    -subj <識別名稱> > <憑證路徑>.pem
    ```
- 根憑證在 Windows 上的副檔名要改為 `.crt`，否則 Windows 會不知道是什麼。

**伺服器憑證**
- 伺服器憑證的要求比較多，下面是幾個注意事項：
1. 識別名稱的 CN 一定要是域名，如果沒有域名則使用 IP。
2. 伺服器憑證需要加入 SAN 選項，否則身份驗證會有錯誤，下方僅列出 strongswan-pki 作法，OpenSSL 請參考[教學文章](https://blog.cssuen.tw/%E7%94%A8-san-certificate-%E5%81%9A-multi-domain-certificate-c7403e05c697)。
    - 加入 `--san <域名或 IP>` 選項即可。
4. 簽發的時候如果連線終端有 Windows，則需要一個多一個用途選項。
    - 如果使用的是 strongswan-pki 則加入 `--flag serverAuth` 選項。
    - 如果使用的是 OpenSSL 則加入 `-addext "extendedKeyUsage = serverAuth"` 選項。
    
**終端憑證**
- 終端憑證的做法一樣要先生成私鑰，並且進行簽發即可，但不同的終端裝置有不同的安裝方式。
- 終端憑證除了 Linux 之外，其於的都需要以 PKCS\#12 的格式做包裝，Android 和 Windows 的細項如下：
    - Android 終端憑證副檔名為 `.p12`，包裝時要加入 `-legacy` 選項，可以參考問題-Android-1
    - Windows 終端憑證副檔名為 `.pfx`
    - PKCS\#12 包裝僅能使用 OpenSSL 進行，指令如下：
    ```
    openssl pkcs12 -export -inkey <憑證私鑰路徑> -in <憑證路徑> > <憑證路徑>.<p12/pfx>
    ```

#### 設定檔
- 下方的設定檔皆使用 swanctl 定義的格式，其它格式可以參考[官方文件](https://docs.strongswan.org/docs/5.9/index.html)的 Legacy 部份。
- swanctl 的設定檔直接在 `/etc/swanctl/conf.d/` 底下上一個副檔名為 `.conf` 的檔案即可。
- 下方只列出常用項，其它請參考[官方文件](https://docs.strongswan.org/docs/5.9/swanctl/swanctlConf.html)。
:::spoiler PKI 驗證
```
# 連線相關設定
connections {
    # 連線設定內可以有多組的設定檔
    # 該組設定檔名稱 rw
    rw {
        # 選定 IKE 版本，下方設定為 IKEv2
        # 可選 0、1、2，預設為 0，代表皆可
        version = 2
        
        # 下面兩點可參考細節-3.
        # 是否傳送憑證請求給終端
        # 預設為 yes
        send_certreq = no
        
        # 是否傳送證書給終端，一定要開啟，因為這裡使用 PKI 進行身份驗證
        # 可選 never、ifasked、always
        # 預設為 ifasked
        # always 為主動傳送
        send_cert = always
        
        # 如果資料比較大，是否要進行分割
        # 可選 yes、accept、force、no
        # yes：只要對方同意則資料進行分割
        # accept：本身的資料不分割，但通知對方可以接收資料分割
        # force：所有資料皆分割
        # no：自已不分割，並且告知對方不可分割
        # 預設 yes
        # fragmentation = yes
        
        # 指定 IP 池
        pools = rw_pool
        
        # 指定 IKEv2 第一步的加解密演算法，一般不設定，可以參考問題-Windows-1
        # proposals = aes256-sha256- prfsha256-modp2048
        
        # reauth_time：代表幾秒斷開連接重新連線
        # rekey_time：幾秒重新協商一次 IPsec 金鑰
        # reauth_time = 7200s
        # rekey_time = 1h
        
        # 是否開啟多終端，也就是說在開啟之後可以同時多人連接該伺服器
        # 可選 yes、no，預設為 yes
        # mobike = yes

        # 需要注意 IPsec 是對等協議，因此此處的 local 相對於終端來講話就是 remote
        # 設定本機如何讓驗證自己
        local {
            # 選用 PKI 的方式
            # 其它選項請見官方文件
            auth = pubkey
            # 用來驗證的憑證
            certs = pc-cert.pem
        }
        # 設定如何驗證對方
        remote {
            # 使用 PKI 來驗證
            auth = pubkey
        }
        
        # 連接之後的細節，這裡只需要設定一項
        children {
            rw {
                # 開啟的通道，也就是說允許哪些目的地的 IP 流進本機，可以參考細節-2.
                #如果所有流量都允許則為 0.0.0.0/0
                local_ts = 0.0.0.0/0
            }
        }
    }
}

# IP 池，同 DHCP 的功能。
pools {
    # 這裡一樣可以定義多個池
    rw_pool {
        # 分配的 IP
        addrs = 192.168.10.0/24
    }
}
```
:::

#### 網路環境
- 一般的網路環境都有閘道器，並且需要設定 Port forwarding。
    - `4500/udp`：IKEv2 用
    - `500/udp`：IPsec 用
- 伺服器的 iptables 需要加入下面兩條規則，否則會出現可以連上但沒網路的狀況。
    ```
    iptables -t nat -A POSTROUTING -s <分配的 IP> -m policy --dir out --pol ipsec -j ACCEPT
    iptables -t nat -A POSTROUTING -s <分配的 IP> -j MASQUERADE
    ```
- 伺服器需要開啟網路轉發，可以直接執行 `echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf`。


#### 啟動伺服器
- daemon 為 `ipsec`，一定要自動啟動，否則任何功能都無法使用，手動啟動指令 `ipsec start`
- swanctl 設定檔完成之後只要輸入 `swanctl -q` 即可讀取所有設定檔，並且自動設定完成。
- 設定檔在變更之後同樣輸入 `swanctl -q`。
- 如果想要看連線紀錄可以使用 `swanctl --log`。

**Docker 注意事項**
1. ports 要進行 mapping。
2. 要開啟 privileged，否則 ipsec 套件無法運作。

### 各種作業系統終端 PKI 連接
#### Android
- Android 要先安裝好終端憑證和根憑證，但因版本不同而安裝方式也不同，這點請參考手機的官方網站。
- VPN 軟體我推薦下載 strongSwan 官方出的 [APP](https://play.google.com/store/apps/details?id=org.strongswan.android)，這個設定上非常容易，並且還有 log 功能可以除錯。
- 下方兩張為設定範例：
    1. Server 填入要連接的域名或 IP
    3. VPN Type 設定為 IKEv2 Certificate
    4. User certificate 選擇終端憑證
    5. CA 憑證可以選用自動，但自動會把所有的根憑證都丟到伺服器，因此可以直接手動指定
    6. Split tunneling 在 Advance 裡面，填入 `0.0.0.0/0` 代表所有流量，參考細節-2
<div style="text-align: center">
    <img src=https://i.imgur.com/sRmKOmq.jpeg width=40%>
    <img src=https://i.imgur.com/HoqqzUw.jpeg width=40%>
</div>

#### Windows
- Windows 和 Android 一樣在連接時要先安裝終端憑證和根憑證，兩者都是點兩下就會進入憑證安裝精靈，但有幾點需要注意：
    1. 兩個憑證在憑證安裝精靈的第一步都需要選擇本機電腦，否則會出現問題。
    <div style="text-align: center;">
        <img src=https://i.imgur.com/cQBnlYD.png
             width=80%
             style="border: 1px solid black"
        >
    </div>

    2. 根憑證在憑證安裝精靈的時候在注意在安裝路徑要選對，如下圖。
    <div style="text-align: center;">
        <img src=https://i.imgur.com/sAQxcur.png
             width=80%
             style="border: 1px solid black"
        >
    </div>

- VPN 軟體使用原生的 VPN 功能即可，但可能會遇到很多問題，可以參考問題-Windows。
- 原生 VPN 可以在控制台找到，Windows 10 可以在新版控制台找到，因為版本不同而介面不同，請自行查找資料。
    - 下方為 Windows 10 新版控制台新增 VPN 範例：
        1. VPN 類型選 IKEv2
        2. 伺服器名稱或位址填入 IP 或域名
        3. 登入資訊的類型選憑證
        <div style="text-align: center">
            <img src=https://i.imgur.com/wqEz7pi.png width=40%>
        </div>
        <br>
        
        - Windows 並不需要指定根憑證和終端憑證。

#### Linux
- 本人僅在 Linux Mint 上測試，我使用 `network-manager-strongswan` 直接使用 apt 進行安裝，在安裝完之後一般可以直接在網路設定裡面新增 IKEv2 VPN。
- 下圖為範例：
    1. Address 填入要連接的域名或 IP
    2. Server 的 Certificate 選擇根憑證路徑
    3. Client 的 Authentication 選 Certificate
    4. Client 的 Certificate 選終端憑證，Private Key 選終端憑證的私鑰

<div style="text-align: center">
    <img src=https://i.imgur.com/npy1uBs.png width=40%>
</div>

### 細節
1. PKI 身份驗證原理
    - 簡圖 **（非實際情況）**
    ```mermaid 
    sequenceDiagram
        終端 ->> 伺服器: 1.你好
        伺服器 ->> 終端: 2. 你好，把你的憑證給我
        終端 ->> 伺服器: 3. 這是我的憑證和簽章，把你的憑證也給我
        伺服器 ->> 終端: 4. 這是我的憑證和簽章
    ```
    - 不同的設備、系統裡面的預設選項不同，如果雙方有一方沒有拿到對方的憑證，則會發生身份驗證錯誤。
    - 請對方給予憑證的功能（憑證請求）不一定每個設備都有，因此使用 PKI 驗證一定要把主動發送憑證開啟。
    - 有些設備不一定會主動給予憑證（例如 Linux），因此伺服器一定要開啟憑證請求。

2. traffic selectors 功能
    - strongSwan 提供的一個功能，其目地在於分流，也就是說可以設定哪些符合條件的 IP 可以通過。
    - 雙方都有 ts 功能，`local_ts` 也就是說對方可以發送什麼流量過來，而 `remote_ts` 則相反。
    - 如果有一邊的 ts 沒有設定好，就會發生封包不走 VPN 通道，而是直接流出去的狀況。
    - 伺服器的 `remote_ts` 請勿設定，否則會出現連的上確沒有網路的狀況，因為所有的流量會被反射回來。

### 問題
#### Windows
1. 連接時出現<span class="underline">原則對應錯誤</span>。
    - 原因：Windows 預設的身份驗證算法僅支持下方列出的算法：
        - `AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048`
        - `AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048`
        - `AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048`
    - 解法：將伺服器的身份驗證算法改為 Windows 支持的算法，即改變 swanctl 設定檔中的 `proposals` 項，將其改為下方列出的其中一種：
        - `aes256-sha1-prfsha1-modp2048`
        - `aes256-sha256-prfsha256-modp2048`
        - `aes256-sha384-prfsha384-modp2048`
    - 如果不想要改變算法，也可以使用改變機碼的方式（本人未測試），[參考資料](https://danny50610.github.io/2019/11/27/ikev2-vpn-server-with-strongswan-note.html)
    
2. 終端憑證已經安裝，但連線時總是顯示 <span class="underline">IKE 找不到有效的機器憑證...</span>
    - 原因：找不到裝好的憑證，或者憑證有問題。
    - 解法：確定安裝的第一步有選擇本機電腦，並且從<span class="underline">管理電腦憑證</span>檢查憑證。
        - 根憑證應該放在<span class="underline">受信任的根憑證授權單位->憑證</span>，如果沒有代表安裝的時候點錯地方。
        - 終端憑證應該放在<span class="underline">個人->憑證</span>，如果沒有代表安裝的時候點錯地方，如果有則點開來看，正常應該出現：
        <div style="text-align: center;">
            <img src=https://i.imgur.com/cvLlAmD.png
                 width=40%
                 style="border: 1px solid black"
            >
        </div>
        <br>
        
        - 下方兩個憑證都是有問題的，左邊是根憑證<span class="underline">沒有使用 RSA 做為算法</span>，右邊是<span class="underline">憑證過期</span>。
        <div style="text-align: center">
            <img src=https://i.imgur.com/4ejHI3Q.png
                 width=40%
                 style="border: 1px solid black"
            >
            <img src=https://i.imgur.com/rpwtb6N.png
                 width=40%
                 style="border: 1px solid black"
            >
        </div>
        <br>
        
        - 如果憑證本身的算法不是 RSA，不會顯示任何錯誤，但是會找不到終端憑證。

3. 使用 PKI 連接時，出現<span class="underline">指定的演算法無效</span>。
    - 原因：憑證算法不支持，有可能是根憑證、終端憑證、伺服器憑證。
    - 解法：先確定所有的憑證都是使用 RSA 而非其它算法。
        - 和 Windows-問題-2 一樣，可以到本機憑證管理員確認根憑證和終端憑證，下方是一個使用非 RSA 的根憑證。
        <div style="text-align: center">
            <img src=https://i.imgur.com/4ejHI3Q.png
                 width=40%
                 style="border: 1px solid black"
            >
        </div>
        <br>
        
        - 伺服器憑證可以使用 `pki --print -i <伺服器憑證路徑>` 檢查是不是使用 RSA。
          下方的圖片為正確的伺服器憑證，注意到紅色處為 RSA 算法
            <div style="text-align: center">
                <img src=https://i.imgur.com/UC1yE9s.png
                     width=80%
                >
             </div>
          下方的圖片為錯誤的伺服器憑證，注意到紅色處為 ED25519 算法。
            <div style="text-align: center">
                <img src=https://i.imgur.com/UkWRORJ.png
                     width=80%
                >
            </div>
            
3. 使用 PKI 連接時，出現<span class="underline">IKE 驗證認證不可接受</span>。
    - 原因：伺服器憑證出現問題。
    - 解法：伺服器憑證可以使用 `pki --print -i <伺服器憑證路徑>` 檢查下方圖片中紅色的部份。
            <div style="text-align: center">
                <img src=https://i.imgur.com/B1ruPUx.jpeg
                     width=80%
                >
            </div>
            如果 flags 為空或 CN 不是正確的域名或 IP，則需要重新簽發伺服器憑證。

#### Android
1. 終端憑證安裝時不停出現密碼錯誤，就算輸入正確也是一樣
    - 原因：Android 支持的 PKCS\#12 算法錯誤。
    - 解法：私鑰生成確定是使用 RSA 生成的，並且執行 `openssl pkcs12` 指令時，加入 `-legacy` 選項。
        需要注意並所有的 Android 版本都需要加 `-legacy` 選項，但測試到 Android 13 仍然需要。

2. PKI 身份驗證錯誤
    <div style="text-align: center">
        <img src=https://i.imgur.com/XmAwj8G.jpeg
            width=40%
            style="border: 1px solid black"
        >
        <img src=https://i.imgur.com/DSPeP0M.jpeg
            width=40%
            style="border: 1px solid black"
        >
    </div> 
    <br>
    
    - 原因：可能是<span class="underline">終端無法認證伺服器</span>或者<span class="underline">伺服器無法認證終端</span>。
    - 解法：可能性如下
        1. 如果是終端無法認證伺服器，則先確定 swanctl 的設定檔有沒有開啟傳送證書，參考細節-3。
        2. 檢查伺服器憑證，可以參考問題-Windows-3，確定有無加入 SAN 選項，參考下圖紅色部份。
            <div style="text-align: center">
                <img src=https://i.imgur.com/NItUc1b.png
                     width=80%
                >
            </div>
            <br>
        4. 確定根憑證有正確安裝。
        5. 如果是伺服器無法認證終端，則應檢查認證方法有沒有選錯，應該要選 IKEv2 Certificate。
        6. 憑證過期等，同樣使用 strongswan-pki 工具檢查，可以參考問題-Windows-3。

#### Linux
1. 無法開啟 VPN 連接
    - 原因：參考問題-Android-2
    - 解法：參考問題-Android-2

---
### 參考資料
1. [strongSwan Documentation](https://docs.strongswan.org/docs/5.9/index.html)
1. [OpenSSL Documentation](https://www.openssl.org/docs/)
3. [安全协议系列（五）---- IKE 与 IPSec（中）](https://www.cnblogs.com/efzju/p/5041797.html)
4. [strongSwan-远程接入-eap-mschapv2](https://zhuanlan.zhihu.com/p/645782263) 

<style>
    .underline {
        text-decoration: underline 
    }
</style>