# 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>