mail.niikn.com: DNS настроен, сценарий задокументирован для dttb.ru
- README НИИКН: обновлён Mailcow (пароль, DNS, порты, ящик) - changelog: добавлена запись о настройке mail.niikn.com - credentials: добавлены Spaceweb, Mailcow НИИКН, NPM НИИКН - decisions: сценарий настройки почтового сервера (шаблон для dttb.ru) - snippets: скрипт spaceweb-dns-api.py для управления DNS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
167
snippets/spaceweb-dns-api.py
Normal file
167
snippets/spaceweb-dns-api.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Spaceweb DNS API — управление DNS записями через JSON-RPC.
|
||||
Панель: https://vps.sweb.ru
|
||||
Домены: niikn.com, dttb.ru, itilegent.ru
|
||||
|
||||
Использование:
|
||||
python3 spaceweb-dns-api.py info niikn.com
|
||||
python3 spaceweb-dns-api.py add-mx niikn.com mail.niikn.com 10
|
||||
python3 spaceweb-dns-api.py add-txt niikn.com @ "v=spf1 mx ~all"
|
||||
python3 spaceweb-dns-api.py add-txt niikn.com _dmarc "v=DMARC1; p=none"
|
||||
python3 spaceweb-dns-api.py del niikn.com TXT 1
|
||||
python3 spaceweb-dns-api.py zone niikn.com
|
||||
"""
|
||||
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import json
|
||||
import http.cookiejar
|
||||
import sys
|
||||
|
||||
LOGIN = "it5870yand"
|
||||
PASSWORD = "1qaz!QAZ"
|
||||
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
|
||||
|
||||
def create_session():
|
||||
"""Логин в Spaceweb, возвращает opener с куками."""
|
||||
cj = http.cookiejar.CookieJar()
|
||||
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
|
||||
|
||||
# GET login page
|
||||
req = urllib.request.Request("https://mcp.sweb.ru/")
|
||||
req.add_header("User-Agent", UA)
|
||||
opener.open(req)
|
||||
|
||||
# POST auth
|
||||
login_data = urllib.parse.urlencode({
|
||||
"login": LOGIN, "password": PASSWORD,
|
||||
"new_panel": "1", "to": "//mcp.sweb.ru/main/index/", "savepref": ""
|
||||
}).encode()
|
||||
req = urllib.request.Request("https://mcp.sweb.ru/main/auth_submit/",
|
||||
data=login_data, method="POST")
|
||||
req.add_header("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.add_header("User-Agent", UA)
|
||||
req.add_header("Referer", "https://mcp.sweb.ru/main/auth/")
|
||||
resp = opener.open(req)
|
||||
if "vps.sweb.ru" not in resp.url and "mcp.sweb.ru/main/index" not in resp.url:
|
||||
raise RuntimeError(f"Login failed, redirected to: {resp.url}")
|
||||
return opener
|
||||
|
||||
|
||||
def api_call(opener, method, params):
|
||||
"""Вызов JSON-RPC API Spaceweb."""
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"user": LOGIN,
|
||||
"method": method,
|
||||
"params": params
|
||||
}
|
||||
body = urllib.parse.quote(json.dumps(payload, separators=(",", ":")), safe="").encode()
|
||||
req = urllib.request.Request("https://api.sweb.ru/domains/dns",
|
||||
data=body, method="POST")
|
||||
req.add_header("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.add_header("User-Agent", UA)
|
||||
req.add_header("Origin", "https://vps.sweb.ru")
|
||||
req.add_header("Referer", "https://vps.sweb.ru/")
|
||||
resp = opener.open(req)
|
||||
result = json.loads(resp.read().decode())
|
||||
if "error" in result:
|
||||
raise RuntimeError(f"API error: {result['error']['message']}")
|
||||
return result.get("result")
|
||||
|
||||
|
||||
def cmd_info(opener, domain):
|
||||
"""Показать все DNS записи."""
|
||||
records = api_call(opener, "info", {"domain": domain})
|
||||
print(f"DNS записи для {domain}:")
|
||||
for r in records:
|
||||
t = r.get("type", "?")
|
||||
n = r.get("name", "@") or "@"
|
||||
v = r.get("value", "")
|
||||
idx = r.get("index", "?")
|
||||
extra = f" (pri: {r.get('priority')})" if r.get("priority") else ""
|
||||
print(f" [{idx:>2}] {t:5s} {n:30s} → {v[:100]}{extra}")
|
||||
|
||||
|
||||
def cmd_zone(opener, domain):
|
||||
"""Показать zone file."""
|
||||
result = api_call(opener, "getFile", {"domain": domain})
|
||||
print(result["content"])
|
||||
|
||||
|
||||
def cmd_add_mx(opener, domain, value, priority="10"):
|
||||
"""Добавить MX запись."""
|
||||
if not value.endswith("."):
|
||||
value += "."
|
||||
result = api_call(opener, "editMx", {
|
||||
"domain": domain, "subDomain": "", "action": "add",
|
||||
"priority": str(priority), "value": value
|
||||
})
|
||||
print(f"MX добавлен: {domain} → {value} (pri {priority}): {result}")
|
||||
|
||||
|
||||
def cmd_add_txt(opener, domain, subdomain, value):
|
||||
"""Добавить TXT запись."""
|
||||
result = api_call(opener, "editTxt", {
|
||||
"domain": domain, "action": "add",
|
||||
"subDomain": subdomain, "value": value
|
||||
})
|
||||
print(f"TXT добавлен: {subdomain}.{domain} → {value[:60]}...: {result}")
|
||||
|
||||
|
||||
def cmd_add_a(opener, domain, name, ip):
|
||||
"""Добавить A запись (субдомен)."""
|
||||
result = api_call(opener, "editMain", {
|
||||
"domain": domain, "action": "add",
|
||||
"name": name, "type": "A", "value": ip, "prefix": ""
|
||||
})
|
||||
print(f"A добавлен: {name}.{domain} → {ip}: {result}")
|
||||
|
||||
|
||||
def cmd_del(opener, domain, record_type, index):
|
||||
"""Удалить запись по типу и индексу."""
|
||||
method_map = {
|
||||
"A": "editMain", "AAAA": "editMain", "CNAME": "editMain",
|
||||
"MX": "editMx", "TXT": "editTxt", "NS": "editNs",
|
||||
"SRV": "editSrv", "CAA": "editCaa"
|
||||
}
|
||||
method = method_map.get(record_type.upper(), "editMain")
|
||||
result = api_call(opener, method, {
|
||||
"domain": domain, "action": "del",
|
||||
"index": int(index), "type": record_type.upper()
|
||||
})
|
||||
print(f"Удалено: {record_type} index={index}: {result}")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
cmd = sys.argv[1]
|
||||
domain = sys.argv[2]
|
||||
opener = create_session()
|
||||
|
||||
if cmd == "info":
|
||||
cmd_info(opener, domain)
|
||||
elif cmd == "zone":
|
||||
cmd_zone(opener, domain)
|
||||
elif cmd == "add-mx" and len(sys.argv) >= 4:
|
||||
pri = sys.argv[4] if len(sys.argv) > 4 else "10"
|
||||
cmd_add_mx(opener, domain, sys.argv[3], pri)
|
||||
elif cmd == "add-txt" and len(sys.argv) >= 5:
|
||||
cmd_add_txt(opener, domain, sys.argv[3], sys.argv[4])
|
||||
elif cmd == "add-a" and len(sys.argv) >= 5:
|
||||
cmd_add_a(opener, domain, sys.argv[3], sys.argv[4])
|
||||
elif cmd == "del" and len(sys.argv) >= 5:
|
||||
cmd_del(opener, domain, sys.argv[3], sys.argv[4])
|
||||
else:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user