Cài Đặt OpenVPN Server trên Ubuntu với Google Authenticator 2FA và Easy-RSA PKI

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

Vấn Đề Thực Tế: Lỗ Hổng Khi VPN Chỉ Dùng Chứng Chỉ Số

Năm ngoái, đội ngũ kỹ thuật của chúng tôi mở rộng từ 12 lên 45 thành viên trải dài trên ba múi giờ khác nhau. Các lập trình viên, quản trị viên cơ sở dữ liệu và cộng tác viên đều cần quyền truy cập 24/7 vào môi trường staging cùng các cụm PostgreSQL nội bộ trên Ubuntu. Giống như nhiều đội ngũ đang phát triển khác, ban đầu chúng tôi triển khai một OpenVPN server tiêu chuẩn và bảo mật duy nhất bằng client certificate cấp qua Easy-RSA.

Hồi chuông cảnh tỉnh vang lên khi một kỹ sư bỏ quên chiếc ThinkPad của công ty tại một nhà ga sân bay đông đúc. Rất may laptop đã bật mã hóa toàn bộ ổ đĩa LUKS nên dữ liệu không bị lộ. Tuy nhiên, sự cố này đã kích hoạt một cuộc đánh giá bảo mật nội bộ ngay lập tức. Những gì chúng tôi phát hiện ra thực sự đáng báo động: bốn lập trình viên thường xuyên sao lưu file cấu hình .ovpn sang USB cá nhân không mã hóa. Tệ hơn nữa, một vài client key còn để trống mật khẩu (passphrase) nhằm tránh phải nhập mật khẩu lặp đi lặp lại.

Cơ chế xác thực chỉ dựa vào certificate hoạt động dựa trên một giả định đơn giản: bất kỳ ai nắm giữ certificate đều là người dùng hợp lệ. Nếu kẻ tấn công trích xuất được private key không mã hóa từ một endpoint bị xâm nhập, vành đai bảo mật của bạn coi như biến mất. Sẽ không có bước xác thực thứ hai nào ngăn cản họ, và họ có thể truy cập tức thì vào các subnet nội bộ. Chúng tôi cần một giải pháp kết hợp giữa danh tính thiết bị qua mật mã học và một yếu tố xác thực thứ hai độc lập, tạm thời.

Phân Tích Nguyên Nhân Gốc Rễ: Vì Sao VPN Tiêu Chuẩn Thất Bại Khi Mở Rộng Quy Mô

Quá trình đánh giá sau sự cố đã chỉ ra ba điểm yếu cốt lõi thường gặp ở các hệ thống OpenVPN thông thường:

  • Lộ thông tin xác thực tĩnh: Một profile .ovpn duy nhất gói gọn cả CA certificate, client certificate và private key vào một file plaintext. Bất kỳ ai có được file đó đều có thể kết nối ngay lập tức.
  • Độ trễ khi thu hồi chứng chỉ: Easy-RSA có hỗ trợ Certificate Revocation List (CRL), nhưng việc thu hồi một key bị mất tốn không ít thời gian. Quản trị viên phải chạy lệnh ./easyrsa revoke, tạo lại file CRL rồi xuất bản nó. Trong sự cố của chúng tôi, đã có 14 giờ trôi qua từ lúc mất laptop đến khi đội bảo mật nhận được ticket.
  • Hiện tượng “mệt mỏi vì mật khẩu”: Việc buộc lập trình viên nhập passphrase dài 16 ký tự mỗi khi kết nối lại dẫn đến các đường tắt mất an toàn. Hai kỹ sư trong số đó đã tự động hóa việc kết nối bằng shell script chứa sẵn mật khẩu hardcode.

Các khóa mật mã tĩnh không thể bảo vệ ranh giới mạng trước nguy cơ phần cứng bị đánh cắp hay file cấu hình bị rò rỉ. Việc chỉ chứng minh quyền sở hữu một file là hoàn toàn không đủ.

So Sánh Các Giải Pháp: Tìm Điểm Cân Bằng Cho Đội Ngũ Làm Việc Từ Xa

Chúng tôi đã dành bốn tuần để đánh giá ba kiến trúc thay thế trên một nhóm thử nghiệm gồm 10 kỹ sư.

1. WireGuard với Public Key Tĩnh

WireGuard mang lại hiệu năng rất ấn tượng. Trong các bài benchmark của chúng tôi, thông lượng đạt tới 920 Mbps trên đường truyền 1 Gbps với mức sử dụng CPU không đáng kể. Tuy nhiên, WireGuard chủ ý lược bỏ cơ chế tương tác dạng challenge-response. Việc tích hợp mã xác thực dùng một lần theo thời gian (TOTP) đòi hỏi các script port-knocking tùy biến hoặc daemon sidecar can thiệp trực tiếp vào iptables. Chúng tôi không muốn dựa vào các script tự viết kém ổn định để bảo vệ vành đai hệ thống.

2. Các Nền Tảng Zero-Trust Thương Mại (Tailscale, Cloudflare Zero Trust)

Các nền tảng SASE quản lý qua đám mây giúp việc thiết lập xác thực đa yếu tố trở nên vô cùng nhẹ nhàng thông qua các nhà cung cấp định danh như Okta hay Google Workspace. Tuy nhiên, chi phí thuê bao dao động từ 5$ đến 15$ cho mỗi người dùng mỗi tháng. Với quy mô 45 kỹ sư và 15 cộng tác viên, con số này lên tới 9.000$ mỗi năm. Thêm vào đó, các yêu cầu tuân thủ SOC 2 và HIPAA không cho phép chúng tôi định tuyến luồng dữ liệu bệnh nhân nhạy cảm qua control plane của bên thứ ba.

3. OpenVPN với Easy-RSA PKI và PAM Google Authenticator

OpenVPN tích hợp nguyên bản với Linux Pluggable Authentication Modules thông qua thư viện openvpn-plugin-auth-pam.so. Kiến trúc này xác minh hai yếu tố độc lập trong mỗi lần kết nối: đầu tiên là kiểm tra thông tin thiết bị qua Easy-RSA client certificate, sau đó kiểm tra mật khẩu Linux của người dùng đi kèm mã 6 chữ số thay đổi mỗi 30 giây từ Google Authenticator.

Một file .ovpn bị đánh cắp sẽ trở nên hoàn toàn vô dụng nếu đứng riêng lẻ. Kẻ xâm nhập vẫn cần mật khẩu tài khoản của kỹ sư và chiếc điện thoại vật lý của họ. Tuyệt vời nhất là toàn bộ giải pháp này chạy mượt mà trên hạ tầng Ubuntu thông thường mà không tốn chi phí bản quyền theo từng người dùng.

Giải Pháp Tối Ưu: Thiết Lập Môi Trường Production Trên Ubuntu 22.04 và 24.04 LTS

Chúng tôi đã triển khai kiến trúc này trên một máy ảo Ubuntu 22.04 LTS (2 vCPU, 4 GB RAM) phục vụ khoảng 35 kết nối đồng thời. Tải máy chủ hiếm khi vượt quá 12% và độ trễ bắt tay trung bình luôn dưới 1,5 giây. Dưới đây là các bước cấu hình chi tiết.

Bước 1: Cài Đặt Các Gói Cần Thiết

Cập nhật kho lưu trữ apt và cài đặt OpenVPN, Easy-RSA, module PAM Google Authenticator cùng các công cụ iptables:

sudo apt update && sudo apt install -y openvpn easy-rsa libpam-google-authenticator iptables iptables-persistent

Bước 2: Khởi Tạo Easy-RSA PKI

Tạo một thư mục riêng cho Certificate Authority (CA) và khởi tạo các thành phần mật mã phía server:

make-cadir ~/openvpn-ca
cd ~/openvpn-ca
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa gen-dh
./easyrsa build-server-full server nopass
openvpn --genkey secret ta.key

Sao chép CA certificate, tham số Diffie-Hellman, server key và TLS authentication key vừa tạo vào thư mục cấu hình OpenVPN server:

sudo cp pki/ca.crt pki/dh.pem pki/issued/server.crt pki/private/server.key ta.key /etc/openvpn/server/

Bước 3: Cấu Hình Linux PAM Cho 2FA

Tạo file cấu hình PAM chuyên dụng cho OpenVPN:

sudo nano /etc/pam.d/openvpn

Thêm ba dòng cấu hình sau:

auth required pam_google_authenticator.so forward_pass
auth required pam_unix.so use_first_pass
account required pam_unix.so

Tham số forward_pass cho phép nhập mật khẩu và mã OTP trong cùng một chuỗi. Khi người dùng gửi mật khẩu kèm mã TOTP 6 chữ số gắn liền nhau, PAM sẽ tách lấy mã token để xác thực rồi chuyển mật khẩu gốc cho pam_unix.so.

Bước 4: Cấu Hình Daemon OpenVPN Server

Lưu file cấu hình daemon chính tại /etc/openvpn/server/server.conf:

port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
keepalive 10 120
cipher AES-256-GCM
auth SHA256
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3
explicit-exit-notify 1

# Tích hợp PAM xác thực hai yếu tố
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
verify-client-cert require
username-as-common-name
reneg-sec 0

Chỉ thị username-as-common-name thiết lập một quy tắc an ninh quan trọng: tên đăng nhập phải trùng khớp hoàn toàn với Common Name được nhúng trong client certificate. Thiết lập reneg-sec 0 giúp tắt tính năng đàm phán lại key mỗi giờ, tránh tình trạng phiên kết nối bị ngắt quãng giữa chừng khi đang làm việc.

Bước 5: Kích Hoạt IP Forwarding và NAT Masquerading

Bật tính năng định tuyến gói tin IPv4 trong sysctl:

sudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
sudo sysctl -p

Xác định card mạng internet chính bằng lệnh ip route show default (thường là eth0 hoặc ens3). Áp dụng quy tắc định tuyến NAT và khởi chạy dịch vụ OpenVPN:

sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo netfilter-persistent save
sudo systemctl enable --now openvpn-server@server

Bước 6: Khởi Tạo Tài Khoản Người Dùng và Khóa TOTP

Với mỗi kỹ sư, hãy tạo một user hệ thống không có quyền truy cập shell tương tác:

sudo useradd -m -s /usr/sbin/nologin devuser1
sudo passwd devuser1

Khởi tạo cấu hình Google Authenticator dưới quyền tài khoản đó:

sudo -u devuser1 google-authenticator -t -d -f -r 3 -R 30 -W

Lệnh này cấu hình token theo thời gian, chống tái sử dụng token, giới hạn brute force ở mức tối đa 3 lần thử trong 30 giây, và lưu file seed vào ~/.google_authenticator. Hiển thị mã QR kết quả cho kỹ sư quét vào ứng dụng xác thực trên điện thoại. Hãy lưu lại 5 mã khẩn cấp (scratch codes) vào trình quản lý mật khẩu của công ty.

Bước 7: Tạo Profile Cấu Hình Client

Tạo client certificate riêng biệt có Common Name khớp chính xác với username hệ thống:

cd ~/openvpn-ca
./easyrsa build-client-full devuser1 nopass

Tổng hợp file client (devuser1.ovpn) với các khối mã hóa được nhúng trực tiếp:

client
dev tun
proto udp
remote 203.0.113.10 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA256
key-direction 1

# Yêu cầu nhập thông tin xác thực tương tác
auth-user-pass
reneg-sec 0

<ca>
# Dán nội dung /etc/openvpn/server/ca.crt vào đây
</ca>
<cert>
# Dán nội dung ~/openvpn-ca/pki/issued/devuser1.crt vào đây
</cert>
<key>
# Dán nội dung ~/openvpn-ca/pki/private/devuser1.key vào đây
</key>
<tls-auth>
# Dán nội dung /etc/openvpn/server/ta.key vào đây
</tls-auth>

Bước 8: Vận Hành Hàng Ngày và Xác Thực Phía Client

Khi kết nối qua OpenVPN GUI, Tunnelblick hoặc giao diện dòng lệnh, người dùng sẽ thấy hộp thoại đăng nhập quen thuộc. Tại ô username, người dùng nhập devuser1. Tại ô password, họ nhập mật khẩu tài khoản Unix nối liền trực tiếp với mã TOTP 6 chữ số (ví dụ: SecretPassphrase839201).

Trong suốt sáu tháng qua, cấu hình này đã phục vụ hơn 4.200 phiên kết nối riêng biệt mà không gặp bất kỳ sự cố rớt mạng bất thường nào. Việc thất lạc thiết bị không còn là tình huống khẩn cấp đe dọa an ninh. Ngay cả khi kẻ tấn công có được toàn bộ profile .ovpn, mạng nội bộ của bạn vẫn an toàn tuyệt đối nếu không có thiết bị xác thực vật lý đi kèm.

Share: