Có hai cách để chúng ta có thể truy cập homelab từ bên ngoài:
- Truy cập trực tiếp bằng Public IP của modem
- Truy cập qua tên miền được cấu hình trỏ đến Public IP.
Đa số mọi người sẽ chọn cách thứ hai vì nó đơn giản, dễ nhớ hơn so với chuỗi dãy số IP xxx.xxx.xxx.xxx
. Tuy nhiên nếu mạng ở nhà sử dụng IP động (đa số trường hợp), tên miền cần phải được tự động cập nhật mỗi khi Public IP thay đổi để tránh kết nối bị lỗi.
Hiện tại mình đang dùng 1 bash script trên Raspberry Pi để tự động cập nhật IP động cho tên miền qua dịch vụ CloudFlare. Nhờ vậy tên miền luôn được trỏ đến IP mới nhất được cấp cho modem mạng ở nhà.
Dưới đây là hướng dẫn chi tiết cách thiết lập
Mục Lục
1. Yêu cầu
- Đã có sẵn tên miền và đã có tài khoản Cloudflare.
- Tên miền đã được chuyển về CloudFlare quản lý
- Máy tính / máy ảo chạy Linux (Ubuntu / Arch / CentOS / …) hoặc Raspberry Pi (đời nào cũng được)
Lưu ý: Tên miền sử dụng phải là loại trả phí (.com, .net, .org, .me, …), không thể sử dụng các loại tên miền miễn phí (.tk, .ga, .ml, .cf, .gq, …) vì Cloudflare không hỗ trợ.
2. Cài đặt Cloudflare.sh script
Bạn cần truy cập SSH vào máy tính để thiết lập script.
Tạo file cloudflare.sh
trong thư mục $HOME
cd ~
mkdir cloudflare
cd cloudflare
nano cloudflare.sh
Code language: Bash (bash)
Nhập vào nội dung sau
#!/bin/bash
# Forked from benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
# API Token (Recommended) #####
auth_token="xxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Domain and DNS record for synchronization
zone_identifier="f1nd7h3fuck1n6z0n31d3n71f13r4l50" # Can be found in the "Overview" tab of your domain
record_name="ipv4.example.org" # Which record you want to be synced
# DO NOT CHANGE LINES BELOW
# SCRIPT START
echo -e "Check Initiated"
# Check for current external network IP
ip=$(curl -s4 https://icanhazip.com/)
if [[ ! -z "${ip}" ]]; then
echo -e " > Fetched current external network IP: ${ip}"
else
>&2 echo -e "Network error, cannot fetch external network IP."
fi
# The execution of update
if [[ ! -z "${auth_token}" ]]; then
header_auth_paramheader=( -H '"Authorization: Bearer '${auth_token}'"' )
else
header_auth_paramheader=( -H '"X-Auth-Email: '${auth_email}'"' -H '"X-Auth-Key: '${auth_key}'"' )
fi
# Seek for the record
seek_current_dns_value_cmd=( curl -s -X GET '"https://api.cloudflare.com/client/v4/zones/'${zone_identifier}'/dns_records?name='${record_name}'&type=A"' "${header_auth_paramheader[@]}" -H '"Content-Type: application/json"' )
record=`eval ${seek_current_dns_value_cmd[@]}`
# Can't do anything without the record
if [[ -z "${record}" ]]; then
>&2 echo -e "Network error, cannot fetch DNS record."
exit 1
elif [[ "${record}" == *'"count":0'* ]]; then
>&2 echo -e "Record does not exist, perhaps create one first?"
exit 1
fi
# Set the record identifier from result
record_identifier=`echo "${record}" | sed 's/.*"id":"//;s/".*//'`
# Set existing IP address from the fetched record
old_ip=`echo "${record}" | sed 's/.*"content":"//;s/".*//'`
echo -e " > Fetched current DNS record value : ${old_ip}"
# Compare if they're the same
if [ "${ip}" == "${old_ip}" ]; then
echo -e "Update for A record '${record_name} (${record_identifier})' cancelled.\\n Reason: IP has not changed."
exit 0
else
echo -e " > Different IP addresses detected, synchronizing..."
fi
# The secret sause for executing the update
json_data_v4="'"'{"id":"'${zone_identifier}'","type":"A","proxied":true,"name":"'${record_name}'","content":"'${ip}'","ttl":120}'"'"
update_cmd=( curl -s -X PUT '"https://api.cloudflare.com/client/v4/zones/'${zone_identifier}'/dns_records/'${record_identifier}'"' "${header_auth_paramheader[@]}" -H '"Content-Type: application/json"' )
# Execution result
update=`eval ${update_cmd[@]} --data $json_data_v4`
# The moment of truth
case "$update" in
*'"success":true'*)
echo -e "Update for A record '${record_name} (${record_identifier})' succeeded.\\n - Old value: ${old_ip}\\n + New value: ${ip}";;
*)
>&2 echo -e "Update for A record '${record_name} (${record_identifier})' failed.\\nDUMPING RESULTS:\\n${update}"
exit 1;;
esac
Code language: Bash (bash)
Bạn cần phải điền thông tin vào các dòng sau:
- auth_token: Cloudflare API Token
- zone_identifier: Zone ID
- record_name: Tên miền
3. Tạo Cloudflare API Token
Truy cập vào trang Cloudflare API Token để tạo Token mới
Bạn cần lưu lại Token vừa mới tạo để lưu vào script đã tạo. Token này sẽ không hiện ra lại trên Cloudflare vì lý do bảo mật. Nếu bạn quên lưu, cần phải xoá Token và tạo lại cái mới.
4. Tìm thông tin Zone ID
Từ trang chủ Cloudflare, bạn bấm vào tên miền muốn sử dụng để cập nhật IP. Sau đó kéo xuống dưới sẽ thấy mục Zone ID. Lưu lại thông số này để điền vào script
5. Tạo A Record cho tên miền
Ví dụ mình muốn sử dụng tên miền alibaba.thuanbui.me
để truy cập đến homelab ở nhà. Bấm vào Add record và tạo một A record mới.
- Name: điền vào
alibaba
- IPv4: điền tạm
1.1.1.1
, thông số này sẽ được tự động cập nhật về IP của modem sau khi chạy script.
6. Cập nhật script
Thông tin cần thiết đã có đầy đủ, mình cập nhật lại vào file cloudflare.sh
và lưu lại
#!/bin/bash
# Forked from benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
# API Token (Recommended) #####
auth_token="LrAB--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Domain and DNS record for synchronization
zone_identifier="48bxxxxxxxxxxxxxxxxxxxx60" # Can be found in the "Overview" tab of your domain
record_name="alibaba.thuanbui.me" # Which record you want to be synced
# DO NOT CHANGE LINES BELOW
# SCRIPT START
echo -e "Check Initiated"
# Check for current external network IP
ip=$(curl -s4 https://icanhazip.com/)
if [[ ! -z "${ip}" ]]; then
echo -e " > Fetched current external network IP: ${ip}"
else
>&2 echo -e "Network error, cannot fetch external network IP."
fi
# The execution of update
if [[ ! -z "${auth_token}" ]]; then
header_auth_paramheader=( -H '"Authorization: Bearer '${auth_token}'"' )
else
header_auth_paramheader=( -H '"X-Auth-Email: '${auth_email}'"' -H '"X-Auth-Key: '${auth_key}'"' )
fi
# Seek for the record
seek_current_dns_value_cmd=( curl -s -X GET '"https://api.cloudflare.com/client/v4/zones/'${zone_identifier}'/dns_records?name='${record_name}'&type=A"' "${header_auth_paramheader[@]}" -H '"Content-Type: application/json"' )
record=`eval ${seek_current_dns_value_cmd[@]}`
# Can't do anything without the record
if [[ -z "${record}" ]]; then
>&2 echo -e "Network error, cannot fetch DNS record."
exit 1
elif [[ "${record}" == *'"count":0'* ]]; then
>&2 echo -e "Record does not exist, perhaps create one first?"
exit 1
fi
# Set the record identifier from result
record_identifier=`echo "${record}" | sed 's/.*"id":"//;s/".*//'`
# Set existing IP address from the fetched record
old_ip=`echo "${record}" | sed 's/.*"content":"//;s/".*//'`
echo -e " > Fetched current DNS record value : ${old_ip}"
# Compare if they're the same
if [ "${ip}" == "${old_ip}" ]; then
echo -e "Update for A record '${record_name} (${record_identifier})' cancelled.\\n Reason: IP has not changed."
exit 0
else
echo -e " > Different IP addresses detected, synchronizing..."
fi
# The secret sause for executing the update
json_data_v4="'"'{"id":"'${zone_identifier}'","type":"A","proxied":true,"name":"'${record_name}'","content":"'${ip}'","ttl":120}'"'"
update_cmd=( curl -s -X PUT '"https://api.cloudflare.com/client/v4/zones/'${zone_identifier}'/dns_records/'${record_identifier}'"' "${header_auth_paramheader[@]}" -H '"Content-Type: application/json"' )
# Execution result
update=`eval ${update_cmd[@]} --data $json_data_v4`
# The moment of truth
case "$update" in
*'"success":true'*)
echo -e "Update for A record '${record_name} (${record_identifier})' succeeded.\\n - Old value: ${old_ip}\\n + New value: ${ip}";;
*)
>&2 echo -e "Update for A record '${record_name} (${record_identifier})' failed.\\nDUMPING RESULTS:\\n${update}"
exit 1;;
esac
Code language: Bash (bash)
Mặc định, script này sẽ kích hoạt chế độ proxy trên Cloudflare nhằm mục đích ẩn IP của modem, giảm nguy cơ bị tấn công vào mạng nhà. Nếu vì lý do nào đó không muốn ẩn IP của mình thì có thể thay đổi thông số "proxied":true
thành "proxied":false
7. Chạy thử nghiệm
Thiết lập quyền thực thi cho file cloudflare.sh
chmod +X cloudflare.sh
Code language: CSS (css)
Chạy thử xem nào
./cloudflare.sh
thuanbui@raspberrypi:~/cloudflare $ ./cloudflare.sh
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Check Initiated
> Fetched current external network IP: 27.xx.xx.xx
> Fetched current DNS record value : 1.1.1.1
> Different IP addresses detected, synchronizing...
Update for A record 'alibaba.thuanbui.me (48bxxxxxxxxxxxxxxxxxxxx60)' succeeded.
- Old value: 1.1.1.1
+ New value: 27.xx.xx.xx
Code language: YAML (yaml)
Quay lại Cloudflare, tên miền alibaba.thuanbui.me
, mình thấy đã được tự động cập nhật với IP mới. Ngon lành!
8. Thiết lập cron tự động
Việc cuối cùng cần làm là thiết lập cron để script tự động chạy 24/7. Sau đó, không cần bận tâm đến nó nữa.
crontab -e
Code language: Nginx (nginx)
Mình chỉnh cho script tự động chạy sau mỗi 10 phút bằng cách thêm dòng này vào dưới cùng
*/10 * * * * /bin/bash /home/thuanbui/cloudflare/cloudflare.sh
Code language: Markdown (markdown)
Chú ý nhớ thay /home/thuanbui/cloudflare/cloudflare.sh
thành đường dẫn đến nơi lưu file cloudflare.sh
của bạn.
Chúc bạn thực hiện thành công!
bạn cho hỏi mình bị lỗi nay khi chay ./cloudflare.sh
./cloudflare.sh: command substitution: line 20: syntax error near unexpected token `)’
./cloudflare.sh: command substitution: line 20: `curl -s4 )’
Network error, cannot fetch external network IP.
Network error, cannot fetch DNS record.
Mình mới kiểm tra lại, dòng 20 bị dư 2 dấu < > nên đã cập nhật lại. Bạn chỉnh code lại theo bản mới ở trên nhé. Cám ơn bạn đã thông báo.
em chạy lệnh thì nó không hiện lỗi dòng nào, nhưng nó báo không fetch dns.
root@rpisrv4:/home/pi/cloudflare# bash cloudflare.sh
Check Initiated
> Fetched current external network IP: 113.113.173.201
Network error, cannot fetch DNS record.
thêm nữa, phần Auth_token nó có 2 dấu [–]
nhưng trong phần token em lấy thì nó không có dấu gạch đó. Em phải điền sao cho đúng?
Nhiều khả năng là em nhập sai API Token rồi. Do sai nên nó không truy cập Cloudflare được nên báo lỗi Cannot fetch DNS Record.
Chú ý là cần lấy API Token, không phải Global API nhé.
1. Về API token, em làm theo hướng dẫn của anh để lấy, nhưng API token khi nhập trong code nó định dạng thêm –.
Trong khi API Token của em lấy thì nó không có cái dấu –.
Em phải điền sao cho đúng.
Ví dụ:
API token của em là ABCDEFGHIJKLM.
Khi nhập vào đoạn code của anh sẽ cắt 4 ký tự đầu ra ABCD–EFGH… ==> như vậy có đúng không?
2. Lỗi trả về khi em chạy file bằng 2 lệnh nó khác nhau.
Lệnh sh:
pi@rpisrv4:~/cloudflare $ sh cloudflare.sh
-e Check Initiated
cloudflare.sh: 20: cloudflare.sh: [[: not found
-e Network error, cannot fetch external network IP.
cloudflare.sh: 28: cloudflare.sh: Syntax error: “(” unexpected (expecting “fi”)
Lệnh bash
pi@rpisrv4:~/cloudflare $ bash cloudflare.sh
Check Initiated
> Fetched current external network IP: xxx.113.173.201
Network error, cannot fetch DNS record.
API Token nó hiện ra sao thì paste vô y hệt vậy, không có chia ra gì hết nha em. Thêm — vô là nó sai rồi.
Bản chất cloudflare.sh nó là câu lệnh rồi nên em gõ `sh cloudflare.sh` là sai. Chỉ cần gõ `./cloudflare.sh` là được.
Check Initiated
> Fetched current external network IP: 155.64.16.13
Network error, cannot fetch DNS record.
Admin giúp mình với ạ. Mình đã check đi check lại API token, zone ID đúng nhưng vẫn bị lỗi trên
Báo lỗi Network error, cannot fetch DNS record nghĩa là kết nối đến Cloudflare không thành công. Bạn cần kiểm tra lại các thông số: API Token, Zone ID, record name
Có phải do trang lấy API của cloudflare bị lỗi không anh
Mình thử chạy: https://api.cloudflare.com/client/v4/zones/{zone_id}
thì trả về kết quả sau: {“success”:false,”errors”:[{“code”:9106,”message”:”Missing X-Auth-Email header”},{“code”:9107,”message”:”Missing X-Auth-Key header”}],”messages”:[],”result”:null}
Trang API của Cloudflare không bị gì cả nhé. Bạn có thể test curl theo lệnh này:
curl -X GET "https://api.cloudflare.com/client/v4/zones/Thay-ZONE-ID-vô-đây" -H "Content-Type:application/json" -H "Authorization: Bearer Thay-API-Token-vô-đây"
. Lưu ý là gõ lệnh vào Terminal, không phải gõ vào trình duyệt. Nếu thông tin chính xác nó sẽ trả về kết quả tương tự như sauEm chạy lệnh “curl” trên thì ra đúng kết quả như anh nói rồi ạ, nhưng chạy /.cloudflare.sh thì vẫn bị lỗi “Network error, cannot fetch DNS record”
Anh giúp em với ạ
Nếu chạy curl ra đúng thì có nghĩa các thông số đều đúng. Chạy file sh báo lỗi thì nhiều khả năng là phần thiết lập thông số trong file cloudflare.sh bị sai chỗ nào rồi. Kiểm tra lại 5 thông số này:
Check Initiated
> Fetched current external network IP: 115.77.50.116
Network error, cannot fetch DNS record.
Mình đã check đi check lại API token, zone ID đúng nhưng vẫn bị lỗi trên.
Mình đã chạy curl ở bình luận trên, thông tin token , zone id mình củng đều đúng.
Mình đã thêm auth_email luôn rồi.
Mong admin giúp đỡ
Bạn cập nhật lại script trong bài nhé. Mình mới cập nhật lại script mới, cái cũ bị lỗi dư kí tự < và > nên fetch DNS bị lỗi. Sorry bạn nhiều.
a cho e hỏi, nếu có nhiều script để chạy cho nhiều subdomain thì setup sao vậy a? e có thử thêm nhiều dòng script vào cron như vầy:
*/10 * * * * /bin/bash /home/thuanbui/cloudflare/1.sh
*/10 * * * * /bin/bash /home/thuanbui/cloudflare/2.sh
*/10 * * * * /bin/bash /home/thuanbui/cloudflare/3.sh
chỉ có cái 1.sh là chạy auto được. ( 1,2,3.sh khi chạy tay đều ok) cám ơn a.
Bạn thử kết hợp tất cả thành 1 dòng như thế này xem có chạy ổn không nhé: */10 * * * * /bin/bash /home/thuanbui/cloudflare/1.sh ; /bin/bash /home/thuanbui/cloudflare/2.sh ; /bin/bash /home/thuanbui/cloudflare/3.sh
Bác ơi xem giúp e xem tại sao sau khi chạy script nó lại báo như thế này. thanks bác, mọi thông số e đã nhập đúng.
Check Initiated
> Fetched current external network IP: 113.xxx.xxx.94
> Fetched current DNS record value : 8.8.8.8
> Different IP addresses detected, synchronizing…
Update for A record ‘home.vinhvinhcloud.tk (f45313b2974xxxxxxx646fb3c94386a6)’ failed.
DUMPING RESULTS:
Nhiều khả năng lỗi là do dùng domain .tk miễn phí nhé. Bạn thử dùng domain .com, .net,… xem có gặp lỗi tương tự không.
Mình có thể chạy script này trên NAS synology được không bạn. Vì NAS mình chạy 24/7
Mình không dùng Synology nên không rành bạn nhé.
cloudflare.sh: line 28: syntax error: unexpected “(” (expecting “fi”)
mình chạy bị lỗi này bác ạ. bác check lại giúp mình với.
Mình mới test lại thấy script vẫn chạy ổn. Bạn thử copy / paste lại nhé.
cloudflare.sh: 20: [[: not found
-e Network error, cannot fetch external network IP.
cloudflare.sh: 28: Syntax error: “(” unexpected (expecting “fi”)
Vẫn bị lỗi bác ạ
crontab -e
*/10 * * * * /root/cloudflare/cloudflare.sh
em thêm đoạn tên vào dưới: esac mà ko được
Bác ơi e có làm theo hd nhưng khí chạy ./cloudflare.sh thì báo lỗi ./cloudflare.sh: command not found. Lỗi này thì sửa tnao ạ 🙁
Bạn cần phải di chuyển vào thư mục đang lưu file cloudflare.sh bằng lệnh cd, sau đó làm theo bước 7 trong bài.
E có di chuyển bằng lệnh cd như hd bác rồi ạ. Nhưng nó vẫn báo như kia 🙁
lethanhtung@ubuntu:~$ cd cloudflare
lethanhtung@ubuntu:~/cloudflare$ dir
cloudflare.sh
lethanhtung@ubuntu:~/cloudflare$ sudo chmod +X cloudflare.sh
[sudo] password for lethanhtung:
lethanhtung@ubuntu:~/cloudflare$ sudo ./cloudflare.sh
sudo: ./cloudflare.sh: command not found
lethanhtung@ubuntu:~/cloudflare$
Khi em chạy nó hiện ra lỗi này ạ:
root@HungNAS:~# chmod +X cloudflare.sh
root@HungNAS:~# chmod +X cloudflare.sh
root@HungNAS:~# ./cloudflare.sh
-bash: ./cloudflare.sh: /bin/bash^M: bad interpreter: No such file or directory
Lỗi này do trong file có kí tự ^M Linux không hiểu.
Bạn tham khảo cách sửa lỗi ở đây: https://stackoverflow.com/questions/14219092/bash-script-bin-bashm-bad-interpreter-no-such-file-or-directory
Mình cũng bị lỗi Network error, cannot fetch DNS record. như mấy bạn trên, tất cả mọi thứ mình đều điền đúng và kiểm tra rất nhiều lần, không biết là bị sao ạ? Mình dùng debian 11
Khúc chạy bash: ./cloudflare.sh: Permission denied mình phải cấp lại quyền theo cách này nó mới chạy được
chmod a+x cloudflare.sh
Mình cũng bị lỗi Network error, cannot fetch DNS record. đã check kĩ, chắc script đã cũ rồi
mình đã xử lý đc do con proxmox mình trỏ dns server do mình tự cấu hình dns
các bạn đổi dns trong etc\resolv.conf thành dns 8.8.8.8 là được.
có cách nào tự động update cho nhiều Record không bạn,
Mình chỉ set cho 1 cái, sau đó những cái cái còn lại chỉnh CNAME trỏ về cái đã set là xong.
Xin hỏi phần cron chưa chạy được nhờ ad hướng dẫn
bài viết quá hay cảm ơn bác ạ, hôm qua em nghe nói là có ông nào đó dùng duckdns xong ném cái địa chỉ của nó vào trong value cname mà k rõ lắm bác có thể giải thích cho em không ạ
Cái địa chỉ của DuckDNS nó đã được trỏ về đúng IP ở nhà. Khi bạn set CNAME là địa chỉ DuckDNS thì cái tên miền sẽ được trỏ về cái IP của DuckDNS.
Ad cho mình hỏi vậy máy chạy script này phải luôn chạy 24/7 phải không ad? Hoặc chạy thẳng trên con vps web luôn được không?
Script này để cập nhật IP động ở nhà nên phải chạy trên server nằm ở nhà nhé.
ad cho mình hỏi, mình muốn truy cập từ cái subdomain đó trỏ về một host trong mạng của mình thì mình phải làm như nào ạ. Ví dụ mình dùng con ubuntu để cài giám sát nagios, chạy thêm cả scrip để cập nhật địa chỉ public như bài được rồi. Làm cách nào để trỏ đến web interface của con giám sát đc ?
Mình không hiểu câu hỏi của bạn nhé. Nếu bạn đang hỏi về cách dùng subdomain để truy cập vào web service trên máy chủ thì cài thêm reverse proxy: nginx hoặc caddy.
anh ơi. anh có thể nói rõ hơn chỗ phần chạy crontab được ko anh? thêm vào đâu ạ ? Vì em chạy lệnh crontab -e nó tự động tạo ra một cái file txt… em ko biết là add vào cuối file đó hay là file cloudflare.sh ạ?
Chạy lệnh crontab -e nó ra file txt là đúng rồi nhé. E thêm vào cuối file và save lại là xong.
Cảm ơn anh ạ !
Hi anh ! cho e hỏi là nếu e xài Nas có cái container manager (giống docker) . Không biết có ứng dụng script này vào đc ko . Cám ơn
Nếu cài bằng Docker thì mình khuyến khích dùng cái này: https://github.com/timothymiller/cloudflare-ddns
Anh ơi cho em hỏi, script này có cập nhập nhiều domain được không ạ, hay mỗi domain sẽ tạo 1 cái script nhưu thế này
Script này chỉ cập nhật được 1 domain nhé. Muốn cập nhiều cùng lúc thì cần chỉnh sửa lại script xíu. Nhưng cho dễ thì dùng Docker sẽ tiện hơn: https://thuanbui.me/cau-hinh-dynamic-dns-qua-cloudflare-su-dung-docker-compose/
của em nó báo vầy Check Initiated
> Fetched current external network IP: 123.19.16.29
do: -c: line 34: syntax error near unexpected token `&’
do: -c: line 34: `seek_current_dns_value_cmd=( curl -s -X GET https://api.cloudflare.com/client/v4/zones/”/dns_records?name=”&type=A "${header_auth_paramheader[@]}" -H Content-Type: application/json )’
Network error, cannot fetch DNS record.
mình thuê tên miền từ nơi khác thì phải làm sao ạ?
Bạn cần cấu hình Nameservers để chuyển DNS quản lý về Cloudflare rồi sẽ áp dụng được cách này
Anh Thuận ơi, em đang cần cấu hình Proxy cho TCP mà trên cloudflare cái đó mất tiền nên em phải tắt cái status Proxied (DNS only) trên record typeA. Nhưng mỗi khi chạy script , recordA vừa update IP Wan mới vừa ghi đè lại cái status Proxied. Có trường nào để em set cứng nó là DNS only trong script không ạ
Trong bài đã có hướng dẫn nha em. Đọc kỹ lại mục 6.
Em cảm ơn anh nha, thực ra em đang muốn làm proxy cho MQTT (tcp request) nhưng khi em dùng lệnh này thì Nginx local nhận địa chỉ IP đầu vào là của Cloudflare nên Mqtt Broker nó không định danh được client:
$ dig @1.1.1.1 iot-mqtt-broker.click
Em mò thì cần phải cài cấu hình Tcp Proxied trên CF và mất tiền phải không ạ