mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-06 21:35:55 +01:00
Remove SECURITY_INSTALLATION.md and implement SSL reconciliation features in manageSSL module. Add new views and URLs for SSL reconciliation, enhance mobile responsiveness in templates, and update SSL utilities for improved functionality. Update upgrade script for scheduled SSL reconciliation tasks.
This commit is contained in:
@@ -1,437 +1,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/local/CyberCP/bin/python
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
|
||||
from plogical.httpProc import httpProc
|
||||
from websiteFunctions.models import Websites, ChildDomains
|
||||
from loginSystem.models import Administrator
|
||||
from plogical.virtualHostUtilities import virtualHostUtilities
|
||||
from django.http import HttpResponse
|
||||
import json
|
||||
sys.path.append('/usr/local/CyberCP')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings")
|
||||
django.setup()
|
||||
|
||||
from django.shortcuts import render, HttpResponse
|
||||
from plogical.acl import ACLManager
|
||||
from plogical.processUtilities import ProcessUtilities
|
||||
|
||||
# Create your views here.
|
||||
|
||||
def loadSSLHome(request):
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
proc = httpProc(request, 'manageSSL/index.html',
|
||||
currentACL, 'admin')
|
||||
return proc.render()
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
from plogical.sslReconcile import SSLReconcile
|
||||
from plogical.sslUtilities import sslUtilities
|
||||
import json
|
||||
|
||||
|
||||
def manageSSL(request):
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
websitesName = ACLManager.findAllSites(currentACL, userID)
|
||||
proc = httpProc(request, 'manageSSL/manageSSL.html',
|
||||
{'websiteList': websitesName}, 'manageSSL')
|
||||
return proc.render()
|
||||
|
||||
def v2ManageSSL(request):
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
websitesName = ACLManager.findAllSites(currentACL, userID)
|
||||
|
||||
data = {}
|
||||
|
||||
if ACLManager.CheckForPremFeature('all'):
|
||||
data['PremStat'] = 1
|
||||
else:
|
||||
data['PremStat'] = 0
|
||||
|
||||
if request.method == 'POST':
|
||||
SAVED_CF_Key = request.POST.get('SAVED_CF_Key')
|
||||
SAVED_CF_Email = request.POST.get('SAVED_CF_Email')
|
||||
from plogical.dnsUtilities import DNS
|
||||
DNS.ConfigureCloudflareInAcme(SAVED_CF_Key, SAVED_CF_Email)
|
||||
data['SaveSuccess'] = 1
|
||||
|
||||
|
||||
RetStatus, SAVED_CF_Key, SAVED_CF_Email = ACLManager.FetchCloudFlareAPIKeyFromAcme()
|
||||
from plogical.dnsUtilities import DNS
|
||||
DNS.ConfigurePowerDNSInAcme()
|
||||
|
||||
data['SAVED_CF_Key'] = SAVED_CF_Key
|
||||
data['SAVED_CF_Email'] = SAVED_CF_Email
|
||||
data['websiteList'] = websitesName
|
||||
|
||||
proc = httpProc(request, 'manageSSL/v2ManageSSL.html',
|
||||
data, 'manageSSL')
|
||||
return proc.render()
|
||||
|
||||
def v2IssueSSL(request):
|
||||
def sslReconcile(request):
|
||||
"""SSL Reconciliation interface"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
try:
|
||||
if ACLManager.CheckForPremFeature('all'):
|
||||
if request.method == 'POST':
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
currentACL = ACLManager.loadedACL(request.user.pk)
|
||||
admin = ACLManager.loadedAdmin(request.user.pk)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif currentACL['manageSSL'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson('SSL', 0)
|
||||
if ACLManager.currentContextPermission(currentACL, 'sslReconcile') == 0:
|
||||
return ACLManager.loadErrorJson('sslReconcile', 0)
|
||||
|
||||
data = json.loads(request.body)
|
||||
virtualHost = data['virtualHost']
|
||||
return render(request, 'manageSSL/sslReconcile.html', {
|
||||
'acls': currentACL,
|
||||
'admin': admin
|
||||
})
|
||||
|
||||
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg) + " [sslReconcile]")
|
||||
return ACLManager.loadErrorJson('sslReconcile', 0)
|
||||
|
||||
try:
|
||||
website = ChildDomains.objects.get(domain=virtualHost)
|
||||
adminEmail = website.master.adminEmail
|
||||
path = website.path
|
||||
except:
|
||||
website = Websites.objects.get(domain=virtualHost)
|
||||
adminEmail = website.adminEmail
|
||||
path = "/home/" + virtualHost + "/public_html"
|
||||
|
||||
## ssl issue
|
||||
def reconcileAllSSL(request):
|
||||
"""Reconcile SSL for all domains"""
|
||||
try:
|
||||
currentACL = ACLManager.loadedACL(request.user.pk)
|
||||
admin = ACLManager.loadedAdmin(request.user.pk)
|
||||
|
||||
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
|
||||
execPath = execPath + " issueSSLv2 --virtualHostName " + virtualHost + " --administratorEmail " + adminEmail + " --path " + path
|
||||
output = ProcessUtilities.outputExecutioner(execPath)
|
||||
if ACLManager.currentContextPermission(currentACL, 'sslReconcile') == 0:
|
||||
return ACLManager.loadErrorJson('sslReconcile', 0)
|
||||
|
||||
if output.find("1,") > -1:
|
||||
## ssl issue ends
|
||||
# Run SSL reconciliation
|
||||
success = SSLReconcile.reconcile_all()
|
||||
|
||||
if success:
|
||||
data_ret = {'reconcileStatus': 1, 'error_message': "SSL reconciliation completed successfully"}
|
||||
else:
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': "SSL reconciliation failed. Check logs for details."}
|
||||
|
||||
website.ssl = 1
|
||||
website.save()
|
||||
|
||||
# Extract detailed logs from output
|
||||
logs = output.split("1,", 1)[1] if "1," in output else output
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
data_ret = {'status': 1, "SSL": 1,
|
||||
'error_message': "None", 'sslLogs': logs, 'fullOutput': output}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
else:
|
||||
# Parse error details from output
|
||||
error_message = output
|
||||
detailed_error = "SSL issuance failed"
|
||||
|
||||
# Check for common ACME errors
|
||||
if "Rate limit" in output or "rate limit" in output:
|
||||
detailed_error = "Let's Encrypt rate limit exceeded. Please wait before retrying."
|
||||
elif "DNS problem" in output or "NXDOMAIN" in output:
|
||||
detailed_error = "DNS validation failed. Please ensure your domain points to this server."
|
||||
elif "Connection refused" in output or "Connection timeout" in output:
|
||||
detailed_error = "Could not connect to ACME server. Check your firewall settings."
|
||||
elif "Unauthorized" in output or "authorization" in output:
|
||||
detailed_error = "Domain authorization failed. Verify domain ownership and DNS settings."
|
||||
elif "CAA record" in output:
|
||||
detailed_error = "CAA record prevents issuance. Check your DNS CAA records."
|
||||
elif "Challenge failed" in output or "challenge failed" in output:
|
||||
detailed_error = "ACME challenge failed. Ensure port 80 is accessible and .well-known path is not blocked."
|
||||
elif "Invalid response" in output:
|
||||
detailed_error = "Invalid response from ACME challenge. Check your web server configuration."
|
||||
else:
|
||||
# Try to extract the actual error message
|
||||
if "0," in output:
|
||||
error_parts = output.split("0,", 1)
|
||||
if len(error_parts) > 1:
|
||||
detailed_error = error_parts[1].strip()
|
||||
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': detailed_error,
|
||||
'sslLogs': output,
|
||||
'fullOutput': output,
|
||||
'technicalDetails': error_message}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except KeyError:
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg) + " [reconcileAllSSL]")
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
def issueSSL(request):
|
||||
def reconcileDomainSSL(request):
|
||||
"""Reconcile SSL for a specific domain"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
try:
|
||||
if request.method == 'POST':
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
currentACL = ACLManager.loadedACL(request.user.pk)
|
||||
admin = ACLManager.loadedAdmin(request.user.pk)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif currentACL['manageSSL'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson('SSL', 0)
|
||||
if ACLManager.currentContextPermission(currentACL, 'sslReconcile') == 0:
|
||||
return ACLManager.loadErrorJson('sslReconcile', 0)
|
||||
|
||||
data = json.loads(request.body)
|
||||
virtualHost = data['virtualHost']
|
||||
|
||||
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
try:
|
||||
website = ChildDomains.objects.get(domain=virtualHost)
|
||||
adminEmail = website.master.adminEmail
|
||||
path = website.path
|
||||
except:
|
||||
website = Websites.objects.get(domain=virtualHost)
|
||||
adminEmail = website.adminEmail
|
||||
path = "/home/" + virtualHost + "/public_html"
|
||||
|
||||
## ssl issue
|
||||
|
||||
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
|
||||
execPath = execPath + " issueSSL --virtualHostName " + virtualHost + " --administratorEmail " + adminEmail + " --path " + path
|
||||
output = ProcessUtilities.outputExecutioner(execPath)
|
||||
|
||||
if output.find("1,None") > -1:
|
||||
pass
|
||||
else:
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': output}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
## ssl issue ends
|
||||
|
||||
website.ssl = 1
|
||||
website.save()
|
||||
|
||||
data_ret = {'status': 1, "SSL": 1,
|
||||
'error_message': "None"}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
domain = request.POST.get('domain')
|
||||
if not domain:
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': "Domain not specified"}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except KeyError:
|
||||
data_ret = {'status': 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
|
||||
# Run SSL reconciliation for specific domain
|
||||
success = SSLReconcile.reconcile_domain(domain)
|
||||
|
||||
if success:
|
||||
data_ret = {'reconcileStatus': 1, 'error_message': f"SSL reconciliation completed for {domain}"}
|
||||
else:
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': f"SSL reconciliation failed for {domain}. Check logs for details."}
|
||||
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg) + " [reconcileDomainSSL]")
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
def sslForHostName(request):
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
websitesName = ACLManager.findAllSites(currentACL, userID, 1)
|
||||
proc = httpProc(request, 'manageSSL/sslForHostName.html',
|
||||
{'websiteList': websitesName}, 'hostnameSSL')
|
||||
return proc.render()
|
||||
|
||||
|
||||
def obtainHostNameSSL(request):
|
||||
def fixACMEContexts(request):
|
||||
"""Fix ACME challenge contexts for all domains"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
try:
|
||||
if request.method == 'POST':
|
||||
currentACL = ACLManager.loadedACL(request.user.pk)
|
||||
admin = ACLManager.loadedAdmin(request.user.pk)
|
||||
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
if ACLManager.currentContextPermission(currentACL, 'sslReconcile') == 0:
|
||||
return ACLManager.loadErrorJson('sslReconcile', 0)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif currentACL['hostnameSSL'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson('SSL', 0)
|
||||
from websiteFunctions.models import Websites
|
||||
|
||||
fixed_count = 0
|
||||
failed_domains = []
|
||||
|
||||
for website in Websites.objects.all():
|
||||
if sslUtilities.fix_acme_challenge_context(website.domain):
|
||||
fixed_count += 1
|
||||
else:
|
||||
failed_domains.append(website.domain)
|
||||
|
||||
if failed_domains:
|
||||
data_ret = {
|
||||
'reconcileStatus': 1,
|
||||
'error_message': f"Fixed ACME contexts for {fixed_count} domains. Failed: {', '.join(failed_domains)}"
|
||||
}
|
||||
else:
|
||||
data_ret = {
|
||||
'reconcileStatus': 1,
|
||||
'error_message': f"Fixed ACME contexts for {fixed_count} domains successfully"
|
||||
}
|
||||
|
||||
data = json.loads(request.body)
|
||||
virtualHost = data['virtualHost']
|
||||
|
||||
try:
|
||||
website = Websites.objects.get(domain=virtualHost)
|
||||
path = "/home/" + virtualHost + "/public_html"
|
||||
except:
|
||||
website = ChildDomains.objects.get(domain=virtualHost)
|
||||
path = website.path
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
|
||||
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
## ssl issue
|
||||
|
||||
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
|
||||
execPath = execPath + " issueSSLForHostName --virtualHostName " + virtualHost + " --path " + path
|
||||
output = ProcessUtilities.outputExecutioner(execPath)
|
||||
|
||||
if output.find("1,None") > -1:
|
||||
data_ret = {"status": 1, "SSL": 1,
|
||||
'error_message': "None"}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
else:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': output}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
## ssl issue ends
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except KeyError:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
|
||||
def sslForMailServer(request):
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
websitesName = ACLManager.findAllSites(currentACL, userID)
|
||||
websitesName = websitesName + ACLManager.findChildDomains(websitesName)
|
||||
|
||||
proc = httpProc(request, 'manageSSL/sslForMailServer.html',
|
||||
{'websiteList': websitesName}, 'mailServerSSL')
|
||||
return proc.render()
|
||||
|
||||
|
||||
def obtainMailServerSSL(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
try:
|
||||
if request.method == 'POST':
|
||||
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif currentACL['mailServerSSL'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson('SSL', 0)
|
||||
|
||||
data = json.loads(request.body)
|
||||
virtualHost = data['virtualHost']
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
path = "/home/" + virtualHost + "/public_html"
|
||||
|
||||
## ssl issue
|
||||
|
||||
execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/virtualHostUtilities.py"
|
||||
execPath = execPath + " issueSSLForMailServer --virtualHostName " + virtualHost + " --path " + path
|
||||
output = ProcessUtilities.outputExecutioner(execPath)
|
||||
|
||||
if output.find("1,None") > -1:
|
||||
data_ret = {"status": 1, "SSL": 1,
|
||||
'error_message': "None"}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
else:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': output}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
## ssl issue ends
|
||||
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except KeyError as msg:
|
||||
data_ret = {"status": 0, "SSL": 0,
|
||||
'error_message': str(msg)}
|
||||
except BaseException as msg:
|
||||
logging.writeToFile(str(msg) + " [fixACMEContexts]")
|
||||
data_ret = {'reconcileStatus': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
def getSSLDetails(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
try:
|
||||
if request.method == 'POST':
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
if currentACL['admin'] == 1:
|
||||
pass
|
||||
elif currentACL['manageSSL'] == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson('SSL', 0)
|
||||
|
||||
data = json.loads(request.body)
|
||||
virtualHost = data['virtualHost']
|
||||
|
||||
if ACLManager.checkOwnership(virtualHost, admin, currentACL) == 1:
|
||||
pass
|
||||
else:
|
||||
return ACLManager.loadErrorJson()
|
||||
|
||||
try:
|
||||
website = ChildDomains.objects.get(domain=virtualHost)
|
||||
except:
|
||||
website = Websites.objects.get(domain=virtualHost)
|
||||
|
||||
try:
|
||||
import OpenSSL
|
||||
from datetime import datetime
|
||||
filePath = '/etc/letsencrypt/live/%s/fullchain.pem' % (virtualHost)
|
||||
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
|
||||
open(filePath, 'r').read())
|
||||
expireData = x509.get_notAfter().decode('ascii')
|
||||
finalDate = datetime.strptime(expireData, '%Y%m%d%H%M%SZ')
|
||||
|
||||
now = datetime.now()
|
||||
diff = finalDate - now
|
||||
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'hasSSL': True,
|
||||
'days': str(diff.days),
|
||||
'authority': x509.get_issuer().get_components()[1][1].decode('utf-8'),
|
||||
'expiryDate': finalDate.strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
if data_ret['authority'] == 'Denial':
|
||||
data_ret['authority'] = 'SELF-SIGNED SSL'
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {
|
||||
'status': 1,
|
||||
'hasSSL': False,
|
||||
'error_message': str(msg)
|
||||
}
|
||||
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
|
||||
except BaseException as msg:
|
||||
data_ret = {'status': 0, 'error_message': str(msg)}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
except KeyError:
|
||||
data_ret = {'status': 0, 'error_message': 'Not logged in'}
|
||||
json_data = json.dumps(data_ret)
|
||||
return HttpResponse(json_data)
|
||||
return HttpResponse(json_data)
|
||||
Reference in New Issue
Block a user