Host onion site trên VPS với Tor và nginx
Chạy onion service v3 trên Ubuntu bằng Tor và nginx bind vào loopback. Hướng dẫn xử lý lỗi rò rỉ khiến địa chỉ .onion bị liên kết với public IP.
Bạn đang xây dựng gì
Một onion site là một web server thông thường chỉ trả lời thông qua mạng Tor. Cài tor, thêm hai dòng vào /etc/tor/torrc, đọc địa chỉ mà tor ghi cho bạn, rồi bind nginx vào 127.0.0.1 để không có dịch vụ nào trả lời trên public IP. Phần cài đặt mất mười phút. Phần còn lại của hướng dẫn này là danh sách các điểm có thể làm lộ thông tin, vì onion site thường bị lỗi khi chính cấu hình của nó trỏ thẳng về operator.
Tor ban đầu có tên là “the onion router”, còn onion service là một service chỉ có thể truy cập thông qua mạng này. Địa chỉ version 3 có 56 ký tự, theo sau là .onion. Các ký tự đó là public key ed25519 của service cùng checksum và một byte version, được mã hóa bằng base32. Địa chỉ version 2 (16 ký tự) đã bị gỡ khỏi mạng vào năm 2021, nên mọi địa chỉ bạn tạo hiện nay đều là v3. Địa chỉ chính là key, và điều này có hai hệ quả. Kết nối được mã hóa và xác thực end-to-end mà không cần certificate authority, còn nếu mất file key thì bạn mất địa chỉ vĩnh viễn.
Server của bạn không bao giờ chấp nhận kết nối inbound. Tor chọn một số relay làm introduction point, upload descriptor đã ký lên directory server, rồi gặp từng visitor tại một rendezvous relay do visitor chọn. Tất cả các kết nối đó đều là outbound từ máy chủ của bạn. Bạn không cần mở port và cũng không cần publish bản ghi DNS.
Cài đặt tor từ repository của Tor Project
Ubuntu phát hành gói tor trong component universe, nhưng gói này thường vẫn gần với phiên bản tại thời điểm bản phát hành được đóng băng. Repository riêng của Tor Project theo sát bản stable hiện tại. Đây là lựa chọn phù hợp cho phần mềm quyết định địa chỉ của bạn có còn thuộc về bạn hay không.
sudo apt update
sudo apt install -y apt-transport-https gnupg wget
KEYURL=https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
wget -qO- "$KEYURL" | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/nullMục repository dùng định dạng deb822, và Suites phải là codename của Ubuntu. Hãy đọc giá trị này từ /etc/os-release thay vì tự nhập, vì codename sai vẫn có thể tạo một repository phân giải bình thường nhưng không có package cho bản phát hành của bạn.
. /etc/os-release
sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null <<EOF
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: $VERSION_CODENAME
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
EOF
sudo apt update
sudo apt install -y tor deb.torproject.org-keyringGói deb.torproject.org-keyring tự cập nhật signing key, nên việc thay key sẽ không làm apt update bị hỏng sau một năm. Kiểm tra xem tor đã khởi động và kết nối được đến mạng chưa:
tor --version
sudo journalctl -u tor@default -n 20Journal phải kết thúc bằng Bootstrapped 100% (done): Done. tor bị kẹt ở Bootstrapped 10% không có đường đi outbound, vì vậy hãy kiểm tra network firewall của nhà cung cấp và các egress rule của bạn: sudo ufw status verbose phải hiển thị allow (outgoing) là mặc định.
Từ đây có hai tên cần nhớ. Package chạy tor dưới user debian-tor, còn unit đang chạy là tor@default.service, vì tor.service trên Debian và Ubuntu là wrapper cho instance. Hãy yêu cầu status và log theo tên instance để luôn nhận đúng tiến trình thực tế.
Cấu hình onion service trong torrc
Thêm 2 dòng vào /etc/tor/torrc:
HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080HiddenServiceDir là nơi Tor lưu các key và địa chỉ của service này. Không tự tạo thư mục này. Tor sẽ tạo nó khi khởi động, với owner và mode phù hợp. Nếu bạn tạo thư mục bằng root, lỗi đầu tiên trong danh sách lỗi bên dưới sẽ xảy ra.
HiddenServicePort gồm 2 phần. Nhầm lẫn 2 phần này là lỗi đầu tiên thường gặp. Số đầu tiên là cổng mà client kết nối đến bên trong tunnel. Vì vậy, 80 là giá trị thường dùng và không cần thay đổi. Phần thứ hai là địa chỉ cục bộ mà Tor chuyển tiếp lưu lượng đến. HiddenServicePort 80 không kèm địa chỉ sẽ chuyển tiếp đến 127.0.0.1:80. Vì vậy, ghi rõ địa chỉ và dùng một cổng cao sẽ giúp onion vhost không xung đột với dịch vụ đang listen trên cổng 80.
sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/Kết quả liệt kê phải có hostname, hs_ed25519_public_key, hs_ed25519_secret_key và một thư mục authorized_clients trống.
Đọc địa chỉ .onion của bạn
sudo cat /var/lib/tor/onion_site/hostnameMột dòng được trả về: 56 ký tự base32 và .onion. Chuỗi đó là toàn bộ danh tính của site. Không ai cấp chuỗi này, không ai có thể chuyển nhượng, và không ai có thể lấy nó khỏi bạn khi bạn còn giữ file key. Hãy sao chép ngay, vì mọi cấu hình bên dưới đều cần chuỗi này. Phần còn lại của hướng dẫn này viết chuỗi đó là <your-address>.onion.
Phục vụ site bằng nginx bind vào 127.0.0.1
sudo apt install -y nginx
sudo install -d -m 755 /srv/onionGhi /etc/nginx/sites-available/onion:
server {
listen 127.0.0.1:8080;
server_name <your-address>.onion;
root /srv/onion;
index index.html;
server_tokens off;
etag off;
access_log off;
error_log /var/log/nginx/onion.error.log error;
}echo '<h1>hello from the onion</h1>' | sudo tee /srv/onion/index.html
sudo ln -s /etc/nginx/sites-available/onion /etc/nginx/sites-enabled/onion
sudo nginx -t
sudo systemctl reload nginxBây giờ xác minh 2 điều từ server. Thứ nhất, nginx trả lời cho onion name, đây là header Host chính xác mà tor sẽ gửi:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/Thứ hai, nginx chỉ trả lời tại địa chỉ đó và không ở nơi nào khác:
sudo ss -tlnp | grep 8080Cột địa chỉ phải hiển thị 127.0.0.1:8080. Nếu hiển thị 0.0.0.0:8080 hoặc *:8080, onion site của bạn cũng đang được public trên Internet. Đây là mục đầu tiên trong danh sách rò rỉ. Dòng listen 8080; không có địa chỉ sẽ bind trên mọi interface, và đó là giá trị mặc định.
Mở địa chỉ này bằng Tor Browser. Lần tải đầu tiên mất vài giây vì client phải lấy descriptor của bạn và dựng một rendezvous circuit.
Tài liệu chính thức của Tor Project ưu tiên dùng unix socket thay cho loopback port: HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, trong đó nginx lắng nghe trên path đó. Một socket hoàn toàn không thể được truy cập từ host khác, kể cả khi sau này máy có thêm interface thứ hai. Đổi lại, bạn phải xử lý quyền truy cập file, vì nginx tạo socket còn tor kết nối đến socket đó dưới user debian-tor. Hai user này phải cùng có quyền truy cập directory. Dùng loopback với output ss đã xác minh dễ cấu hình đúng hơn, và phần còn lại của guide này giả định bạn dùng cách đó.
Khi site chỉ chạy trên loopback, box không cần inbound rule nào cho site này. Giữ port 22 để bạn truy cập và deny các port còn lại (các ufw default nên đặt trên VPS). Lưu ý rằng firewall không thể vô hiệu hóa service bind vào 0.0.0.0. Firewall chỉ lọc các packet đi đến firewall. Container làm vấn đề này nghiêm trọng hơn, vì publish Docker port sẽ ghi rule iptables trước ufw, nên -p 8080:80 sẽ đặt onion backend trên public IP trong khi ufw vẫn báo port đó bị deny. Hãy publish port của container bằng -p 127.0.0.1:8080:80.
Các rò rỉ làm mất tính ẩn danh của onion site
Tor che giấu vị trí của server. Tor không che giấu nội dung server gửi đi. Mỗi mục dưới đây đều là thông tin do chính stack của bạn công khai.
Cùng một site phản hồi trên public IP của bạn
Đây là lỗi khiến nhiều người bị lộ. Các scanner liên tục index HTTP response của mọi địa chỉ có thể định tuyến, và những kết quả đó đều công khai, có thể tìm kiếm. Nếu bạn phục vụ cùng một trang trên public IP và onion address, việc liên kết hai địa chỉ chỉ cần một truy vấn: cùng title, cùng favicon hash, cùng ETag, cùng thứ tự header. Dòng listen 127.0.0.1:8080; ở trên là cách sửa. Hãy kiểm tra từ một máy khác, không kiểm tra từ chính server:
curl -sv --max-time 5 http://<your-public-ip>:8080/Connection refused hoặc timeout mới là kết quả đúng. Bất kỳ HTML nào cũng có nghĩa là site đang public. Nếu máy này cũng chạy một clearnet site, hãy đặt root riêng cho vhost đó và giữ một block default_server rõ ràng trên public listener, để header Host không khớp không bao giờ rơi xuống onion vhost.
Banner phiên bản
curl -sI http://127.0.0.1:8080/ | grep -i '^server'nginx mặc định trả về Server: nginx/1.24.0. Chuỗi phiên bản đó, cùng với thứ tự chính xác của các header khác, tạo thành fingerprint có thể dùng để đối chiếu onion với clearnet host của bạn. server_tokens off; rút gọn nó thành Server: nginx. Cấu hình này không xóa header, và nginx không có directive tích hợp để làm việc đó, nên module headers-more thường là lựa chọn phù hợp nếu bạn muốn xóa hẳn header. PHP thêm X-Powered-By cho đến khi bạn đặt expose_php = Off. etag off; cũng cần nằm trong danh sách này, vì nginx tạo ETag từ thời điểm sửa đổi và kích thước file. Do đó, cùng một file được sao chép lên hai server sẽ tạo ra cùng ETag trên cả hai server.
URL tuyệt đối trỏ đến clearnet domain của bạn
Một thẻ rel="canonical", một og:url của Open Graph, RSS feed, sitemap, email đặt lại mật khẩu hoặc URL logo hardcode. Chỉ một mục trong số đó cũng đủ nêu tên clearnet site bên trong trang được phục vụ qua onion. Hãy dùng các path tương đối với root như /static/logo.svg, và để application đọc base URL từ host trong request thay vì dùng một hằng số. Redirect cũng là cùng một lỗi nhưng ở vị trí khác: return 301 https://example.com$request_uri; trong catch-all block sẽ chuyển visitor từ onion đến domain thật của bạn, còn header Location gửi câu trả lời trực tiếp cho họ.
Certificate TLS dùng chung với clearnet site
Onion address tự xác thực vì address chính là public key, nên http:// qua kết nối onion vốn đã được mã hóa end-to-end và Tor Browser coi đó là secure context. Cài certificate hiện có lên onion vhost sẽ công khai liên kết giữa hai site, vì mọi certificate được public trust đều được ghi vào Certificate Transparency logs, và các log đó công khai, vĩnh viễn, đồng thời có thể tìm kiếm theo tên. Hãy giữ certificate Let's Encrypt trên clearnet vhost và để onion vhost chạy plain HTTP.
Font và analytics của bên thứ ba
Một font từ CDN (content delivery network) hoặc một analytics script. Browser của visitor sẽ fetch từng tài nguyên trực tiếp, nên bên thứ ba biết có người đã tải trang của bạn và thường biết cả trang nào được tải. Ngoài ra, các security level nghiêm ngặt hơn của Tor Browser cũng chặn request này, khiến layout bị hỏng. Hãy self-host mọi asset mà trang cần.
Host header không khớp
Nếu server_name không khớp với header Host mà Tor gửi, nginx sẽ chuyển sang default server của listen address đó. Trên máy chỉ có một vhost, điều này không dễ nhận thấy vì server block duy nhất cũng là default. Sau này, nếu thêm clearnet vhost, các request onion có thể bắt đầu rơi vào đó, kèm theo các canonical tag và redirect của clearnet site. Hãy chạy lại kiểm tra curl -H 'Host: ...' sau mỗi thay đổi nginx, rồi grep kết quả để tìm domain thật của bạn:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -uBiết process nào sở hữu socket nào là phần lớn công việc này (cách port và listening socket hoạt động trên Linux).
Những gì còn lại trong log
Mọi request đều đến từ 127.0.0.1, nên nginx không có địa chỉ visitor để ghi lại và access_log off; không khiến bạn mất gì. Application phía trên lại là chuyện khác, vì order, địa chỉ email hoặc metadata trong file được upload đều là dữ liệu bạn phải xử lý. Thói quen của chính bạn cũng quan trọng: quản trị máy qua một login chưa được harden nằm ngoài phạm vi bảo vệ của Tor, vì vậy hãy xem hardening SSH trên cùng VPS là một phần của cấu hình này.
Sao lưu private key vì đó là địa chỉ
/var/lib/tor/onion_site/hs_ed25519_secret_key là service. Không có registrar và không có cơ chế khôi phục. Mất private key thì mất địa chỉ. Nếu copy private key, bất kỳ ai giữ bản copy đều có thể phục vụ nội dung của họ tại địa chỉ đó, và bạn không có cách nào để thu hồi.
sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -czf onion-keys.tgz onion_site
sudo chmod 600 onion-keys.tgz
sudo systemctl start tor@defaultMã hóa archive đó (gpg -c onion-keys.tgz) rồi chuyển nó ra khỏi server. Khi khôi phục trên VPS mới, chỉ cần archive và ownership mà tor yêu cầu:
sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -xzf onion-keys.tgz
sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_site
sudo chmod 700 /var/lib/tor/onion_site
sudo systemctl start tor@default
sudo cat /var/lib/tor/onion_site/hostnameĐịa chỉ cũ sẽ hoạt động lại trên phần cứng mới sau một hoặc hai phút, khi tor publish lại descriptor. Toàn bộ quá trình migration chỉ có vậy: không cần thay đổi DNS và không cần cấp lại certificate.
Onion-Location khi website cũng có trên clearnet
Nếu onion chỉ để tiện truy cập chứ không nhằm giữ bí mật, hãy quảng bá onion từ clearnet vhost:
add_header Onion-Location http://<your-address>.onion$request_uri;Tor Browser sẽ hiển thị nút .onion available trên thanh địa chỉ và cho phép chuyển sang onion. Header này chỉ được chấp nhận khi trang clearnet được phân phối qua HTTPS và giá trị là một onion URL hợp lệ.
Có một quy tắc của nginx cần lưu ý. Các directive add_header chỉ được kế thừa vào block location khi block đó không khai báo directive riêng. Vì vậy, một location có add_header riêng sẽ âm thầm loại bỏ Onion-Location. Hãy lặp lại header này trong block đó hoặc đặt toàn bộ response header ở một nơi. Việc chủ động công bố header này liên kết hai website với nhau. Điều đó phù hợp với mirror, nhưng không phù hợp với nội dung cần giữ tách biệt.
Địa chỉ vanity
mkp224o tạo các cặp key cho đến khi có một cặp tạo ra địa chỉ bắt đầu bằng prefix bạn yêu cầu. Đây là quá trình brute force, nên không cần cấu hình gì ngoài prefix và khoảng thời gian bạn sẵn sàng chờ.
sudo apt install -y git gcc libc6-dev libsodium-dev make autoconf
git clone https://github.com/cathugger/mkp224o
cd mkp224o
./autogen.sh
./configure --enable-amd64-51-30k
make
./mkp224o -d onionkeys blogMỗi kết quả tìm được nằm trong onionkeys/<address>.onion/, chứa hostname và hs_ed25519_secret_key. Để cài đặt, dừng tor, sao chép thư mục đó vào HiddenServiceDir, rồi áp dụng cùng chown và chmod 700 như khi khôi phục ở trên.
Độ dài prefix quyết định toàn bộ chi phí. Địa chỉ dùng base32, nên mỗi ký tự bổ sung sẽ nhân số key dự kiến lên 32 lần. Prefix ngắn có thể hoàn tất trên laptop. Prefix dài thì không thể hoàn tất trên bất kỳ thiết bị nào bạn sở hữu. Vanity prefix cũng khiến người đọc quen nhận diện vài ký tự đầu thay vì toàn bộ địa chỉ. Đây là thói quen mà các bản sao phishing của onion site thường khai thác.
Các dạng lỗi và chuỗi bạn sẽ thấy
Không có file hostname sau khi restart. Tor chưa khởi động hoặc đã từ chối thư mục này. sudo journalctl -u tor@default -n 50 sẽ hiển thị nguyên nhân:
/var/lib/tor/onion_site/ is not owned by this user (debian-tor, 108) but by root (0). Perhaps you are running Tor as the wrong user?Đó là dạng của một thư mục được tạo thủ công. Hãy sửa owner và mode, hoặc xóa thư mục để tor tự tạo lại.
Tor Browser hiển thị Onionsite Not Found (0xF0). Client không lấy được descriptor, nên từ góc nhìn của network, không có gì được publish tại địa chỉ đó. Xác nhận tor đang chạy và đã bootstrap, so sánh địa chỉ bạn nhập với sudo cat /var/lib/tor/onion_site/hostname từng ký tự một, rồi kiểm tra clock. Tor cần thời gian chính xác để publish và xác thực descriptor, và timedatectl phải báo System clock synchronized: yes.
Địa chỉ resolve được nhưng trang không bao giờ tải xong. Tor đã hoàn tất rendezvous nhưng lỗi ở hop cuối, từ tor đến nginx. Hop này là local nên log của tor vẫn không có thông tin. Chạy curl -sI http://127.0.0.1:8080/ trên server. Connection refused có nghĩa là nginx đang down hoặc đang listen trên một địa chỉ khác với địa chỉ mà HiddenServicePort trỏ tới.
Trang tải được nhưng mọi link đều trỏ về domain thật của bạn. Nguyên nhân là các URL tuyệt đối trong template. Chạy kiểm tra grep -o 'https\?://[^"]*' ở trên và sửa mọi nội dung mà lệnh in ra trước khi chia sẻ địa chỉ ở bất kỳ đâu.
Chạy được rồi dừng sau khi reboot. Trước khi dựa vào site này, hãy chủ động reboot máy một lần, sau đó chạy sudo systemctl status tor@default và sudo systemctl status nginx. Một service được ai đó start thủ công trông giống hệt service đã enable cho đến khi máy restart.
FAQ
Tôi có cần mở một cổng trên firewall cho dịch vụ onion Tor không?
Không. Tor daemon chỉ tạo các kết nối outbound đến directory server, introduction point và từng rendezvous relay, nên không cần rule inbound. Web server tự nó chỉ lắng nghe trên 127.0.0.1. Giữ ufw ở chế độ mặc định deny đối với traffic inbound và chỉ cho phép SSH. Đặc tính này cũng có nghĩa là dịch vụ onion hoạt động được trên máy nằm sau NAT (network address translation), ngay cả khi không có public IP.
Tại sao tôi không thể truy cập địa chỉ .onion trong Tor Browser?
Hãy kiểm tra từ phía server ra ngoài. sudo journalctl -u tor@default -n 50 phải hiển thị Bootstrapped 100% (done): Done. Sau đó, chạy curl -sI http://127.0.0.1:8080/ trên server; lệnh này phải trả về một dòng trạng thái. Tiếp theo, so sánh địa chỉ bạn đã nhập với file hostname, vì chỉ cần sai một ký tự là bạn đang truy cập một dịch vụ khác. Onionsite Not Found (0xF0) có nghĩa là không tìm thấy descriptor cho địa chỉ đó. Nguyên nhân thường là tor không chạy hoặc system clock bị sai.
Tôi có thể chuyển site onion sang server mới mà vẫn giữ địa chỉ cũ không?
Có. Địa chỉ được tạo từ hs_ed25519_secret_key, vì vậy hãy copy toàn bộ HiddenServiceDir sang máy mới, đặt ownership và mode 700 cho debian-tor, rồi khởi động tor. Địa chỉ sẽ hoạt động lại sau khi descriptor được publish lại. Bạn không cần cập nhật DNS record. Nếu mất file đó thì không thể khôi phục địa chỉ, nên hãy backup file bằng mã hóa và lưu ngoài server ngay trong ngày tạo file.
Site onion có cần chứng chỉ HTTPS không?
Không. Địa chỉ dài 56 ký tự là public key của dịch vụ, nên kết nối đã được mã hóa và xác thực end-to-end. Tor Browser cũng coi http:// trên hostname .onion là secure context. Dùng lại clearnet certificate cho onion vhost còn tệ hơn không dùng gì, vì Certificate Transparency log là công khai và ghi vĩnh viễn những hostname dùng chung một certificate. Lý do duy nhất để mua certificate cho hostname .onion là cần sự bảo chứng thương hiệu từ CA có cấp loại certificate này. Liên kết đó vốn được công khai theo thiết kế.