Repository navigation
Expand file tree
/
Copy pathaa
More file actions
190 lines (164 loc) · 6.38 KB
/
Copy pathaa
File metadata and controls
190 lines (164 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import os
import json
import requests
import base64
import time
from pathlib import Path
# 設定檔案路徑
CONFIG_FILE = "/etc/cloudflare_nas/config.json"
CREDENTIALS_FILE = "/etc/cloudflare_nas/credentials.json"
CLOUDFLARED_CONFIG = "/etc/cloudflare/config.yml"
CLOUDFLARED_BIN = "/usr/local/bin/cloudflared"
class CloudflareNAS:
def __init__(self):
self.load_config()
self.headers = {
"Authorization": f"Bearer {self.config['api_token']}",
"Content-Type": "application/json"
}
def load_config(self):
"""載入設定檔"""
with open(CONFIG_FILE, "r") as f:
self.config = json.load(f)
def get_public_ip(self):
"""獲取當前公網 IP"""
try:
return requests.get('https://api.ipify.org').text
except Exception as e:
print(f"獲取 IP 失敗: {e}")
return None
def update_dns_record(self, ip):
"""更新或創建 DNS A 紀錄"""
url = f"https://api.cloudflare.com/client/v4/zones/{self.config['zone_id']}/dns_records"
params = {
"type": "A",
"name": self.config["dns_record"]
}
# 檢查現有紀錄
response = requests.get(url, headers=self.headers, params=params)
records = response.json()["result"]
data = {
"type": "A",
"name": self.config["dns_record"],
"content": ip,
"ttl": 1,
"proxied": False
}
if records:
# 更新現有紀錄
record_id = records[0]["id"]
response = requests.put(f"{url}/{record_id}", headers=self.headers, json=data)
else:
# 創建新紀錄
response = requests.post(url, headers=self.headers, json=data)
return response.json()
def create_tunnel(self):
"""透過 API 創建 Cloudflare Tunnel"""
url = f"https://api.cloudflare.com/client/v4/accounts/{self.config['account_id']}/tunnels"
# 生成隨機 Tunnel Secret
tunnel_secret = base64.b64encode(os.urandom(32)).decode('utf-8')
data = {
"name": self.config["tunnel_name"],
"tunnel_secret": tunnel_secret
}
response = requests.post(url, headers=self.headers, json=data)
result = response.json()
if response.status_code == 200:
self.tunnel_id = result["result"]["id"]
self.tunnel_secret = tunnel_secret
return True
else:
print(f"創建 Tunnel 失敗: {result}")
return False
def configure_tunnel_route(self):
"""配置 Tunnel 路由到域名"""
url = f"https://api.cloudflare.com/client/v4/accounts/{self.config['account_id']}/tunnels/{self.tunnel_id}/routes"
data = {
"type": "dns",
"hostname": self.config["dns_record"],
"service": self.config["nas_service_url"]
}
response = requests.post(url, headers=self.headers, json=data)
return response.json()
def save_tunnel_credentials(self):
"""保存 Tunnel 憑證文件"""
credentials = {
"AccountTag": self.config["account_id"],
"TunnelID": self.tunnel_id,
"TunnelSecret": self.tunnel_secret
}
with open(CREDENTIALS_FILE, "w") as f:
json.dump(credentials, f)
os.chmod(CREDENTIALS_FILE, 0o600)
def generate_cloudflared_config(self):
"""生成 cloudflared 配置文件"""
config = {
"tunnel": self.tunnel_id,
"credentials-file": CREDENTIALS_FILE,
"ingress": [
{"hostname": self.config["dns_record"], "service": self.config["nas_service_url"]},
{"service": "http_status:404"}
]
}
with open(CLOUDFLARED_CONFIG, "w") as f:
json.dump(config, f, indent=2)
def install_cloudflared(self):
"""下載並安裝 cloudflared"""
if not Path(CLOUDFLARED_BIN).exists():
os.system("wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -O /tmp/cloudflared")
os.system("chmod +x /tmp/cloudflared")
os.system("sudo mv /tmp/cloudflared /usr/local/bin/cloudflared")
def setup_service(self):
"""設置 systemd 服務"""
service_content = f"""
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
ExecStart={CLOUDFLARED_BIN} tunnel run --config {CLOUDFLARED_CONFIG}
Restart=always
[Install]
WantedBy=multi-user.target
"""
with open("/etc/systemd/system/cloudflared.service", "w") as f:
f.write(service_content)
os.system("sudo systemctl daemon-reload")
os.system("sudo systemctl enable cloudflared")
os.system("sudo systemctl start cloudflared")
def run(self):
"""主執行流程"""
# 步驟 1: 更新 DDNS
current_ip = self.get_public_ip()
if current_ip:
print(f"當前公網 IP: {current_ip}")
dns_result = self.update_dns_record(current_ip)
print(f"DNS 更新結果: {dns_result['success']}")
# 步驟 2: 創建 Tunnel
if self.create_tunnel():
print(f"Tunnel 創建成功: {self.tunnel_id}")
self.configure_tunnel_route()
self.save_tunnel_credentials()
self.generate_cloudflared_config()
# 步驟 3: 安裝並啟動服務
self.install_cloudflared()
self.setup_service()
print("Cloudflare Tunnel 服務已啟動")
if __name__ == "__main__":
# 初次運行前需手動創建設定檔案
config_path = Path(CONFIG_FILE)
if not config_path.exists():
config = {
"api_token": "YOUR_API_TOKEN",
"zone_id": "YOUR_ZONE_ID",
"account_id": "YOUR_ACCOUNT_ID",
"dns_record": "nas.yourdomain.com",
"tunnel_name": "feynax_nas_tunnel",
"nas_service_url": "http://localhost:8080" # 修改為 NAS 實際服務地址
}
config_path.parent.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE, "w") as f:
json.dump(config, f, indent=2)
print(f"請先編輯設定檔: {CONFIG_FILE}")
else:
nas = CloudflareNAS()
nas.run()