---
# System prepended metadata

title: OpenVPN setup guide for Ubuntu 22.04 VPS
tags: [Docs]

---

# Thiết lập và cấu hình OpenVPN Server trên VPS Ubuntu 22.04

#### Phiên bản: 1.3.0 | [Changelog](#Changelog)

Tài liệu này cung cấp các bước và hướng dẫn để thiết lập máy chủ OpenVPN trên VPS Ubuntu 22.04 và cấu hình sao cho các máy cùng kết nối với VPN có thể nói chuyện được với nhau.

Các bước bao gồm việc cài đặt các phần mềm liên quan, tạo PKI (Public Key Infrastructure), cấu hình tường lửa và định tuyến và tạo các tệp cấu hình (`.ovpn`) máy chủ và máy khách.

## Tài liệu tham khảo

- [Nyr's openvpn-install script](https://github.com/Nyr/openvpn-install/blob/master/openvpn-install.sh)
- [OpenVPN How To Guide: Set Up & Configure OpenVPN Client/server VPN](https://openvpn.net/community-resources/how-to/)
- [DigitalOcean How To Set Up and Configure an OpenVPN Server on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-and-configure-an-openvpn-server-on-ubuntu-20-04)
- [ArchWiki OpenVPN wiki page](https://wiki.archlinux.org/title/OpenVPN)

## Tổng quan

1. [Phạm vi và điều kiện tiên quyết](#Phạm-vi-và-điều-kiện-tiên-quyết)
2. [Cài đặt các phần mềm và công cụ](#Cài-đặt-các-phần-mềm-và-công-cụ)
3. [Thiết lập PKI](#Thiết-lập-PKI)
4. [Tạo chứng chỉ và khoá bí mật](#Tạo-chứng-chỉ-và-khoá-bí-mật)
   4.1. [Máy chủ](#Máy-chủ)
   4.2. [Các máy khách](#Các-máy-khách)
   4.3. [Tạo tham số Diffie-Hellman](#Tạo-tham-số-Diffie-Hellman)
5. [Cấu hình máy chủ OpenVPN](#Cấu-hình-máy-chủ-OpenVPN)
6. [Cấu hình bảo mật kênh điều khiển TLS](#Cấu-hình-bảo-mật-kênh-điều-khiển-TLS)
7. [Cấu hình định tuyến](#Cấu-hình-định-tuyến)
8. [Tạo các tệp cấu hình OpenVPN cho máy khách](#Tạo-các-tệp-cấu-hình-OpenVPN-cho-máy-khách)
9. [Cài đặt cấu hình OpenVPN tại máy khách](#Cài-đặt-cấu-hình-OpenVPN-tại-máy-khách)
10. [Kiểm thử](#Kiểm-thử)
    10.1 [Xác minh kết nối VPN hoạt động với các chức năng cơ bản](#Xác-minh-kết-nối-VPN-hoạt-động-với-các-chức-năng-cơ-bản)
    10.2 [Xác minh kết nối giữa các máy khách trên mạng VPN](#Xác-minh-kết-nối-giữa-các-máy-khách-trên-mạng-VPN)

## Phạm vi và điều kiện tiên quyết

### Phạm vi

Tài liệu này chỉ bao gồm những mục tiêu:

- Cài đặt và cấu hình máy chủ OpenVPN hỗ trợ IPv4 hoạt động trên VPS Ubuntu 22.04.
- Tạo chứng chỉ và các tệp cấu hình máy khách để kết nối máy chủ OpenVPN.
- Nếu cần thiết, giải thích lý do đằng sau các lựa chọn cấu hình.

Những thứ tài liệu này **không** bao gồm:

- Cấu hình hỗ trợ IPv6
- Các cài đặt, thực hành bảo mật nâng cao.
- Giải thích chi tiết các câu lệnh CLI.
- Cách khắc phục các sự cố kết nối.
- Tự động quản lý nhiều máy khách.

**Một số lưu ý:**

- Tài liệu tập trung vào một phương pháp cố định để cài đặt và cấu hình OpenVPN. Do đó, tài liệu này chỉ mang tính tham khảo và người triển khai có thể điều chỉnh cấu hình cho phù hợp với trường hợp sử dụng cụ thể của mình.
    - Người triển khai cần hiểu rõ trước khi chạy ý nghĩa và các hệ quả của một số các câu lệnh shell được trình bày mẫu trong tài liệu này, đặc biệt là các câu lệnh thực hiện chức năng viết và thay đổi các tệp cấu hình; tránh việc các câu lệnh cài đặt các cấu hình không mong muốn, hoặc không phù hợp với trường hợp sử dụng cụ thể của người triển khai.
- Tài liệu hướng dẫn này được thực hiện dưới người dùng `root`. Nếu thực hiện trên tài khoản người dùng có quyền admin, các lệnh cần được thêm tiền tố `sudo` khi cần thiết (như các lệnh `apt`, `cp`, `chmod`, `chown`, `systemctl`,...)
- Trong tài liệu hướng dẫn, các phần liên quan đến địa chỉ IPv4 công cộng của VPS sẽ bị lược bỏ và thay thế bởi `<vps_public_ipv4>`. Địa chỉ công cộng của từng VPS cần được tự xác định và điền vào những chỗ tương ứng bởi người thực hiện cấu hình.
    - Đặc biệt nếu VPS đứng sau NAT, IPv4 công cộng phải được xác định qua những cách như hỏi một dịch vụ bên ngoài. Dưới đây là một lệnh mẫu để xác định:
      ```bash
      curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/" | grep -m 1 -oE "^[0-9]{1,3}(\.[0-9]{1,3}){3}$"
      ```

### Điều kiện tiên quyết

- VPS chạy Ubuntu 22.04 cho phép đăng nhập root hoặc người dùng có quyền admin (`sudo`, `doas`,...)
- Có trình soạn thảo văn bản dòng lệnh (`nano`, `vim`, `nvim`,...) được cài đặt để chỉnh sửa các tệp cấu hình khi cần.
- Kết nối đến VPS qua SSH (phải cài trên máy thực hiện kết nối) hoặc qua phương pháp nhà cung cấp dịch vụ cung cấp.

## Cài đặt các phần mềm và công cụ

Trước khi cài đặt các phần mềm, ta đảm bảo danh sách phần mềm của `apt` được cập nhật và cập nhật các gói có sẵn trên hệ thống nếu cần bằng câu lệnh sau:

```sh
apt update && apt upgrade
```

> ***Gợi ý***
> 
> Có thể cấu hình cho `apt` tự động chọn các máy chủ nhân bản (mirror) gần nhất với VPS, từ đó giảm độ trễ và tăng băng thông tiềm năng khi đồng bộ và tải các gói phần mềm. Điều này đạt được bằng cách chỉnh các dòng chỉ định máy chủ trong `/etc/apt/sources.list` để sử dụng giao thức `mirror://`. Vì mục đích an toàn và tin cây, tệp `sources.list` nên được backup trước khi được điều chỉnh.
> 
> Ví dụ: dòng
> ```
> deb http://nova.clouds.archive.ubuntu.com/ubuntu/ jammy main restricted
> ```
> sẽ được chỉnh thành
> ```
> deb mirror://mirrors.ubuntu.com/mirrors.txt jammy main restricted
> ```
> Dưới đây là một câu lệnh mẫu để thay đổi tất cả các dòng chỉ định trong `sources.list` sang dùng giao thức `mirror://`:
> ```sh
> sed -i -e 's/http:\/\/nova.clouds.archive/mirror:\/\/mirrors/' -e 's/\/ubuntu\//\/mirrors.txt/' /etc/apt/sources.list
> ```
> 
> Lưu ý rằng, việc chuyển sang sử dụng giao thức này có thể sẽ khiến lệnh `do-release-upgrade` hoạt động không ổn định.

Ta sẽ cài 4 gói sau:

- `openvpn`
- `openssl` và `ca-certificates`
  - Các chức năng mã hóa và xác thực an toàn sẽ dựa vào thư viện OpenSSL.
- `iptables`
  - Một phần mềm dòng lệnh để cấu hình tường lửa nhân Linux. Ta sẽ cấu hình firewall ở những bước sau.

```sh
apt install openvpn openssl ca-certificates iptables
```

Sau đó cài [Easy-RSA](https://github.com/OpenVPN/easy-rsa), là công cụ mà ta sẽ sử dụng trên máy chủ OpenVPN để [thiết lập PKI](#Thiết-lập-PKI) và [tạo các chứng chỉ và khoá bí mật](#Tạo-chứng-chỉ-và-khoá-bí-mật). Ta sẽ tải tarball về bằng `curl` và giải nén vào `/etc/openvpn/server/easy-rsa`

```sh
mkdir -p /etc/openvpn/server/easy-rsa/
curl -sL "https://github.com/OpenVPN/easy-rsa/releases/download/v3.2.0/EasyRSA-3.2.0.tgz" | tar xz -C /etc/openvpn/server/easy-rsa/ --strip-components 1
chown -R root:root /etc/openvpn/server/easy-rsa/
```

## Thiết lập PKI

Một trong những bước đầu tiên trong việc thiết lập và cấu hình server OpenVPN là thiết lập [PKI](https://en.wikipedia.org/wiki/Public_key_infrastructure) (hạ tầng khóa công khai), bao gồm:

- Chứng chỉ `.crt` (còn được hiểu là khoá công khai) và khóa bí mật `.key` cho máy chủ và từng máy khách
- Chứng chỉ và khóa bí mật của [Nhà cung cấp chứng thực số](https://en.wikipedia.org/wiki/Certificate_authority) (CA) được sử dụng để ký từng chứng chỉ máy chủ và máy khách.

OpenVPN hỗ trợ xác thực hai chiều dựa trên chứng chỉ, nghĩa là máy khách phải xác thực chứng chỉ máy chủ và máy chủ phải xác thực chứng chỉ máy khách trước khi thiết lập sự tin cậy lẫn nhau.

Để thiết lập PKI, ta `cd` vào thư mục `/etc/openvpn/server/easy-rsa` mà ta tạo ở bước và sử dụng phần mềm `easyrsa` chứa trong đó để khởi tạo CA cùng với các chứng chỉ của máy chủ và máy khách:

```sh
cd /etc/openvpn/server/easy-rsa
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa --days=3650 gen-crl
```

Các câu lệnh lần lượt:

- Thay đổi thư mục làm việc đến thư mục EasyRSA.
- Khởi tạo môi trường PKI. Điều này tạo ra các thư mục và tệp cấu hình cần thiết để tạo chứng chỉ và khóa.
- Tạo chứng chỉ của Nhà cung cấp chứng thực số (CA). Tham số `nopass` chỉ định rằng chứng chỉ của CA sẽ không được bảo vệ bằng mật khẩu.
- Tạo Danh sách thu hồi chứng chỉ (CRL). Danh sách này chứa các chứng chỉ đã bị thu hồi và không còn đáng tin cậy nữa. `--days=3650` đặt hiệu lực của danh sách là 10 năm.

## Tạo chứng chỉ và khoá bí mật

### Máy chủ

Với thư mục làm việc tại `/etc/openvpn/server/easy-rsa`, chạy câu lệnh sau:

```sh
./easyrsa --days=3650 build-server-full server nopass
```

Câu lệnh tạo chứng chỉ và khóa bí mật máy chủ có tên "server". `--days=3650` đặt thời hạn hiệu lực của chứng chỉ là 10 năm (3650 ngày). Tương tự như CA, `nopass` loại bỏ bất cứ bảo vệ bằng mật khẩu nào.

Tiếp theo ta sẽ thực hiện một chuỗi lệnh

```sh
cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server
chown nobody:nogroup /etc/openvpn/server/crl.pem
chmod o+x /etc/openvpn/server/
```

Dòng đầu tiên là một lệnh `cp` sao chép các file sang thư mục `/etc/openvpn/server`. Về cơ bản, lệnh này sao chép các chứng chỉ, khóa bí mật và CRL cần thiết từ thư mục EasyRSA sang thư mục được sử dụng bởi máy chủ OpenVPN

Hai dòng tiếp theo thiết lập quyền trên tệp CRL đã sao chép (`/etc/openvpn/server/crl.pem`).

- Lệnh `chown` thay đổi quyền sở hữu tệp `crl.pem` thành của người dùng `nobody` và nhóm `nogroup`, vì CRL được đọc mỗi khi máy chủ nhận được một kết nối từ máy khách, mà tiến trình OpenVPN thường được cấu hình để chạy dưới người dùng `nobody`.
- Lệnh `chmod` sửa đổi các quyền của thư mục `/etc/openvpn/server`. Phần `o+x` cấp quyền thực thi (`x`) cho người khác (`o`). Nếu thư mục không có quyền `x`, OpenVPN không thể chạy lệnh trên tệp CRL.

### Các máy khách

Với thư mục làm việc tại `/etc/openvpn/server/easy-rsa`, chạy câu lệnh sau:

```sh
./easyrsa --days=3650 build-client-full client1 nopass
```

Câu lệnh tạo chứng chỉ và khoá bí mật cho máy khách có tên được chỉ định "client1". Một lần nữa, `--days=3650` đặt hiệu lực là 10 năm và `nopass` sẽ loại bỏ bảo vệ bằng mật khẩu.

Ta không cần di chuyển hay sao chép chứng chỉ và khoá bí mật của máy khách đi đâu. Những tệp này về sau sẽ được sử dụng để sinh cấu hình `.ovpn` cho từng máy khách.

Những cặp chứng chỉ và khoá bí mật cho các máy khách khác có thể được tạo thêm với tên riêng biệt ở bất cứ thời điểm nào. Sẵn tiện vẫn đang ở thư mục làm việc `easy-rsa`, ta có thể tạo sẵn cho các máy khách khác:

```sh
for CLIENT_NUM in {1..10}; do ./easyrsa --days=3650 build-client-full "client$CLIENT_NUM" nopass; done
```

### Tạo tham số Diffie-Hellman

Các tham số Diffie Hellman phải được tạo để hoàn thiện cơ sở hạ tầng xác thực cho máy chủ OpenVPN. Easy-RSA có sẵn lệnh để sinh tham số DH. Sau khi sinh xong, ta `cp` tệp `dh.pem` vào thư mục `/etc/openvpn/server`.

```sh
./easyrsa gen-dh
cp pki/dh.pem /etc/openvpn/server
```

## Cấu hình máy chủ OpenVPN

Trong OpenVPN, **chỉ thị** là lệnh cài đặt được sử dụng để cấu hình hành vi của máy chủ và máy khách. Các lệnh này được chỉ định trong các tệp cấu hình `.conf` và cho OpenVPN biết cách thiết lập kết nối.

Đầu tiên ta `cp` tệp `server.conf` mẫu được cung cấp khi ta cài gói `openvpn` làm điểm bắt đầu cho tệp cấu hình:

```sh
cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn/server/
```

Chúng ta sẽ cần thay đổi một vài dòng trong tệp này bằng cách

- Bật/Tắt những chỉ thị bằng cách thêm/xoá ký tự `;` vào đầu dòng
- Thay đổi những chỉ thị sẵn có
- Thêm chỉ thị vào dòng riêng

Một số chỉ thị quan trọng cần chỉnh:

- Cấu hình IPv4 **công cộng** của OpenVPN
    ```
    local <vps_public_ipv4>
    ```
- Chỉ đinh tệp tham số Diffie-Hellman ta đã tạo ở bước [Tạo tham số Diffie-Hellman](#Tạo-tham-số-Diffie-Hellman)
    ```
    dh dh.pem
    ```
- Dùng cấu trúc liên kết mạng `subnet` (việc đánh địa chỉ được thực hiện bằng IP & netmask)
    ```
    topology subnet
    ```
- Chuyển hướng tất cả lưu lượng truy cập Internet của máy khách đến máy chủ
    ```
    push "redirect-gateway def1 bypass-dhcp"
    ```
- Cấu hình custom DNS (cấu hình ở dưới là Cloudflare)
    ```
    push "dhcp-option DNS 1.1.1.1"
    push "dhcp-option DNS 1.0.0.1"
    ```
- Cấu hình phương pháp ngăn chặn rò rỉ DNS trong Windows bằng cách chặn quyền truy cập vào máy chủ VPN thông qua các giao diện mạng thông thường bằng quy tắc Windows Filtering Platform (WFP)
    ```
    push "block-outside-dns"
    ```
- Cấu hình cho các máy khách khác nhau cùng kết nối với máy chủ OpenVPN có thể "nhìn" thấy nhau.
    ```
    client-to-client
    ```
    > **_Lưu ý_**
    > 
    > Với cài đặt hoàn chỉnh của tài liệu hướng dẫn này, các máy khách vẫn luôn có thể ping tới nhau mà không cần bật chỉ thị `client-to-client`, nhờ việc các gói tin được chuyển hướng tới Default Gateway trên Subnet `10.8.0.0/24` của máy chủ là `10.8.0.1`. Gateway sẽ tìm destination của gói tin và chuyển hướng lại về đến client đích.
    >
    > Chỉ thị được đặt với lý do giúp cho việc định tuyến các gói hiệu quả hơn (vì chúng không cần phải chuyển hướng lại trên Default Gateway), có thể giảm độ trễ và tài nguyên máy chủ.
- Tắt hai dòng chỉ thị `tls-auth` và `cipher`, các cài đặt liên quan đến bảo mật và mã hóa sẽ được cấu hình tại bước [Cấu hình bảo mật kênh điều khiển TLS](#Cấu-hình-bảo-mật-kênh-điều-khiển-TLS).
    ```
    ;tls-auth ta.key
    ;cipher AES-256-CBC
    ```
- Giảm các đặc quyền của daemon OpenVPN khi khởi chạy:
    ```
    user nobody
    group nogroup
    ```
- Cấu hình cho máy chủ OpenVPN kiểm tra danh sách thu hồi chứng chỉ (CRL)mỗi lần thực hiện kết nối
    ```
    crl-verify crl.pem
    ```
    
Sau khi chỉnh sửa, `server.conf` sẽ bao gồm những chỉ thị được bật sau:

```
local <vps_public_ipv4>
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem

topology subnet
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 1.0.0.1"
push "block-outside-dns"
client-to-client

keepalive 10 120
user nobody
group nogroup
persist-key
persist-tun
verb 3

crl-verify crl.pem
```

Với cấu hình ở trên, máy chủ OpenVPN sẽ sử dụng mạng con 10.8.0.0/24 để quản lý việc gán IP máy khách. Tiến trình OpenVPN trên máy chủ sẽ chỉ định địa chỉ IPv4 trong dải từ 10.8.0.1 đến 10.8.0.254 cho các máy khách được kết nối (bản thân máy chủ thường sử dụng địa chỉ .1 và đóng vai trò là Gateway trong mạng con, trong trường hợp này là 10.8.0.1). Sau khi chỉ định địa chỉ cho từng máy khách kết nối, OpenVPN sẽ ghi nhớ các cặp địa chỉ IP và máy khách vào tệp `/var/log/openvpn/ipp.txt`, để khi cùng một máy khách kết nối lại, nó sẽ được chỉ định cùng một địa chỉ IP cố định.

## Cấu hình bảo mật kênh điều khiển TLS

Ta sẽ cấu hình thêm một khóa bí mật dùng chung mà máy chủ và tất cả máy khách sẽ sử dụng với chỉ thị `tls-crypt` của OpenVPN. Chỉ thị này được sử dụng để kích hoạt tính năng bảo vệ bắt tay TLS [HMAC](https://en.wikipedia.org//wiki/HMAC), xáo trộn chứng chỉ TLS được sử dụng khi máy chủ và máy khách kết nối với nhau ban đầu. Nó cũng được máy chủ OpenVPN sử dụng để thực hiện kiểm tra nhanh các gói đến: nếu một gói được ký bằng khóa thì máy chủ sẽ xử lý nó; nếu nó chưa được ký thì máy chủ sẽ biết nó đến từ một nguồn không đáng tin cậy và có thể loại bỏ nó mà không cần phải thực hiện thêm công việc giải mã.

Mục đích của việc cấu hình bảo vệ bắt tay TLS HMAC:

- Có lớp bảo mật bổ sung.
- Là một phương pháp đối phó với lưu lượng truy cập không được xác thực, quét cổng và các cuộc tấn công từ chối dịch vụ

Để tạo khóa bí mật chia sẻ cho `tls-crypt`, hãy chạy câu lệnh sau:

```sh
openvpn --genkey secret /etc/openvpn/server/tc.key
```

Sau đó cấu hình cho máy chủ OpenVPN sử dụng khóa bí mật này cùng với thuật toán tiêu hóa tin nhắn HMAC (ở dưới là SHA512) bằng cách thêm các dòng chỉ thị mới vào `/etc/openvpn/server/server.conf`:

```
tls-crypt tc.key
auth SHA512
```

Cấu hình này sẽ thay thế chỉ thị `tls-auth` và `cipher` đã được tắt tại phần [Cấu hình máy chủ OpenVPN](#Cấu-hình-máy-chủ-OpenVPN); máy chủ sẽ tự động thương lượng chế độ thuật toán mã hóa AES-256-GCM cho `cipher` ở chế độ TLS.

## Cấu hình định tuyến

Máy chủ OpenVPN tạo giao diện mạng ảo cho các máy khách được kết nối. Để đảm bảo định tuyến thích hợp và an toàn, việc định cấu hình các quy tắc tường lửa là một điều cần làm. Các quy tắc này xác định cách thức NAT và đường truyền nào được phép vào và ra khỏi máy chủ thông qua đường hầm OpenVPN.

Trước hết, ta bật `net.ipv4.ip_forward` của kernel để cho phép máy chủ của ta có thể chuyển tiếp các gói tin đến:

```sh
echo 1 > /proc/sys/net/ipv4/ip_forward
```

Việc thay đổi trạng thái kernel qua lệnh trên chỉ mang tính tạm thời và sẽ bị mất sau khi khởi động lại hệ thống. Để cấu hình cho việc này tự động, ta tạo một tệp cấu hình `sysctl` mới sẽ được đọc và áp dụng mỗi khi hệ thống khởi động:

```sh
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-openvpn-forward.conf
```

Tiếp theo, ta cấu hình tường lửa được lưu trữ cố định bằng một tệp dịch vụ `openvpn-iptables.service` cho systemd; tệp này sẽ được đặt trong thư mục `/etc/systemd/system/`. Dịch vụ này sẽ quản lý và duy trì các quy tắc tường lửa `iptables` cho máy chủ OpenVPN:

```sh
[Unit]
Before=network.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to <public_ipv4_address>
ExecStart=/usr/sbin/iptables -I INPUT -p udp --dport 1194 -j ACCEPT
ExecStart=/usr/sbin/iptables -I FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStart=/usr/sbin/iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=/usr/sbin/iptables -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to <public_ipv4_address>
ExecStop=/usr/sbin/iptables -D INPUT -p udp --dport 1194 -j ACCEPT
ExecStop=/usr/sbin/iptables -D FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStop=/usr/sbin/iptables -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
```

Về cú pháp và ngữ nghĩa của tệp `.service` của systemd, và các lệnh `iptables` cấu hình tường lửa, hãy tham khảo các trang `man` dưới đây:

- [systemd.service.5](https://manpages.ubuntu.com/manpages/focal/en/man5/systemd.service.5.html)
- [iptables.8](https://manpages.ubuntu.com/manpages/trusty/man8/iptables.8.html)

Ta tập trung vào các lệnh `iptables` dùng để thêm/xóa các quy tắc tường lửa cho phép và định tuyến đường truyền trong phần `[Service]`, lần lượt từ trên xuống:

- Thêm quy tắc SNAT (Dịch địa chỉ mạng nguồn) dịch địa chỉ IPv4 riêng của các máy  khách thành địa chỉ IPv4 công cộng của máy chủ, cho phép chúng truy cập internet.
- Cho phép đường truyền UDP đến trên cổng 1194, cổng OpenVPN mặc định.
- Cho phép đường truyền truy cập có nguồn từ mạng con máy khách VPN (10.8.0.0/24 với cài đặt trong tài liệu hướng dẫn này) truyền ra internet.
- Cho phép các kết nối đã được thiết lập (`ESTABLISHED`) hoặc có liên quan (`RELATED`) đi qua tường lửa, đảm bảo liên lạc hai chiều thích hợp trong đường hầm VPN.

Khi dịch vụ dừng, các quy tắc được thêm vào lúc dịch vụ bắt đầu sẽ bị xóa đi, loại bỏ cấu hình tường lửa.

Sau khi viết xong tệp dịch vụ cấu hình tường lửa, ta `enable` và `start` nó cùng với dịch vụ `openvpn-server@server.service` để khởi chạy máy chủ OpenVPN:

```sh
systemctl enable --now openvpn-iptables.service
systemctl enable --now openvpn-server@server.service
```

## Tạo các tệp cấu hình OpenVPN cho máy khách

Bây giờ cấu hình máy chủ đã hoàn tất và các chứng chỉ và khoá bí mật cho khách đã được tạo ở bước [Tạo chứng chỉ và khóa bí mật cho máy khách](#Các-máy-khách), ta sẽ tạo các tệp cấu hình `.ovpn` riêng cho từng máy khách. Các tệp này chứa các chỉ thị cần thiết để phần mềm OpenVPN của máy khách kết nối với máy chủ.

Để đơn giản hóa việc tạo cấu hình máy khách, ta sẽ tạo tệp mẫu có tên `client-common.txt` ở trong thư mục `/etc/openvpn/server`. Tệp này lưu trữ các chỉ thị OpenVPN chung cho tất cả các cấu hình máy khách, 

> **Lưu ý**
>
> [Tương tự như `server.conf`](#Cấu-hình-máy-chủ-OpenVPN), một tệp cấu hình mẫu có đi kèm chú thích của các chỉ thị cho máy khách cũng được cung cấp tại `/usr/share/doc/openvpn/examples/sample-config-files/client.conf`, và có thể được sao chép vào `client-common.txt` và chỉnh sửa cho bao gồm những chỉ thị được bật dưới đây.

```
client
dev tun
proto udp
remote <vps_public_ipv4> 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth SHA512
ignore-unknown-option block-outside-dns
verb 3
```

Một số chỉ thị quan trọng:

- `client`: Chỉ định chế độ client cho OpenVPN.
- `remote <vps_public_ipv4> 1194`: Xác định địa chỉ máy chủ và cổng (mặc định 1194 cho OpenVPN như ta đã [cấu hình tường lửa ở trên](#Cấu-hình-tường-lửa)).
- `resolv-retry infinite`: Cố gắng lấy thông tin DNS từ máy chủ vô thời hạn.
- `nobind`: Các máy khách kết nối tới máy chủ sẽ không chủ động gán đường truyền của mình với một địa chỉ và cổng cụ thể, mà chồng giao thức IP sẽ phân bổ một cổng động.
- `remote-cert-tls server`: Xác minh chứng chỉ của máy chủ qua đường truyền TLS.
- `auth SHA512`: Sử dụng thuật toán tiêu hóa tin nhắn SHA512 cho xác thực TLS HMAC.
- `ignore-unknown-option block-outside-dns`: Bỏ qua tùy chọn `block-outside-dns` nếu không được nhận dạng (các máy khách không chạy Windows).

Để tạo thành một tệp `.ovpn` hoàn chỉnh cho một máy khách, ta sẽ phải nhúng thêm nội dung chứng chỉ của CA (`ca.crt`), chứng chỉ `.crt` và khoá bí mật `.key` của máy khách, và khoá chung dành cho [bảo mật kênh TLS mà ta đã cấu hình ở bước trước](#Cấu-hình-bảo-mật-kênh-điều-khiển-TLS). Ta tiếp tục tạo một tập lệnh (script) shell `new_client.sh` trong thư mục `/etc/openvpn/server` để tự động hoá điều này:

```bash
#!/bin/bash

# Check if a client name argument is provided
if [ $# -eq 0 ]; then
  echo "Error: Please provide a client name as an argument. (e.g. client1, client2,...)"
  exit 1
fi

client="$1"

# Generate the new client.ovpn configuration
new_client() {
        {
                cat /etc/openvpn/server/client-common.txt
                echo "<ca>"
                cat /etc/openvpn/server/easy-rsa/pki/ca.crt
                echo "</ca>"
                echo "<cert>"
                sed -ne '/BEGIN CERTIFICATE/,$ p' /etc/openvpn/server/easy-rsa/pki/issued/"$client".crt
                echo "</cert>"
                echo "<key>"
                cat /etc/openvpn/server/easy-rsa/pki/private/"$client".key
                echo "</key>"
                echo "<tls-crypt>"
                sed -ne '/BEGIN OpenVPN Static key/,$ p' /etc/openvpn/server/tc.key
                echo "</tls-crypt>"
        } > ~/"$client".ovpn
}

new_client "$client"

echo
echo "New OpenVPN client configuration created for: $client"
echo
echo "The client configuration is available in:" ~/"$client.ovpn"
echo "New clients can be added by running this script again."
echo
```

Tập lệnh nhận một tham số duy nhất là tên máy khách (phải cùng tên với máy khách đã được [tạo chứng chỉ và khoá bí mật](#Các-máy-khách)) và chèn các nội dung cần nhúng vào vào giữa các thẻ thích hợp (`<ca>`, `<cert>`,...) trong tệp cấu hình. Cuối cùng, tập lệnh ghi cấu hình hoàn chỉnh vào tệp `.ovpn` mới được đặt tên theo máy khách (`$client.ovpn`) vào thư mục home của người dùng (trong trường hợp này, home của `root` là `/root/`.

> ***Lưu ý***
> 
> Các tệp lệnh `.sh` như này cần được đánh quyền execute (`chmod +x`) trước khi chúng có thể được chạy.

Để tự động tạo ra nhiều tệp cấu hình, một vòng lặp có thể được sử dụng:

```bash
for CLIENT_NUM in {1..10}; do ./new_client.sh "client$CLIENT_NUM"; done
```

## Cài đặt cấu hình OpenVPN tại máy khách

Sau khi các tệp `.ovpn` của máy khách được tạo, ta có thể tải các tệp cấu hình xuống máy và cài đặt phần mềm OpenVPN cho máy khách (nếu chưa cài) để chuẩn bị cho việc kiểm tra kết nối.

Để tải xuống các tệp cấu hình, trước hết ta đóng kết nối SSH hiện tại bằng lệnh `exit` và kết nối với máy chủ qua giao thức [SFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol):

- **Linux/macOS**
    ```bash
    sftp <user>@<remote_ipv4>
    ```
- **Windows:** Sử dụng ứng dụng khách SFTP chuyên dụng như [FileZilla](https://filezilla-project.org/) hoặc [WinSCP](https://winscp.net/eng/index.php). Định cấu hình chi tiết kết nối (IPv4 máy chủ, tên người dùng, mật khẩu) và thiết lập kết nối SFTP.

Trong tài liệu này, ta vẫn sẽ kết nối vào người dùng `root` trên máy chủ. Sau khi kết nối, ta lập tức có thể sử dụng lệnh `get` để tải các tệp `.ovpn` có trong thư mục home của người dùng về máy. Ví dụ, để tải xuống tệp có tên `client1.ovpn`:

```sh
get client1.ovpn
```

Lặp lại lệnh cho từng tệp cấu hình máy khách mà cần được tải xuống, hoặc sử dụng câu lệnh `sftp` gợi ý sau để tải về tất cả các tệp `.ovpn`:

```sh
mget *.ovpn
```

Sau đó, nhập lệnh `exit` để ngắt kết nối khỏi phiên SFTP.

Dưới đây là hướng dẫn chung, kèm theo hướng dẫn cụ thể để cài đặt ứng dụng khách OpenVPN cho các hệ điều hành và bản phân phối Linux phổ biến:

- **Ubuntu/Debian**
    ```bash
    sudo apt update
    sudo apt install openvpn
    ```
- **RHEL/Fedora**
    ```bash
    sudo dnf update
    sudo dnf install openvpn
    ```
- **Arch Linux**
    ```bash
    sudo pacman -Syu
    sudo pacman -S openvpn
    ```
- **Windows**
    - Tải xuống trình cài đặt OpenVPN [từ trang web chính thức](https://openvpn.net/community-downloads/).
    - Chạy trình cài đặt đã tải xuống và làm theo hướng dẫn trên màn hình.

- **macOS**
    - Có thể cài đặt OpenVPN bằng [Homebrew](https://brew.sh/), một trình quản lý gói (package manager) cho macOS và Linux. Mở một cửa sổ dòng lệnh và chạy:
        ```bash
        /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
        ```
    - Làm theo hướng dẫn trên màn hình để hoàn tất cài đặt Homebrew.
    - Sau khi cài đặt Homebrew, chạy lệnh sau để cài đặt OpenVPN:
        ```bash
        brew install openvpn
        ```

Khi đã cài đặt OpenVPN, tài liệu phần mềm máy khách OpenVPN cụ thể cho từng hệ điều hành cần được tham khảo để biết hướng dẫn nhập tệp cấu hình `.ovpn` đã tải xuống.

## Kiểm thử

Phần này ghi lại phương pháp, quá trình và kết quả của cuộc kiểm tra được tiến hành để xác nhận cấu hình máy chủ và các máy khách OpenVPN đạt được các yêu cầu:

- Máy khách kết nối được với máy chủ (VPS) OpenVPN qua tất cả các tệp cấu hình `.ovpn` được tạo
- Hai máy A và B cùng kết nối với VPN trong cùng một thời điểm có thể "nhìn" thấy nhau và có thể truy cập các ứng dụng/dịch vụ đang được host trên máy của nhau.
    - A ping được tới B và ngược lại, với tỷ lệ mất gói tin (packet loss) $\le$ 0%.
    - A có thể truy cập các ứng dụng/dịch vụ đang được host trên máy B và ngược lại thông qua việc truy cập cổng mà ứng dụng đang chạy trên địa chỉ IPv4 trên subnet của VPN (ví dụ, `http://10.8.0.2:8000`)

Môi trường thử nghiệm sẽ gồm 2 máy:

- Một máy Linux (Arch Linux) sử dụng systemd (Máy A)
- Một máy ảo KVM Windows 10 chạy trên QEMU (Máy B)

Các giả định môi trường trước khi kiểm chứng:

- Các tệp cấu hình máy khách (`*.ovpn`) và cài đặt phần mềm máy khách OpenVPN trên cả hai máy (tham khảo phần [Cài đặt cấu hình OpenVPN tại máy khách](#Cài-đặt-cấu-hình-OpenVPN-tại-máy-khách).
- Các hệ điều hành đã được cập nhật với các bản cập nhật mới nhất (Windows) và các gói phần mềm mới nhất (Linux) của nhà phân phối.
- Tường lửa trên các máy khách đã được điều chỉnh để cho phép liên lạc trên các cổng cụ thể cho ứng dụng/dịch vụ (cần thiết cho bước [Xác minh kết nối giữa các máy khách trên mạng VPN](#Xác-minh-kết-nối-giữa-các-máy-khách-trên-mạng-VPN).
    - Có thể không cần thiết nếu theo sát các bước thực hiện kiểm thử, vì tài liệu sẽ chỉ dùng cổng 80 - cổng mà HTTP chạy và khả năng cao không bị chặn.


### Xác minh kết nối VPN hoạt động với các chức năng cơ bản

**Mục tiêu thử nghiệm:** Xác nhận rằng mỗi máy khách có thể kết nối với máy chủ OpenVPN bằng tệp `.ovpn` tương ứng và các chức năng cơ bản như kết nối được ra ngoài Internet hoạt động và phân giải được địa chỉ web.

**Thủ tục:**

- Trên Máy A và B, khởi chạy phần mềm máy khách OpenVPN và nhập các tệp `.ovpn` tương ứng cho mỗi máy. (Tham khảo tài liệu phần mềm máy khách để biết hướng dẫn nhập cụ thể.)
- Bắt đầu kết nối VPN trên cả hai máy.
- Mở chương trình dòng lệnh cho mỗi máy và thực hiện lệnh ping `google.com`
    ```bash
    ping -4c 10 google.com
    ```

**Kết quả:**

- Phần mềm máy khách OpenVPN sẽ thiết lập thành công kết nối trên cả hai máy. Nhật ký (log trên ứng dụng OpenVPN của Windows và `journalctl` trên máy Linux) cung cấp chi tiết kết nối như địa chỉ IPv4 được chỉ định trên mạng con VPN.
- Các lệnh ping đến `google.com` hiển thị các phản hồi thành công với tỷ lệ mất gói là 0%. Điều này xác nhận các chức năng cơ bản khi kết nối VPN, bao gồm kết nối Internet ổn định và phân giải tên miền.

### Xác minh kết nối giữa các máy khách trên mạng VPN

**Mục tiêu thử nghiệm:**

- Xác minh các máy được kết nối qua VPN có thể kết nối với nhau qua địa chỉ IPv4 được chỉ định của nhau trên mạng con ảo của VPN.
- Đảm bảo các máy có thể truy cập các ứng dụng/dịch vụ chạy trên mạng của nhau.

**Thủ tục:**

- Xác định địa chỉ IP được chỉ định:
    - Trên Máy A (Linux): Sử dụng lệnh dưới đây để lọc ra các dòng nhật ký cung cấp thông tin về IPv4 được chỉ định của máy:
        ```bash
        journalctl | grep "openvpn" | grep -o "10\.8\.0\.[0-9]" | tail -1
        ```
        Lệnh này sẽ in ra địa chỉ IPv4 được gán của máy trên mạng con VPN trong lần gần nhất máy kết nối VPN.
    - Trên Máy B (Windows): Đọc nhật ký log trên ứng dụng OpenVPN để biết thông tin về địa chỉ IP được chỉ định.
- Trên Máy A, mở cửa sổ dòng lệnh và chạy lệnh sau, thay thế `<client_b_ip>` bằng địa chỉ IPv4 được gán cho Máy B trên mạng VPN (ví dụ: `ping -4c 10 10.8.0.3`):
    ```
    ping -4c 10 <client_b_ip>
    ```
- Lặp lại kiểm tra ping từ Máy B sang Máy A
- Thiết lập ứng dụng đơn giản (trên Máy A). Ở đây, ứng dụng được thiết lập là ứng dụng Django mặc định cơ bản, và cuộc thử nghiệm này giả định rằng người triển khai có [kiến thức cơ bản về thiết lập và chạy ứng dụng này](https://www.djangoproject.com/start).
- Chạy ứng dụng nghe trên 0.0.0.0:80.
    > ***Lưu ý***
    > 
    > - Không chạy ứng dụng nghe trên địa chỉ loopback 127.0.0.1, vì với địa chỉ đó, không máy nào trong cùng mạng con với máy host có thể truy cập ứng dụng.
    > - Ứng dụng có thể nghe trên bất cứ cổng nào. Tài liệu này chọn cổng 80 vì đây là cổng mà dịch vụ HTTP chạy, nên thường mặc định không bị chặn và không cần phải mở trên tường lửa. Tuy nhiên, khi chạy ứng dụng trên máy Linux, sẽ có một nhược điểm là các cổng trong khoảng 0-1023 chỉ có thể được mở bởi người dùng `root`, vì vậy ứng dụng sẽ phải được chạy với lệnh `sudo` (hoặc bất cứ lệnh thay thế nào).
    > - Nếu nghe trên cổng khác, người triển khai sẽ phải mở cổng tương ứng trên cấu hình tường lửa của máy.
- Trên Máy B, mở trình duyệt web và điều hướng đến URL sau (thay thế `<client_a_ip>` bằng địa chỉ IP được gán cho Máy A trên mạng con VPN):
    ```
    http://<client_a_ip>:80
    ```

**Kết quả:**

- Các lệnh ping hiển thị các phản hồi thành công với tỷ lệ mất gói 0% và không bị định tuyến lại (reroute) tại Gateway 10.8.0.1 trên máy chủ. Điều này xác nhận kết nối cơ bản, trực tiếp giữa các máy trên mạng VPN.
- Trình duyệt web trên Máy B sẽ hiển thị thành công nội dung của ứng dụng Django đang chạy trên Máy A. Điều này xác minh khả năng truy cập các ứng dụng/dịch vụ được host bởi các máy khách qua địa chỉ IPv4 được chỉ định trong mạng VPN.

## Changelog

| Phiên bản | Nội dung thay đổi |
| --------- | ----------------- |
| 1.0.0     | Phiên bản hoàn thiện đầu tiên. |
| 1.1.0     | Thêm gợi ý hướng dẫn chỉnh danh sách mirror cho `apt`. |
| 1.1.1     | Chỉnh sửa lại một số câu lệnh bị sai và/hoặc thừa. |
| 1.2.0     | Thêm lưu ý về tính chất tham khảo của tài liệu. |
| 1.2.1     | Thêm lưu ý nhỏ về việc chạy các tệp lệnh `.sh`. |
| 1.2.2     | Chỉnh sửa lại các câu lệnh bị sai khác. |
| 1.2.3     | Thêm các lệnh khởi chạy dịch vụ systemd bị thiếu. |
| 1.2.4     | Thêm lệnh gợi ý để tải nhiều tệp `.ovpn` qua SFTP. |'
| 1.3.0     | Bổ sung thông tin cấu hình bị thiếu về `net.ipv4.ip_forward` |