File: //usr/local/CyberCP/dns/dnsManager.py
#!/usr/local/CyberCP/bin/python
import argparse
import errno
import os.path
import sys
import django
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
django.setup()
from django.http import HttpResponse
import json
try:
from plogical.dnsUtilities import DNS
from loginSystem.models import Administrator
from .models import Domains,Records
from plogical.mailUtilities import mailUtilities
except:
pass
import os
from re import match,I,M
from plogical.acl import ACLManager
import CloudFlare
import re
import plogical.CyberCPLogFileWriter as logging
from plogical.processUtilities import ProcessUtilities
from plogical.httpProc import httpProc
class DNSManager:
defaultNameServersPath = '/home/cyberpanel/defaultNameservers'
def __init__(self, extraArgs=None):
self.extraArgs = extraArgs
def loadCFKeys(self):
cfFile = '%s%s' % (DNS.CFPath, self.admin.userName)
data = open(cfFile, 'r').readlines()
self.email = data[0].rstrip('\n')
self.key = data[1].rstrip('\n')
def loadDNSHome(self, request = None, userID = None):
admin = Administrator.objects.get(pk=userID)
template = 'dns/index.html'
proc = httpProc(request, template, {"type": admin.type}, 'createDNSZone')
return proc.render()
def createNameserver(self, request = None, userID = None):
mailUtilities.checkHome()
if os.path.exists('/home/cyberpanel/powerdns'):
finalData = {"status": 1}
else:
finalData = {"status": 0}
template = 'dns/createNameServer.html'
proc = httpProc(request, template, finalData, 'createNameServer')
return proc.render()
def NSCreation(self, userID = None, data = None):
try:
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'createNameServer') == 0:
return ACLManager.loadErrorJson('NSCreation', 0)
domainForNS = data['domainForNS']
ns1 = data['ns1']
ns2 = data['ns2']
firstNSIP = data['firstNSIP']
secondNSIP = data['secondNSIP']
DNS.dnsTemplate(domainForNS, admin)
newZone = Domains.objects.get(name=domainForNS)
## NS1
record = Records(domainOwner=newZone,
domain_id=newZone.id,
name=ns1,
type="A",
content=firstNSIP,
ttl=3600,
prio=0,
disabled=0,
auth=1)
record.save()
## NS2
record = Records(domainOwner=newZone,
domain_id=newZone.id,
name=ns2,
type="A",
content=secondNSIP,
ttl=3600,
prio=0,
disabled=0,
auth=1)
record.save()
final_dic = {'NSCreation': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'NSCreation': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def createDNSZone(self, request = None, userID = None):
if os.path.exists('/home/cyberpanel/powerdns'):
finalData = {'status': 1}
else:
finalData = {'status': 0}
template = 'dns/createDNSZone.html'
proc = httpProc(request, template, finalData, 'createDNSZone')
return proc.render()
def zoneCreation(self, userID = None, data = None):
try:
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'createDNSZone') == 0:
return ACLManager.loadErrorJson('zoneCreation', 0)
zoneDomain = data['zoneDomain']
newZone = Domains(admin=admin, name=zoneDomain, type="MASTER")
newZone.save()
content = "ns1." + zoneDomain + " hostmaster." + zoneDomain + " 1 10800 3600 1209600 3600"
soaRecord = Records(domainOwner=newZone,
domain_id=newZone.id,
name=zoneDomain,
type="SOA",
content=content,
ttl=3600,
prio=0,
disabled=0,
auth=1)
soaRecord.save()
final_dic = {'zoneCreation': 1}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'zoneCreation': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def addDeleteDNSRecords(self, request = None, userID = None):
currentACL = ACLManager.loadedACL(userID)
if not os.path.exists('/home/cyberpanel/powerdns'):
finalData = {"status": 0}
else:
finalData = {"status": 1}
# Get DNS zones directly from the Domains table instead of just websites
finalData['domainsList'] = ACLManager.findAllDNSZones(currentACL, userID)
template = 'dns/addDeleteDNSRecords.html'
proc = httpProc(request, template, finalData, 'addDeleteRecords')
return proc.render()
def getCurrentRecordsForDomain(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('fetchStatus', 0)
zoneDomain = data['selectedZone']
currentSelection = data['currentSelection']
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
domain = Domains.objects.get(name=zoneDomain)
records = Records.objects.filter(domain_id=domain.id)
fetchType = ""
if currentSelection == 'aRecord':
fetchType = 'A'
elif currentSelection == 'aaaaRecord':
fetchType = 'AAAA'
elif currentSelection == 'cNameRecord':
fetchType = 'CNAME'
elif currentSelection == 'mxRecord':
fetchType = 'MX'
elif currentSelection == 'txtRecord':
fetchType = 'TXT'
elif currentSelection == 'spfRecord':
fetchType = 'SPF'
elif currentSelection == 'nsRecord':
fetchType = 'NS'
elif currentSelection == 'soaRecord':
fetchType = 'SOA'
elif currentSelection == 'srvRecord':
fetchType = 'SRV'
elif currentSelection == 'caaRecord':
fetchType = 'CAA'
json_data = "["
checker = 0
for items in records:
if items.type == fetchType:
dic = {'id': items.id,
'type': items.type,
'name': items.name,
'content': items.content,
'priority': items.prio,
'ttl': items.ttl
}
if checker == 0:
json_data = json_data + json.dumps(dic)
checker = 1
else:
json_data = json_data + ',' + json.dumps(dic)
else:
continue
json_data = json_data + ']'
final_json = json.dumps({'status': 1, 'fetchStatus': 1, 'error_message': "None", "data": json_data})
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def addDNSRecord(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('add_status', 0)
zoneDomain = data['selectedZone']
recordType = data['recordType']
recordName = data['recordName']
ttl = int(data['ttl'])
if ttl < 0:
raise ValueError("TTL: The item must be greater than 0")
elif ttl > 86400:
raise ValueError("TTL: The item must be lesser than 86401")
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
zone = Domains.objects.get(name=zoneDomain)
value = ""
if recordType == "A":
recordContentA = data['recordContentA'] ## IP or pointing value
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
DNS.createDNSRecord(zone, value, recordType, recordContentA, 0, ttl)
elif recordType == "MX":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentMX = data['recordContentMX']
priority = data['priority']
DNS.createDNSRecord(zone, value, recordType, recordContentMX, priority, ttl)
elif recordType == "AAAA":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentAAAA = data['recordContentAAAA'] ## IP or pointing value
DNS.createDNSRecord(zone, value, recordType, recordContentAAAA, 0, ttl)
elif recordType == "CNAME":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentCNAME = data['recordContentCNAME'] ## IP or pointing value
DNS.createDNSRecord(zone, value, recordType, recordContentCNAME, 0, ttl)
elif recordType == "SPF":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentSPF = data['recordContentSPF'] ## IP or pointing value
DNS.createDNSRecord(zone, value, recordType, recordContentSPF, 0, ttl)
elif recordType == "TXT":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentTXT = data['recordContentTXT'] ## IP or pointing value
DNS.createDNSRecord(zone, value, recordType, recordContentTXT, 0, ttl)
elif recordType == "SOA":
recordContentSOA = data['recordContentSOA']
DNS.createDNSRecord(zone, recordName, recordType, recordContentSOA, 0, ttl)
elif recordType == "NS":
recordContentNS = data['recordContentNS']
if recordContentNS == "@":
recordContentNS = "ns1." + zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?',
recordContentNS, M | I):
recordContentNS = recordContentNS
else:
recordContentNS = recordContentNS + "." + zoneDomain
DNS.createDNSRecord(zone, recordName, recordType, recordContentNS, 0, ttl)
elif recordType == "SRV":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentSRV = data['recordContentSRV']
priority = data['priority']
DNS.createDNSRecord(zone, value, recordType, recordContentSRV, priority, ttl)
elif recordType == "CAA":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentCAA = data['recordContentCAA'] ## IP or pointing value
DNS.createDNSRecord(zone, value, recordType, recordContentCAA, 0, ttl)
final_dic = {'status': 1, 'add_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'add_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def updateRecord(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('add_status', 0)
zoneDomain = data['selectedZone']
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
record = Records.objects.get(pk=data['id'])
if ACLManager.VerifyRecordOwner(currentACL, record, zoneDomain) == 1:
pass
else:
return ACLManager.loadErrorJson()
if data['nameNow'] != None:
record.name = data['nameNow']
if data['ttlNow'] != None:
record.ttl = int(data['ttlNow'])
if record.ttl < 0:
raise ValueError("TTL: The item must be greater than 0")
elif record.ttl > 86400:
raise ValueError("TTL: The item must be lesser than 86401")
if data['priorityNow'] != None:
record.prio = int(data['priorityNow'])
if data['contentNow'] != None:
record.content = data['contentNow']
record.save()
final_dic = {'status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def deleteDNSRecord(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('delete_status', 0)
id = data['id']
delRecord = Records.objects.get(id=id)
admin = Administrator.objects.get(pk=userID)
if ACLManager.checkOwnershipZone(delRecord.domainOwner.name, admin, currentACL) == 1:
pass
else:
return ACLManager.loadError()
delRecord.delete()
final_dic = {'status': 1, 'delete_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'delete_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def deleteDNSZone(self, request = None, userID = None):
currentACL = ACLManager.loadedACL(userID)
if not os.path.exists('/home/cyberpanel/powerdns'):
finalData = {"status": 0}
else:
finalData = {"status": 1}
# Get DNS zones directly from the Domains table instead of just websites
finalData['domainsList'] = ACLManager.findAllDNSZones(currentACL, userID)
template = 'dns/deleteDNSZone.html'
proc = httpProc(request, template, finalData, 'deleteZone')
return proc.render()
def submitZoneDeletion(self, userID = None, data = None):
try:
zoneDomain = data['zoneDomain']
currentACL = ACLManager.loadedACL(userID)
admin = Administrator.objects.get(pk=userID)
if ACLManager.currentContextPermission(currentACL, 'deleteZone') == 0:
return ACLManager.loadErrorJson('delete_status', 0)
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadError()
delZone = Domains.objects.get(name=zoneDomain)
admin = Administrator.objects.get(pk=userID)
if currentACL['admin'] == 1:
if delZone.admin != admin:
return ACLManager.loadErrorJson()
delZone.delete()
final_dic = {'delete_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'delete_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def configureDefaultNameServers(self, request=None, userID=None):
currentACL = ACLManager.loadedACL(userID)
if not os.path.exists('/home/cyberpanel/powerdns'):
data = {"status": 0}
else:
data = {"status": 1}
data['domainsList'] = ACLManager.findAllDomains(currentACL, userID)
if os.path.exists(DNSManager.defaultNameServersPath):
nsData = open(DNSManager.defaultNameServersPath, 'r').readlines()
try:
data['firstNS'] = nsData[0].rstrip('\n')
except:
pass
try:
data['secondNS'] = nsData[1].rstrip('\n')
except:
pass
try:
data['thirdNS'] = nsData[2].rstrip('\n')
except:
pass
try:
data['forthNS'] = nsData[3].rstrip('\n')
except:
pass
template = 'dns/configureDefaultNameServers.html'
proc = httpProc(request, template, data, 'admin')
return proc.render()
def saveNSConfigurations(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if currentACL['admin'] == 1:
pass
else:
return ACLManager.loadErrorJson()
nsContent = ''
try:
nsContent = '%s\n%s\n%s\n%s\n' % (data['firstNS'].rstrip('\n'), data['secondNS'].rstrip('\n'), data['thirdNS'].rstrip('\n'), data['forthNS'].rstrip('\n'))
except:
try:
nsContent = '%s\n%s\n%s\n' % (data['firstNS'].rstrip('\n'), data['secondNS'].rstrip('\n'), data['thirdNS'].rstrip('\n'))
except:
try:
nsContent = '%s\n%s\n' % (data['firstNS'].rstrip('\n'), data['secondNS'].rstrip('\n'))
except:
try:
nsContent = '%s\n' % (data['firstNS'].rstrip('\n'))
except:
pass
writeToFile = open(DNSManager.defaultNameServersPath, 'w')
writeToFile.write(nsContent.rstrip('\n'))
writeToFile.close()
###
import tldextract
no_cache_extract = tldextract.TLDExtract(cache_dir=None)
nsData = open(DNSManager.defaultNameServersPath, 'r').readlines()
for ns in nsData:
extractDomain = no_cache_extract(ns.rstrip('\n'))
topLevelDomain = extractDomain.domain + '.' + extractDomain.suffix
zone = Domains.objects.get(name=topLevelDomain)
DNS.createDNSRecord(zone, ns, 'A', ACLManager.fetchIP(), 0, 1400)
final_dic = {'status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def addDeleteDNSRecordsCloudFlare(self, request = None, userID = None):
currentACL = ACLManager.loadedACL(userID)
if not os.path.exists('/home/cyberpanel/powerdns'):
status = 0
else:
status = 1
admin = Administrator.objects.get(pk=userID)
CloudFlare = 0
cfPath = '%s%s' % (DNS.CFPath, admin.userName)
if os.path.exists(cfPath):
CloudFlare = 1
domainsList = ACLManager.findAllDomains(currentACL, userID)
self.admin = admin
self.loadCFKeys()
data = {"domainsList": domainsList, "status": status, 'CloudFlare': CloudFlare, 'cfEmail': self.email,
'cfToken': self.key}
else:
data = {"status": status, 'CloudFlare': CloudFlare}
template = 'dns/addDeleteDNSRecordsCloudFlare.html'
proc = httpProc(request, template, data, 'addDeleteRecords')
return proc.render()
def saveCFConfigs(self, userID = None, data = None):
try:
cfEmail = data['cfEmail']
cfToken = data['cfToken']
cfSync = data['cfSync']
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('status', 0)
admin = Administrator.objects.get(pk=userID)
cfPath = '%s%s' % (DNS.CFPath, admin.userName)
writeToFile = open(cfPath, 'w')
writeToFile.write('%s\n%s\n%s' % (cfEmail, cfToken, cfSync))
writeToFile.close()
os.chmod(cfPath, 0o600)
final_dic = {'status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def getCurrentRecordsForDomainCloudFlare(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('fetchStatus', 0)
zoneDomain = data['selectedZone']
currentSelection = data['currentSelection']
admin = Administrator.objects.get(pk=userID)
self.admin = admin
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
self.loadCFKeys()
params = {'name': zoneDomain, 'per_page':50}
cf = CloudFlare.CloudFlare(email=self.email,token=self.key)
try:
zones = cf.zones.get(params=params)
except BaseException as e:
final_json = json.dumps({'status': 0, 'fetchStatus': 0, 'error_message': str(e), "data": '[]'})
return HttpResponse(final_json)
# there should only be one zone
if len(zones) == 0:
final_json = json.dumps({'status': 1, 'fetchStatus': 1, 'error_message': '', "data": '[]'})
return HttpResponse(final_json)
for zone in sorted(zones, key=lambda v: v['name']):
zone_name = zone['name']
zone_id = zone['id']
fetchType = ""
if currentSelection == 'aRecord':
fetchType = 'A'
elif currentSelection == 'aaaaRecord':
fetchType = 'AAAA'
elif currentSelection == 'cNameRecord':
fetchType = 'CNAME'
elif currentSelection == 'mxRecord':
fetchType = 'MX'
elif currentSelection == 'txtRecord':
fetchType = 'TXT'
elif currentSelection == 'spfRecord':
fetchType = 'SPF'
elif currentSelection == 'nsRecord':
fetchType = 'NS'
elif currentSelection == 'soaRecord':
fetchType = 'SOA'
elif currentSelection == 'srvRecord':
fetchType = 'SRV'
elif currentSelection == 'caaRecord':
fetchType = 'CAA'
try:
dns_records = cf.zones.dns_records.get(zone_id, params={'per_page':50, 'type':fetchType})
except BaseException as e:
final_json = json.dumps({'status': 0, 'fetchStatus': 0, 'error_message': str(e), "data": '[]'})
return HttpResponse(final_json)
prog = re.compile('\.*' + zone_name + '$')
dns_records = sorted(dns_records, key=lambda v: prog.sub('', v['name']) + '_' + v['type'])
json_data = "["
checker = 0
for dns_record in dns_records:
if dns_record['ttl'] == 1:
ttl = 'AUTO'
else:
ttl = dns_record['ttl']
dic = {'id': dns_record['id'],
'type': dns_record['type'],
'name': dns_record['name'],
'content': dns_record['content'],
'priority': '1400',
'ttl': ttl,
'proxy': dns_record['proxied'],
'proxiable': dns_record['proxiable']
}
if checker == 0:
json_data = json_data + json.dumps(dic)
checker = 1
else:
json_data = json_data + ',' + json.dumps(dic)
json_data = json_data + ']'
final_json = json.dumps({'status': 1, 'fetchStatus': 1, 'error_message': "None", "data": json_data})
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'fetchStatus': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def deleteDNSRecordCloudFlare(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('fetchStatus', 0)
zoneDomain = data['selectedZone']
id = data['id']
admin = Administrator.objects.get(pk=userID)
self.admin = admin
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
self.loadCFKeys()
params = {'name': zoneDomain, 'per_page': 50}
cf = CloudFlare.CloudFlare(email=self.email, token=self.key)
try:
zones = cf.zones.get(params=params)
except BaseException as e:
final_json = json.dumps({'status': 0, 'delete_status': 0, 'error_message': str(e), "data": '[]'})
return HttpResponse(final_json)
for zone in sorted(zones, key=lambda v: v['name']):
zone_id = zone['id']
cf.zones.dns_records.delete(zone_id, id)
final_dic = {'status': 1, 'delete_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'delete_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def addDNSRecordCloudFlare(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('add_status', 0)
zoneDomain = data['selectedZone']
recordType = data['recordType']
recordName = data['recordName']
ttl = int(data['ttl'])
if ttl < 0:
raise ValueError("TTL: The item must be greater than 0")
elif ttl > 86400:
raise ValueError("TTL: The item must be lesser than 86401")
admin = Administrator.objects.get(pk=userID)
self.admin = admin
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
## Get zone
self.loadCFKeys()
params = {'name': zoneDomain, 'per_page': 50}
cf = CloudFlare.CloudFlare(email=self.email, token=self.key)
try:
zones = cf.zones.get(params=params)
except BaseException as e:
final_json = json.dumps({'status': 0, 'delete_status': 0, 'error_message': str(e), "data": '[]'})
return HttpResponse(final_json)
for zone in sorted(zones, key=lambda v: v['name']):
zone = zone['id']
value = ""
if recordType == "A":
recordContentA = data['recordContentA'] ## IP or pointing value
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentA, 0, ttl)
elif recordType == "MX":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentMX = data['recordContentMX']
priority = data['priority']
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentMX, priority, ttl)
elif recordType == "AAAA":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentAAAA = data['recordContentAAAA'] ## IP or pointing value
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentAAAA, 0, ttl)
elif recordType == "CNAME":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentCNAME = data['recordContentCNAME'] ## IP or pointing value
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentCNAME, 0, ttl)
elif recordType == "SPF":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentSPF = data['recordContentSPF'] ## IP or pointing value
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentSPF, 0, ttl)
elif recordType == "TXT":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentTXT = data['recordContentTXT'] ## IP or pointing value
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentTXT, 0, ttl)
elif recordType == "SOA":
recordContentSOA = data['recordContentSOA']
DNS.createDNSRecordCloudFlare(cf, zone, recordName, recordType, recordContentSOA, 0, ttl)
elif recordType == "NS":
recordContentNS = data['recordContentNS']
if recordContentNS == "@":
recordContentNS = "ns1." + zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?',
recordContentNS, M | I):
recordContentNS = recordContentNS
else:
recordContentNS = recordContentNS + "." + zoneDomain
DNS.createDNSRecordCloudFlare(cf, zone, recordName, recordType, recordContentNS, 0, ttl)
elif recordType == "SRV":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentSRV = data['recordContentSRV']
priority = data['priority']
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentSRV, priority, ttl)
elif recordType == "CAA":
if recordName == "@":
value = zoneDomain
## re.match
elif match(r'([\da-z\.-]+\.[a-z\.]{2,12}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*[\/\?]?', recordName,
M | I):
value = recordName
else:
value = recordName + "." + zoneDomain
recordContentCAA = data['recordContentCAA'] ## IP or pointing value
DNS.createDNSRecordCloudFlare(cf, zone, value, recordType, recordContentCAA, 0, ttl)
final_dic = {'status': 1, 'add_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'add_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def syncCF(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('add_status', 0)
zoneDomain = data['selectedZone']
admin = Administrator.objects.get(pk=userID)
self.admin = admin
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
## Get zone
dns = DNS()
status, error = dns.cfTemplate(zoneDomain, admin)
if status == 1:
final_dic = {'status': 1, 'error_message': 'None'}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
else:
final_dic = {'status': 0, 'error_message': error}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def enableProxy(self, userID = None, data = None):
try:
currentACL = ACLManager.loadedACL(userID)
if ACLManager.currentContextPermission(currentACL, 'addDeleteRecords') == 0:
return ACLManager.loadErrorJson('fetchStatus', 0)
zoneDomain = data['selectedZone']
name = data['name']
value = data['value']
admin = Administrator.objects.get(pk=userID)
self.admin = admin
if ACLManager.checkOwnershipZone(zoneDomain, admin, currentACL) == 1:
pass
else:
return ACLManager.loadErrorJson()
self.loadCFKeys()
params = {'name': zoneDomain, 'per_page': 50}
cf = CloudFlare.CloudFlare(email=self.email, token=self.key)
## Get zone
zones = cf.zones.get(params=params)
zone = zones[0]
##
zone_id = zone['id']
params = {'name': name}
dns_records = cf.zones.dns_records.get(zone_id, params=params)
##
if value == True:
new_r_proxied_flag = False
else:
new_r_proxied_flag = True
for dns_record in dns_records:
r_name = dns_record['name']
r_type = dns_record['type']
r_content = dns_record['content']
r_ttl = dns_record['ttl']
r_proxied = dns_record['proxied']
if r_proxied == new_r_proxied_flag:
# Nothing to do
continue
dns_record_id = dns_record['id']
new_dns_record = {
'type': r_type,
'name': r_name,
'content': r_content,
'ttl': r_ttl,
'proxied': new_r_proxied_flag
}
cf.zones.dns_records.put(zone_id, dns_record_id, data=new_dns_record)
final_dic = {'status': 1, 'delete_status': 1, 'error_message': "None"}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
except BaseException as msg:
final_dic = {'status': 0, 'delete_status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def installPowerDNS(self):
try:
if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
command = 'systemctl stop systemd-resolved'
ProcessUtilities.executioner(command, 'root', True)
command = 'systemctl disable systemd-resolved.service'
ProcessUtilities.executioner(command, 'root', True)
if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu or ProcessUtilities.ubuntu20:
command = 'DEBIAN_FRONTEND=noninteractive apt-get -y purge pdns-server pdns-backend-mysql -y'
ProcessUtilities.executioner(command, 'root', True)
else:
command = 'yum -y erase pdns pdns-backend-mysql'
ProcessUtilities.executioner(command, 'root', True)
#### new install
if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
try:
os.rename('/etc/resolv.conf', 'etc/resolved.conf')
except OSError as e:
if e.errno != errno.EEXIST and e.errno != errno.ENOENT:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], "[ERROR] Unable to rename /etc/resolv.conf to install PowerDNS: " +
str(e) + "[404]")
return 0
try:
os.remove('/etc/resolv.conf')
except OSError as e1:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
"[ERROR] Unable to remove existing /etc/resolv.conf to install PowerDNS: " +
str(e1) + "[404]")
return 0
if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu or ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu20:
# Update package list first
command = "DEBIAN_FRONTEND=noninteractive apt-get update"
ProcessUtilities.executioner(command, 'root', True)
command = "DEBIAN_FRONTEND=noninteractive apt-get -y install pdns-server pdns-backend-mysql"
result = ProcessUtilities.executioner(command, 'root', True)
# Ensure service is stopped after installation for configuration
command = 'systemctl stop pdns || true'
ProcessUtilities.executioner(command, 'root', True)
return 1
else:
command = 'yum -y install pdns pdns-backend-mysql'
ProcessUtilities.executioner(command, 'root', True)
return 1
except BaseException as msg:
logging.CyberCPLogFileWriter.writeToFile('[ERROR] ' + str(msg) + " [installPowerDNS]")
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'[ERROR] ' + str(msg) + " [installPowerDNS][404]")
return 0
def installPowerDNSConfigurations(self, mysqlPassword):
try:
### let see if this is needed the chdir
cwd = os.getcwd()
os.chdir('/usr/local/CyberCP/install')
if ProcessUtilities.decideDistro() == ProcessUtilities.centos or ProcessUtilities.decideDistro() == ProcessUtilities.cent8:
dnsPath = "/etc/pdns/pdns.conf"
else:
dnsPath = "/etc/powerdns/pdns.conf"
# Ensure directory exists for Ubuntu
dnsDir = os.path.dirname(dnsPath)
if not os.path.exists(dnsDir):
try:
os.makedirs(dnsDir, mode=0o755)
except OSError as e:
if e.errno != errno.EEXIST:
raise
import shutil
# Backup existing config if it exists
if os.path.exists(dnsPath):
try:
shutil.move(dnsPath, dnsPath + '.bak')
except:
os.remove(dnsPath)
shutil.copy("dns-one/pdns.conf", dnsPath)
# Verify the file was copied and has MySQL backend configuration
try:
with open(dnsPath, "r") as f:
content = f.read()
if not content or "launch=gmysql" not in content:
writeToFile.writeToFile("PowerDNS config incomplete, attempting to fix...")
logging.InstallLog.writeToFile("PowerDNS config incomplete, fixing...")
# Directly write the essential MySQL configuration
mysql_config = """# PowerDNS MySQL Backend Configuration
launch=gmysql
gmysql-host=localhost
gmysql-port=3306
gmysql-user=cyberpanel
gmysql-password=""" + mysqlPassword + """
gmysql-dbname=cyberpanel
# Basic PowerDNS settings
daemon=no
guardian=no
setgid=pdns
setuid=pdns
"""
# Write complete config
with open(dnsPath, "w") as f:
f.write(mysql_config)
writeToFile.writeToFile("MySQL backend configuration written directly")
except Exception as e:
writeToFile.writeToFile("Warning: Could not verify config content: " + str(e))
# Continue anyway as the file copy might have worked
data = open(dnsPath, "r").readlines()
writeDataToFile = open(dnsPath, "w")
dataWritten = "gmysql-password=" + mysqlPassword + "\n"
for items in data:
if items.find("gmysql-password") > -1:
writeDataToFile.writelines(dataWritten)
else:
writeDataToFile.writelines(items)
# if self.distro == ubuntu:
# os.fchmod(writeDataToFile.fileno(), stat.S_IRUSR | stat.S_IWUSR)
writeDataToFile.close()
if self.remotemysql == 'ON':
command = "sed -i 's|gmysql-host=localhost|gmysql-host=%s|g' %s" % (self.mysqlhost, dnsPath)
ProcessUtilities.executioner(command, 'root', True)
command = "sed -i 's|gmysql-port=3306|gmysql-port=%s|g' %s" % (self.mysqlport, dnsPath)
ProcessUtilities.executioner(command, 'root', True)
# Set proper permissions for PowerDNS config
if ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu or ProcessUtilities.decideDistro() == ProcessUtilities.ubuntu20:
# Ensure pdns user/group exists
command = 'id -u pdns &>/dev/null || useradd -r -s /usr/sbin/nologin pdns'
ProcessUtilities.executioner(command, 'root', True)
command = 'chown root:pdns %s' % dnsPath
ProcessUtilities.executioner(command, 'root', True)
command = 'chmod 640 %s' % dnsPath
ProcessUtilities.executioner(command, 'root', True)
return 1
except IOError as msg:
logging.CyberCPLogFileWriter.writeToFile('[ERROR] ' + str(msg) + " [installPowerDNSConfigurations]")
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'[ERROR] ' + str(msg) + " [installPowerDNSConfigurations][404]")
return 0
def startPowerDNS(self):
############## Start PowerDNS ######################
command = 'systemctl enable pdns'
ProcessUtilities.executioner(command)
# Give PowerDNS time to read configuration
import time
time.sleep(2)
command = 'systemctl start pdns'
result = ProcessUtilities.executioner(command)
# Check if service started successfully
command = 'systemctl is-active pdns'
output = ProcessUtilities.outputExecutioner(command)
if output.strip() != 'active':
logging.CyberCPLogFileWriter.writeToFile('[ERROR] PowerDNS failed to start. Service status: ' + output)
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'[ERROR] PowerDNS service failed to start properly [404]')
return 0
return 1
def ResetDNSConfigurations(self):
try:
### Check if remote or local mysql
passFile = "/etc/cyberpanel/mysqlPassword"
try:
jsonData = json.loads(ProcessUtilities.outputExecutioner('cat %s' % (passFile)))
self.mysqluser = jsonData['mysqluser']
self.mysqlpassword = jsonData['mysqlpassword']
self.mysqlport = jsonData['mysqlport']
self.mysqlhost = jsonData['mysqlhost']
self.remotemysql = 'ON'
if self.mysqlhost.find('rds.amazon') > -1:
self.RDS = 1
## Also set localhost to this server
ipFile = "/etc/cyberpanel/machineIP"
f = open(ipFile)
ipData = f.read()
ipAddressLocal = ipData.split('\n', 1)[0]
self.LOCALHOST = ipAddressLocal
except BaseException as msg:
self.remotemysql = 'OFF'
if os.path.exists(ProcessUtilities.debugPath):
logging.CyberCPLogFileWriter.writeToFile('%s. [setupConnection:75]' % (str(msg)))
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Removing and re-installing DNS..,5')
if self.installPowerDNS() == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'installPowerDNS failed. [404].')
return 0
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Resetting configurations..,40')
import sys
sys.path.append('/usr/local/CyberCP')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
from CyberCP import settings
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Configurations reset..,70')
if self.installPowerDNSConfigurations(settings.DATABASES['default']['PASSWORD']) == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'installPowerDNSConfigurations failed. [404].')
return 0
if self.startPowerDNS() == 0:
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
'startPowerDNS failed. [404].')
return 0
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Fixing permissions..,90')
ACLManager.fixPermissions()
logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], 'Completed [200].')
except BaseException as msg:
final_dic = {'status': 0, 'error_message': str(msg)}
final_json = json.dumps(final_dic)
return HttpResponse(final_json)
def main():
parser = argparse.ArgumentParser(description='CyberPanel')
parser.add_argument('function', help='Specify a function to call!')
parser.add_argument('--tempStatusPath', help='Path of temporary status file.')
args = parser.parse_args()
if args.function == "ResetDNSConfigurations":
extraArgs = {'tempStatusPath': args.tempStatusPath}
ftp = DNSManager(extraArgs)
ftp.ResetDNSConfigurations()
if __name__ == "__main__":
main()