From 651324b464a70a5863f4d98f932878c9741c3c69 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Fri, 8 Aug 2025 00:56:41 +0500 Subject: [PATCH] bug fix: https://github.com/usmannasir/cyberpanel/issues/1471 --- .idea/workspace.xml | 9 +- loginSystem/views.py | 2 +- plogical/adminPass.py | 2 +- plogical/customACME.py | 344 +++++++++++++++++++++----------------- plogical/sslUtilities.py | 210 +++++++++++++---------- plogical/sslv2.py | 38 +++-- websiteFunctions/views.py | 189 +++++++++++++++++---- 7 files changed, 495 insertions(+), 299 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 5fc3522e1..78540cb15 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -6,6 +6,12 @@ + + + + + + diff --git a/loginSystem/views.py b/loginSystem/views.py index a49bac1cf..750a6e14c 100644 --- a/loginSystem/views.py +++ b/loginSystem/views.py @@ -222,7 +222,7 @@ def loadLoginPage(request): token = hashPassword.generateToken('admin', '1234567') - email = 'example@example.org' + email = 'admin@cyberpanel.net' admin = Administrator(userName="admin", password=password, type=1, email=email, firstName="Cyber", lastName="Panel", acl=acl, token=token) admin.save() diff --git a/plogical/adminPass.py b/plogical/adminPass.py index 3acb40b16..06f2c71e7 100644 --- a/plogical/adminPass.py +++ b/plogical/adminPass.py @@ -47,7 +47,7 @@ def main(): acl = ACL.objects.get(name='admin') token = hashPassword.generateToken('admin', adminPass) - email = 'example@example.org' + email = 'admin@cyberpanel.net' admin = Administrator(userName="admin", password=hashPassword.hash_password(adminPass), type=1, email=email, firstName="Cyber", lastName="Panel", acl=acl, token=token) admin.save() diff --git a/plogical/customACME.py b/plogical/customACME.py index 8a7ca13b5..287f97793 100644 --- a/plogical/customACME.py +++ b/plogical/customACME.py @@ -17,15 +17,17 @@ from plogical import CyberCPLogFileWriter as logging from plogical.processUtilities import ProcessUtilities import socket + class CustomACME: def __init__(self, domain, admin_email, staging=False, provider='letsencrypt'): """Initialize CustomACME""" - logging.CyberCPLogFileWriter.writeToFile(f'Initializing CustomACME for domain: {domain}, email: {admin_email}, staging: {staging}, provider: {provider}') + logging.CyberCPLogFileWriter.writeToFile( + f'Initializing CustomACME for domain: {domain}, email: {admin_email}, staging: {staging}, provider: {provider}') self.domain = domain self.admin_email = admin_email self.staging = staging self.provider = provider - + # Set the ACME directory URL based on provider and staging flag if provider == 'zerossl': if staging: @@ -41,7 +43,7 @@ class CustomACME: else: self.acme_directory = "https://acme-v02.api.letsencrypt.org/directory" logging.CyberCPLogFileWriter.writeToFile('Using Let\'s Encrypt production ACME directory') - + self.account_key = None self.account_url = None self.directory = None @@ -50,16 +52,17 @@ class CustomACME: self.authorizations = [] self.finalize_url = None self.certificate_url = None - + # Initialize paths self.cert_path = f'/etc/letsencrypt/live/{domain}' self.challenge_path = '/usr/local/lsws/Example/html/.well-known/acme-challenge' self.account_key_path = f'/etc/letsencrypt/accounts/{domain}.key' - logging.CyberCPLogFileWriter.writeToFile(f'Certificate path: {self.cert_path}, Challenge path: {self.challenge_path}') - + logging.CyberCPLogFileWriter.writeToFile( + f'Certificate path: {self.cert_path}, Challenge path: {self.challenge_path}') + # Create accounts directory if it doesn't exist os.makedirs('/etc/letsencrypt/accounts', exist_ok=True) - + def _generate_account_key(self): """Generate RSA account key""" try: @@ -82,7 +85,8 @@ class CustomACME: logging.CyberCPLogFileWriter.writeToFile(f'Fetching ACME directory from {self.acme_directory}') response = requests.get(self.acme_directory) self.directory = response.json() - logging.CyberCPLogFileWriter.writeToFile(f'Successfully fetched ACME directory: {json.dumps(self.directory)}') + logging.CyberCPLogFileWriter.writeToFile( + f'Successfully fetched ACME directory: {json.dumps(self.directory)}') return True except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error getting directory: {str(e)}') @@ -93,19 +97,19 @@ class CustomACME: try: logging.CyberCPLogFileWriter.writeToFile('Getting new nonce...') response = requests.head(self.directory['newNonce']) - + # Check for nonce in headers (case-insensitive) nonce_header = None for header_name in ['Replay-Nonce', 'replay-nonce', 'REPLAY-NONCE']: if header_name in response.headers: nonce_header = header_name break - + if not nonce_header: # Log all available headers for debugging logging.CyberCPLogFileWriter.writeToFile(f'Available headers: {list(response.headers.keys())}') raise KeyError('Replay-Nonce header not found in response') - + self.nonce = response.headers[nonce_header] logging.CyberCPLogFileWriter.writeToFile(f'Successfully got nonce: {self.nonce}') return True @@ -119,22 +123,22 @@ class CustomACME: logging.CyberCPLogFileWriter.writeToFile(f'Creating JWS for URL: {url}') if payload is not None: logging.CyberCPLogFileWriter.writeToFile(f'Payload: {json.dumps(payload)}') - + # Get a fresh nonce for this request if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get fresh nonce') return None - + # Get the private key numbers logging.CyberCPLogFileWriter.writeToFile('Getting private key numbers...') private_numbers = self.account_key.private_numbers() public_numbers = private_numbers.public_numbers - + # Convert numbers to bytes logging.CyberCPLogFileWriter.writeToFile('Converting RSA numbers to bytes...') n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big') e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big') - + # Create JWK logging.CyberCPLogFileWriter.writeToFile('Creating JWK...') jwk_key = { @@ -144,14 +148,14 @@ class CustomACME: "alg": "RS256" } logging.CyberCPLogFileWriter.writeToFile(f'Created JWK: {json.dumps(jwk_key)}') - + # Create protected header protected = { "alg": "RS256", "url": url, "nonce": self.nonce } - + # Add either JWK or Key ID based on whether we have an account URL if self.account_url and url != self.directory['newAccount']: protected["kid"] = self.account_url @@ -159,13 +163,13 @@ class CustomACME: else: protected["jwk"] = jwk_key logging.CyberCPLogFileWriter.writeToFile('Using JWK for new account') - + # Encode protected header logging.CyberCPLogFileWriter.writeToFile('Encoding protected header...') protected_b64 = base64.urlsafe_b64encode( json.dumps(protected).encode('utf-8') ).decode('utf-8').rstrip('=') - + # For POST-as-GET requests, payload_b64 should be empty string if payload is None: payload_b64 = "" @@ -176,11 +180,11 @@ class CustomACME: payload_b64 = base64.urlsafe_b64encode( json.dumps(payload).encode('utf-8') ).decode('utf-8').rstrip('=') - + # Create signature input logging.CyberCPLogFileWriter.writeToFile('Creating signature input...') signature_input = f"{protected_b64}.{payload_b64}".encode('utf-8') - + # Sign the input logging.CyberCPLogFileWriter.writeToFile('Signing input...') signature = self.account_key.sign( @@ -188,26 +192,23 @@ class CustomACME: padding.PKCS1v15(), hashes.SHA256() ) - + # Encode signature logging.CyberCPLogFileWriter.writeToFile('Encoding signature...') signature_b64 = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=') - + # Create final JWS logging.CyberCPLogFileWriter.writeToFile('Creating final JWS...') jws = { "protected": protected_b64, + "payload": payload_b64, # Always include payload field, even if empty "signature": signature_b64 } - - # Only add payload if it exists - if payload is not None: - jws["payload"] = payload_b64 - + # Ensure the JWS is properly formatted jws_str = json.dumps(jws, separators=(',', ':')) logging.CyberCPLogFileWriter.writeToFile(f'Final JWS: {jws_str}') - + return jws_str except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error creating JWS: {str(e)}') @@ -257,16 +258,19 @@ class CustomACME: "termsOfServiceAgreed": True, "contact": [f"mailto:{self.admin_email}"] } - + # Check if External Account Binding is required (for ZeroSSL) - if self.provider == 'zerossl' and 'meta' in self.directory and 'externalAccountRequired' in self.directory['meta']: + if self.provider == 'zerossl' and 'meta' in self.directory and 'externalAccountRequired' in self.directory[ + 'meta']: if self.directory['meta']['externalAccountRequired']: - logging.CyberCPLogFileWriter.writeToFile('ZeroSSL requires External Account Binding, getting EAB credentials...') - + logging.CyberCPLogFileWriter.writeToFile( + 'ZeroSSL requires External Account Binding, getting EAB credentials...') + # Get EAB credentials from ZeroSSL eab_kid, eab_hmac_key = self._get_zerossl_eab_credentials() if not eab_kid or not eab_hmac_key: - logging.CyberCPLogFileWriter.writeToFile('Failed to get ZeroSSL EAB credentials, falling back to Let\'s Encrypt') + logging.CyberCPLogFileWriter.writeToFile( + 'Failed to get ZeroSSL EAB credentials, falling back to Let\'s Encrypt') # Fallback to Let's Encrypt self.provider = 'letsencrypt' self.acme_directory = "https://acme-v02.api.letsencrypt.org/directory" @@ -275,15 +279,15 @@ class CustomACME: if not self._get_nonce(): return False return self._create_account() - + # Add EAB to payload payload['externalAccountBinding'] = self._create_eab(eab_kid, eab_hmac_key) - + jws = self._create_jws(payload, self.directory['newAccount']) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for account creation') return False - + logging.CyberCPLogFileWriter.writeToFile('Sending account creation request...') headers = { 'Content-Type': 'application/jose+json' @@ -291,15 +295,17 @@ class CustomACME: response = requests.post(self.directory['newAccount'], data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Account creation response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Account creation response: {response.text}') - + if response.status_code == 201: self.account_url = response.headers['Location'] - logging.CyberCPLogFileWriter.writeToFile(f'Successfully created account. Account URL: {self.account_url}') + logging.CyberCPLogFileWriter.writeToFile( + f'Successfully created account. Account URL: {self.account_url}') # Save the account key for future use self._save_account_key() return True elif response.status_code == 429: - logging.CyberCPLogFileWriter.writeToFile('Rate limit hit for account creation. Using staging environment...') + logging.CyberCPLogFileWriter.writeToFile( + 'Rate limit hit for account creation. Using staging environment...') self.staging = True self.acme_directory = "https://acme-staging-v02.api.letsencrypt.org/directory" # Get new directory and nonce for staging @@ -323,7 +329,7 @@ class CustomACME: """Get External Account Binding credentials from ZeroSSL""" try: logging.CyberCPLogFileWriter.writeToFile('Getting ZeroSSL EAB credentials...') - + # Request EAB credentials from ZeroSSL API eab_url = 'https://api.zerossl.com/acme/eab-credentials-email' headers = { @@ -332,16 +338,16 @@ class CustomACME: data = { 'email': self.admin_email } - + response = requests.post(eab_url, headers=headers, data=data) logging.CyberCPLogFileWriter.writeToFile(f'ZeroSSL EAB response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'ZeroSSL EAB response: {response.text}') - + if response.status_code == 200: eab_data = response.json() if 'eab_kid' in eab_data and 'eab_hmac_key' in eab_data: return eab_data['eab_kid'], eab_data['eab_hmac_key'] - + return None, None except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error getting ZeroSSL EAB credentials: {str(e)}') @@ -351,52 +357,52 @@ class CustomACME: """Create External Account Binding for ZeroSSL""" try: logging.CyberCPLogFileWriter.writeToFile('Creating External Account Binding...') - + # Get the private key numbers private_numbers = self.account_key.private_numbers() public_numbers = private_numbers.public_numbers - + # Convert numbers to bytes n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big') e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big') - + # Create JWK jwk = { "kty": "RSA", "n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='), "e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('=') } - + # Create protected header for EAB protected = { "alg": "HS256", "kid": eab_kid, "url": self.directory['newAccount'] } - + # Encode protected header protected_b64 = base64.urlsafe_b64encode( json.dumps(protected).encode('utf-8') ).decode('utf-8').rstrip('=') - + # Encode JWK payload payload_b64 = base64.urlsafe_b64encode( json.dumps(jwk).encode('utf-8') ).decode('utf-8').rstrip('=') - + # Create signature using HMAC-SHA256 signature_input = f"{protected_b64}.{payload_b64}".encode('utf-8') hmac_key = base64.urlsafe_b64decode(eab_hmac_key + '==') # Add padding if needed signature = hmac.new(hmac_key, signature_input, hashlib.sha256).digest() signature_b64 = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=') - + # Create EAB object eab = { "protected": protected_b64, "payload": payload_b64, "signature": signature_b64 } - + logging.CyberCPLogFileWriter.writeToFile('Successfully created External Account Binding') return eab except Exception as e: @@ -411,12 +417,12 @@ class CustomACME: payload = { "identifiers": identifiers } - + jws = self._create_jws(payload, self.directory['newOrder']) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order creation') return False - + logging.CyberCPLogFileWriter.writeToFile('Sending order creation request...') headers = { 'Content-Type': 'application/jose+json' @@ -424,7 +430,7 @@ class CustomACME: response = requests.post(self.directory['newOrder'], data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Order creation response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Order creation response: {response.text}') - + if response.status_code == 201: self.order_url = response.headers['Location'] self.authorizations = response.json()['authorizations'] @@ -442,26 +448,26 @@ class CustomACME: """Handle HTTP-01 challenge""" try: logging.CyberCPLogFileWriter.writeToFile(f'Handling HTTP challenge: {json.dumps(challenge)}') - + # Get key authorization key_auth = self._get_key_authorization(challenge) if not key_auth: logging.CyberCPLogFileWriter.writeToFile('Failed to get key authorization') return False - + # Create challenge directory if it doesn't exist if not os.path.exists(self.challenge_path): logging.CyberCPLogFileWriter.writeToFile(f'Creating challenge directory: {self.challenge_path}') os.makedirs(self.challenge_path) - + # Write challenge file challenge_file = os.path.join(self.challenge_path, challenge['token']) logging.CyberCPLogFileWriter.writeToFile(f'Writing challenge file: {challenge_file}') - + # Write only the key authorization to the file with open(challenge_file, 'w') as f: f.write(key_auth) - + logging.CyberCPLogFileWriter.writeToFile('Successfully handled HTTP challenge') return True except Exception as e: @@ -483,22 +489,22 @@ class CustomACME: """Get key authorization for challenge""" try: logging.CyberCPLogFileWriter.writeToFile('Getting key authorization...') - + # Get the private key numbers private_numbers = self.account_key.private_numbers() public_numbers = private_numbers.public_numbers - + # Convert numbers to bytes n_bytes = public_numbers.n.to_bytes((public_numbers.n.bit_length() + 7) // 8, 'big') e_bytes = public_numbers.e.to_bytes((public_numbers.e.bit_length() + 7) // 8, 'big') - + # Create JWK without alg field jwk_key = { "kty": "RSA", "n": base64.urlsafe_b64encode(n_bytes).decode('utf-8').rstrip('='), "e": base64.urlsafe_b64encode(e_bytes).decode('utf-8').rstrip('=') } - + # Calculate the JWK thumbprint according to RFC 7638 # The thumbprint is a hash of the JWK (JSON Web Key) in a specific format # First, we create a dictionary with the required JWK parameters @@ -507,23 +513,23 @@ class CustomACME: "kty": "RSA", # Key type "n": base64.urlsafe_b64encode(public_numbers.n.to_bytes(256, 'big')).decode('utf-8').rstrip('=') } - + # Sort the JWK parameters alphabetically by key name # This ensures consistent thumbprint calculation regardless of parameter order sorted_jwk = json.dumps(jwk, sort_keys=True, separators=(',', ':')) - + # Calculate the SHA-256 hash of the sorted JWK # Example of what sorted_jwk might look like: # {"e":"AQAB","kty":"RSA","n":"tVKUtcx_n9rt5afY_2WFNVAu9fjD4xqX4Xm3dJz3XYb"} # The thumbprint will be a 32-byte SHA-256 hash of this string # For example, it might look like: b'x\x9c\x1d\x8f\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e\x8b\x1b\x1e' thumbprint = hashlib.sha256(sorted_jwk.encode('utf-8')).digest() - + # Encode the thumbprint in base64url format (RFC 4648) # This removes padding characters (=) and replaces + and / with - and _ # Example final thumbprint: "xJ0dj8sbHosbHosbHosbHos" thumbprint = base64.urlsafe_b64encode(thumbprint).decode('utf-8').rstrip('=') - + # Combine token and key authorization key_auth = f"{challenge['token']}.{thumbprint}" logging.CyberCPLogFileWriter.writeToFile(f'Key authorization: {key_auth}') @@ -534,31 +540,31 @@ class CustomACME: def _verify_challenge(self, challenge_url): """Verify challenge completion with the ACME server - + This function sends a POST request to the ACME server to verify that the challenge has been completed successfully. The challenge URL is provided by the ACME server when the challenge is created. - + Example challenge_url: "https://acme-v02.api.letsencrypt.org/acme/challenge/example.com/123456" - + The verification process: 1. Creates an empty payload (POST-as-GET request) 2. Creates a JWS (JSON Web Signature) with the payload 3. Sends the request to the ACME server 4. Checks the response status - + Returns: bool: True if challenge is verified successfully, False otherwise """ try: logging.CyberCPLogFileWriter.writeToFile(f'Verifying challenge at URL: {challenge_url}') - + # Create empty payload for POST-as-GET request # This is a special type of request where we want to GET a resource # but need to include a signature, so we use POST with an empty payload payload = {} - + # Create JWS (JSON Web Signature) for the request # Example JWS might look like: # { @@ -570,15 +576,15 @@ class CustomACME: if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for challenge verification') return False - + logging.CyberCPLogFileWriter.writeToFile('Sending challenge verification request...') - + # Set headers for the request # Content-Type: application/jose+json indicates we're sending a JWS headers = { 'Content-Type': 'application/jose+json' } - + # Send the verification request to the ACME server # Example response might look like: # { @@ -590,7 +596,7 @@ class CustomACME: response = requests.post(challenge_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Challenge verification response: {response.text}') - + # Check if the challenge was verified successfully # Status code 200 indicates success # The response will contain the challenge status and validation time @@ -609,12 +615,12 @@ class CustomACME: payload = { "csr": base64.urlsafe_b64encode(csr).decode('utf-8').rstrip('=') } - + jws = self._create_jws(payload, self.finalize_url) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order finalization') return False - + logging.CyberCPLogFileWriter.writeToFile('Sending order finalization request...') headers = { 'Content-Type': 'application/jose+json' @@ -622,7 +628,7 @@ class CustomACME: response = requests.post(self.finalize_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Order finalization response: {response.text}') - + if response.status_code == 200: # Wait for order to be processed max_attempts = 30 @@ -631,33 +637,36 @@ class CustomACME: if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check') return False - + # Use POST-as-GET for order status check jws = self._create_jws(None, self.order_url) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order status check') return False - + response = requests.post(self.order_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}') - + if response.status_code == 200: order_status = response.json().get('status') if order_status == 'valid': self.certificate_url = response.json().get('certificate') - logging.CyberCPLogFileWriter.writeToFile(f'Successfully finalized order. Certificate URL: {self.certificate_url}') + logging.CyberCPLogFileWriter.writeToFile( + f'Successfully finalized order. Certificate URL: {self.certificate_url}') return True elif order_status == 'invalid': logging.CyberCPLogFileWriter.writeToFile('Order validation failed') return False elif order_status == 'processing': - logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}') + logging.CyberCPLogFileWriter.writeToFile( + f'Order still processing, attempt {attempt + 1}/{max_attempts}') time.sleep(delay) continue - - logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}') + + logging.CyberCPLogFileWriter.writeToFile( + f'Order status check failed, attempt {attempt + 1}/{max_attempts}') time.sleep(delay) - + logging.CyberCPLogFileWriter.writeToFile('Order processing timed out') return False return False @@ -670,16 +679,31 @@ class CustomACME: try: logging.CyberCPLogFileWriter.writeToFile('Downloading certificate...') logging.CyberCPLogFileWriter.writeToFile(f'Certificate URL: {self.certificate_url}') - - # For certificate downloads, we can use a simple GET request - response = requests.get(self.certificate_url) + + # Get a fresh nonce for the request + if not self._get_nonce(): + logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for certificate download') + return None + + # Use POST-as-GET for certificate download (ACME v2 requirement) + jws = self._create_jws(None, self.certificate_url) + if not jws: + logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for certificate download') + return None + + headers = { + 'Content-Type': 'application/jose+json' + } + response = requests.post(self.certificate_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response headers: {response.headers}') - logging.CyberCPLogFileWriter.writeToFile(f'Certificate download response content: {response.text}') - + if response.status_code == 200: logging.CyberCPLogFileWriter.writeToFile('Successfully downloaded certificate') - return response.content + # The response should be the PEM-encoded certificate chain + return response.text.encode('utf-8') if isinstance(response.text, str) else response.content + else: + logging.CyberCPLogFileWriter.writeToFile(f'Certificate download failed: {response.text}') return None except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error downloading certificate: {str(e)}') @@ -693,19 +717,19 @@ class CustomACME: if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for challenge status check') return False - + # Use POST-as-GET for challenge status check jws = self._create_jws(None, challenge_url) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for challenge status check') return False - + headers = { 'Content-Type': 'application/jose+json' } response = requests.post(challenge_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Challenge status check response: {response.text}') - + if response.status_code == 200: challenge_status = response.json().get('status') if challenge_status == 'valid': @@ -714,10 +738,11 @@ class CustomACME: elif challenge_status == 'invalid': logging.CyberCPLogFileWriter.writeToFile('Challenge validation failed') return False - - logging.CyberCPLogFileWriter.writeToFile(f'Challenge still pending, attempt {attempt + 1}/{max_attempts}') + + logging.CyberCPLogFileWriter.writeToFile( + f'Challenge still pending, attempt {attempt + 1}/{max_attempts}') time.sleep(delay) - + logging.CyberCPLogFileWriter.writeToFile('Challenge validation timed out') return False except Exception as e: @@ -726,40 +751,40 @@ class CustomACME: def _check_dns_record(self, domain): """Check if a domain has valid DNS records - + This function performs multiple DNS checks to ensure the domain has valid DNS records. It includes: 1. A record (IPv4) check 2. AAAA record (IPv6) check 3. DNS caching prevention 4. Multiple DNS server checks - + Args: domain (str): The domain to check - + Returns: bool: True if valid DNS records are found, False otherwise """ try: logging.CyberCPLogFileWriter.writeToFile(f'Checking DNS records for domain: {domain}') - + # List of public DNS servers to check against dns_servers = [ - '8.8.8.8', # Google DNS - '1.1.1.1', # Cloudflare DNS - '208.67.222.222' # OpenDNS + '8.8.8.8', # Google DNS + '1.1.1.1', # Cloudflare DNS + '208.67.222.222' # OpenDNS ] - + # Function to check DNS record with specific DNS server def check_with_dns_server(server, record_type='A'): try: # Create a new socket for each check sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(5) # 5 second timeout - + # Set the DNS server sock.connect((server, 53)) - + # Create DNS query query = bytearray() # DNS header @@ -769,54 +794,54 @@ class CustomACME: query += b'\x00\x00' # Answer RRs: 0 query += b'\x00\x00' # Authority RRs: 0 query += b'\x00\x00' # Additional RRs: 0 - + # Domain name for part in domain.split('.'): query.append(len(part)) query.extend(part.encode()) query += b'\x00' # End of domain name - + # Query type and class if record_type == 'A': query += b'\x00\x01' # Type: A else: # AAAA query += b'\x00\x1c' # Type: AAAA query += b'\x00\x01' # Class: IN - + # Send query sock.send(query) - + # Receive response response = sock.recv(1024) - + # Check if we got a valid response if len(response) > 12: # Minimum DNS response size # Check if there are answers in the response answer_count = int.from_bytes(response[6:8], 'big') if answer_count > 0: return True - + return False except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS with server {server}: {str(e)}') return False finally: sock.close() - + # Check A records (IPv4) with multiple DNS servers a_record_found = False for server in dns_servers: if check_with_dns_server(server, 'A'): a_record_found = True break - + # Check AAAA records (IPv6) with multiple DNS servers aaaa_record_found = False for server in dns_servers: if check_with_dns_server(server, 'AAAA'): aaaa_record_found = True break - + # Also check with system's DNS resolver as a fallback try: # Try to resolve A record (IPv4) @@ -824,23 +849,23 @@ class CustomACME: a_record_found = True except socket.gaierror: pass - + try: # Try to resolve AAAA record (IPv6) socket.getaddrinfo(domain, None, socket.AF_INET6) aaaa_record_found = True except socket.gaierror: pass - + # Log the results if a_record_found: logging.CyberCPLogFileWriter.writeToFile(f'IPv4 DNS record found for domain: {domain}') if aaaa_record_found: logging.CyberCPLogFileWriter.writeToFile(f'IPv6 DNS record found for domain: {domain}') - + # Return True if either A or AAAA record is found return a_record_found or aaaa_record_found - + except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error checking DNS records: {str(e)}') return False @@ -853,19 +878,19 @@ class CustomACME: if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for order status check') return False - + # Use POST-as-GET for order status check jws = self._create_jws(None, self.order_url) if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for order status check') return False - + headers = { 'Content-Type': 'application/jose+json' } response = requests.post(self.order_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Order status check response: {response.text}') - + if response.status_code == 200: order_status = response.json().get('status') if order_status == 'valid': @@ -876,13 +901,15 @@ class CustomACME: logging.CyberCPLogFileWriter.writeToFile('Order validation failed') return False elif order_status == 'processing': - logging.CyberCPLogFileWriter.writeToFile(f'Order still processing, attempt {attempt + 1}/{max_attempts}') + logging.CyberCPLogFileWriter.writeToFile( + f'Order still processing, attempt {attempt + 1}/{max_attempts}') time.sleep(delay) continue - - logging.CyberCPLogFileWriter.writeToFile(f'Order status check failed, attempt {attempt + 1}/{max_attempts}') + + logging.CyberCPLogFileWriter.writeToFile( + f'Order status check failed, attempt {attempt + 1}/{max_attempts}') time.sleep(delay) - + logging.CyberCPLogFileWriter.writeToFile('Order processing timed out') return False except Exception as e: @@ -892,14 +919,15 @@ class CustomACME: def issue_certificate(self, domains, use_dns=False): """Main method to issue certificate""" try: - logging.CyberCPLogFileWriter.writeToFile(f'Starting certificate issuance for domains: {domains}, use_dns: {use_dns}') - + logging.CyberCPLogFileWriter.writeToFile( + f'Starting certificate issuance for domains: {domains}, use_dns: {use_dns}') + # Try to load existing account key first if self._load_account_key(): logging.CyberCPLogFileWriter.writeToFile('Using existing account key') else: logging.CyberCPLogFileWriter.writeToFile('No existing account key found, will create new one') - + # Filter domains to only include those with valid DNS records valid_domains = [] for domain in domains: @@ -907,27 +935,27 @@ class CustomACME: valid_domains.append(domain) else: logging.CyberCPLogFileWriter.writeToFile(f'Skipping domain {domain} due to missing DNS records') - + if not valid_domains: logging.CyberCPLogFileWriter.writeToFile('No valid domains found with DNS records') return False - + # Initialize ACME logging.CyberCPLogFileWriter.writeToFile('Step 1: Generating account key') if not self._generate_account_key(): logging.CyberCPLogFileWriter.writeToFile('Failed to generate account key') return False - + logging.CyberCPLogFileWriter.writeToFile('Step 2: Getting ACME directory') if not self._get_directory(): logging.CyberCPLogFileWriter.writeToFile('Failed to get ACME directory') return False - + logging.CyberCPLogFileWriter.writeToFile('Step 3: Getting nonce') if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce') return False - + logging.CyberCPLogFileWriter.writeToFile('Step 4: Creating account') if not self._create_account(): logging.CyberCPLogFileWriter.writeToFile('Failed to create account') @@ -944,13 +972,13 @@ class CustomACME: return False else: return False - + # Create order with only valid domains logging.CyberCPLogFileWriter.writeToFile('Step 5: Creating order') if not self._create_order(valid_domains): logging.CyberCPLogFileWriter.writeToFile('Failed to create order') return False - + # Handle challenges logging.CyberCPLogFileWriter.writeToFile('Step 6: Handling challenges') for auth_url in self.authorizations: @@ -958,7 +986,7 @@ class CustomACME: if not self._get_nonce(): logging.CyberCPLogFileWriter.writeToFile('Failed to get nonce for authorization') return False - + # Get authorization details with POST-as-GET request # ACME protocol requires POST with empty payload for fetching resources logging.CyberCPLogFileWriter.writeToFile(f'Fetching authorization details for: {auth_url}') @@ -966,22 +994,22 @@ class CustomACME: if not jws: logging.CyberCPLogFileWriter.writeToFile('Failed to create JWS for authorization request') return False - + headers = { 'Content-Type': 'application/jose+json' } response = requests.post(auth_url, data=jws, headers=headers) logging.CyberCPLogFileWriter.writeToFile(f'Authorization response status: {response.status_code}') logging.CyberCPLogFileWriter.writeToFile(f'Authorization response: {response.text}') - + if response.status_code != 200: logging.CyberCPLogFileWriter.writeToFile('Failed to get authorization') return False - + challenges = response.json()['challenges'] for challenge in challenges: logging.CyberCPLogFileWriter.writeToFile(f'Processing challenge: {json.dumps(challenge)}') - + # Only handle the challenge type we're using if use_dns and challenge['type'] == 'dns-01': if not self._handle_dns_challenge(challenge): @@ -1005,7 +1033,7 @@ class CustomACME: return False else: logging.CyberCPLogFileWriter.writeToFile(f'Skipping {challenge["type"]} challenge') - + # Generate CSR logging.CyberCPLogFileWriter.writeToFile('Step 7: Generating CSR') key = rsa.generate_private_key( @@ -1013,7 +1041,7 @@ class CustomACME: key_size=2048, backend=default_backend() ) - + # Get the domain from the order response # Use POST-as-GET to get order details jws = self._create_jws(None, self.order_url) @@ -1023,7 +1051,7 @@ class CustomACME: order_response = requests.post(self.order_url, data=jws, headers=headers).json() order_domains = [identifier['value'] for identifier in order_response['identifiers']] logging.CyberCPLogFileWriter.writeToFile(f'Order domains: {order_domains}') - + # Create CSR with exactly the domains from the order csr = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ @@ -1035,39 +1063,39 @@ class CustomACME: ]), critical=False ).sign(key, hashes.SHA256(), default_backend()) - + # Finalize order logging.CyberCPLogFileWriter.writeToFile('Step 8: Finalizing order') if not self._finalize_order(csr.public_bytes(serialization.Encoding.DER)): logging.CyberCPLogFileWriter.writeToFile('Failed to finalize order') return False - + # Wait for order processing logging.CyberCPLogFileWriter.writeToFile('Step 9: Waiting for order processing') if not self._wait_for_order_processing(): logging.CyberCPLogFileWriter.writeToFile('Failed to process order') return False - + # Download certificate logging.CyberCPLogFileWriter.writeToFile('Step 10: Downloading certificate') certificate = self._download_certificate() if not certificate: logging.CyberCPLogFileWriter.writeToFile('Failed to download certificate') return False - + # Save certificate logging.CyberCPLogFileWriter.writeToFile('Step 11: Saving certificate') if not os.path.exists(self.cert_path): logging.CyberCPLogFileWriter.writeToFile(f'Creating certificate directory: {self.cert_path}') os.makedirs(self.cert_path) - + cert_file = os.path.join(self.cert_path, 'fullchain.pem') key_file = os.path.join(self.cert_path, 'privkey.pem') - + logging.CyberCPLogFileWriter.writeToFile(f'Saving certificate to: {cert_file}') with open(cert_file, 'wb') as f: f.write(certificate) - + logging.CyberCPLogFileWriter.writeToFile(f'Saving private key to: {key_file}') with open(key_file, 'wb') as f: f.write(key.private_bytes( @@ -1075,7 +1103,7 @@ class CustomACME: format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() )) - + logging.CyberCPLogFileWriter.writeToFile('Successfully completed certificate issuance') return True except Exception as e: diff --git a/plogical/sslUtilities.py b/plogical/sslUtilities.py index 5274b2da4..34ce4ece8 100644 --- a/plogical/sslUtilities.py +++ b/plogical/sslUtilities.py @@ -6,6 +6,7 @@ import shlex import subprocess import socket from plogical.processUtilities import ProcessUtilities + try: from websiteFunctions.models import ChildDomains, Websites except: @@ -14,18 +15,17 @@ from plogical.acl import ACLManager class sslUtilities: - Server_root = "/usr/local/lsws" redisConf = '/usr/local/lsws/conf/dvhost_redis.conf' - + @staticmethod def parseACMEError(error_output): """Parse ACME error output to extract meaningful error messages""" if not error_output: return "Unknown error occurred" - + error_output = str(error_output) - + # Common ACME/Let's Encrypt errors error_patterns = { r"rateLimited": "Rate limit exceeded. Too many certificates issued for this domain. Please wait before retrying.", @@ -61,7 +61,7 @@ class sslUtilities: r"blacklisted": "Domain is blacklisted by the certificate authority.", r"PolicyForbids": "Certificate authority policy forbids issuance for this domain." } - + # Check each pattern import re for pattern, message in error_patterns.items(): @@ -73,7 +73,7 @@ class sslUtilities: message += f" Detail: {line.split('Detail:')[1].strip()}" break return message - + # Try to extract specific error details from acme.sh output if "[" in error_output and "]" in error_output: # Extract content between brackets which often contains the error @@ -84,20 +84,20 @@ class sslUtilities: potential_error = bracket_content[-1] if len(potential_error) > 10: # Make sure it's meaningful return f"SSL issuance failed: {potential_error}" - + # Look for lines starting with "Error:" or containing "error:" lines = error_output.split('\n') for line in lines: if line.strip().startswith('Error:') or 'error:' in line.lower(): return line.strip() - + # If we can't parse a specific error, return a portion of the output if len(error_output) > 200: # Get the last 200 characters which likely contain the error return f"SSL issuance failed: ...{error_output[-200:]}" - + return f"SSL issuance failed: {error_output}" - + @staticmethod def checkDNSRecords(domain): """Check if domain has valid DNS records using external DNS query""" @@ -108,23 +108,25 @@ class sslUtilities: result = subprocess.run(command, shell=True, capture_output=True, text=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) - + result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + # If there's any output, the domain has A records if result.stdout.strip(): return True - + # Also check AAAA records command = f"dig +short {domain} AAAA @8.8.8.8" try: result = subprocess.run(command, shell=True, capture_output=True, text=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) - + result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + if result.stdout.strip(): return True - + return False except: # Fallback to socket method if dig fails @@ -164,7 +166,6 @@ class sslUtilities: except BaseException as msg: return 0, str(msg) - @staticmethod def CheckIfSSLNeedsToBeIssued(virtualHostName): #### if website already have an SSL, better not issue again - need to check for wild-card @@ -177,7 +178,6 @@ class sslUtilities: if os.path.exists(ProcessUtilities.debugPath): logging.CyberCPLogFileWriter.writeToFile(f'SSL provider for {virtualHostName} is {SSLProvider}.') - #### totally seprate check to see if both non-www and www are covered if SSLProvider == "(STAGING) Let's Encrypt": @@ -189,7 +189,8 @@ class sslUtilities: if len(domains) > 1: ### need further checks here to see if ssl is valid for less then 15 days etc logging.CyberCPLogFileWriter.writeToFile( - '[CheckIfSSLNeedsToBeIssued] SSL exists for %s and both versions are covered, just need to ensure if SSL is valid for less then 15 days.' % (virtualHostName), 0) + '[CheckIfSSLNeedsToBeIssued] SSL exists for %s and both versions are covered, just need to ensure if SSL is valid for less then 15 days.' % ( + virtualHostName), 0) pass else: return sslUtilities.ISSUE_SSL @@ -202,7 +203,7 @@ class sslUtilities: now = datetime.now() diff = finalDate - now - if int(diff.days) >= 15 and SSLProvider!='Denial': + if int(diff.days) >= 15 and SSLProvider != 'Denial': logging.CyberCPLogFileWriter.writeToFile( '[CheckIfSSLNeedsToBeIssued] SSL exists for %s and is not ready to fetch new SSL., skipping..' % ( virtualHostName), 0) @@ -260,8 +261,7 @@ class sslUtilities: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [IO Error with main config file [checkSSLListener]]") return str(msg) return 0 - - + @staticmethod def checkSSLIPv6Listener(): try: @@ -271,7 +271,8 @@ class sslUtilities: return 1 except BaseException as msg: - logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [IO Error with main config file [checkSSLIPv6Listener]]") + logging.CyberCPLogFileWriter.writeToFile( + str(msg) + " [IO Error with main config file [checkSSLIPv6Listener]]") return str(msg) return 0 @@ -290,14 +291,14 @@ class sslUtilities: @staticmethod def PatchVhostConf(virtualHostName): """Patch the virtual host configuration to add ACME challenge support - + This function adds the necessary configuration to handle ACME challenges for both OpenLiteSpeed (OLS) and Apache configurations. It also checks for potential configuration conflicts before making changes. - + Args: virtualHostName (str): The domain name to configure - + Returns: tuple: (status, message) where status is 1 for success, 0 for failure """ @@ -305,12 +306,12 @@ class sslUtilities: # Construct paths confPath = os.path.join(sslUtilities.Server_root, "conf", "vhosts", virtualHostName) completePathToConfigFile = os.path.join(confPath, "vhost.conf") - + # Check if file exists if not os.path.exists(completePathToConfigFile): logging.CyberCPLogFileWriter.writeToFile(f'Configuration file not found: {completePathToConfigFile}') return 0, f'Configuration file not found: {completePathToConfigFile}' - + # Read current configuration try: with open(completePathToConfigFile, 'r') as f: @@ -318,41 +319,42 @@ class sslUtilities: except IOError as e: logging.CyberCPLogFileWriter.writeToFile(f'Error reading configuration file: {str(e)}') return 0, f'Error reading configuration file: {str(e)}' - + # Check for potential conflicts conflicts = [] - + # Check if ACME challenge is already configured if DataVhost.find('/.well-known/acme-challenge') != -1: logging.CyberCPLogFileWriter.writeToFile(f'ACME challenge already configured for {virtualHostName}') return 1, 'ACME challenge already configured' - + # Check for conflicting rewrite rules if DataVhost.find('rewrite') != -1 and DataVhost.find('enable 1') != -1: conflicts.append('Active rewrite rules found that might interfere with ACME challenges') - + # Check for conflicting location blocks if DataVhost.find('location /.well-known') != -1: conflicts.append('Existing location block for /.well-known found') - + # Check for conflicting aliases if DataVhost.find('Alias /.well-known') != -1: conflicts.append('Existing alias for /.well-known found') - + # Check for conflicting context blocks if DataVhost.find('context /.well-known') != -1: conflicts.append('Existing context block for /.well-known found') - + # Check for conflicting access controls if DataVhost.find('deny from all') != -1 and DataVhost.find('location') != -1: conflicts.append('Global deny rules found that might block ACME challenges') - + # If conflicts found, log them and return if conflicts: conflict_message = 'Configuration conflicts found: ' + '; '.join(conflicts) - logging.CyberCPLogFileWriter.writeToFile(f'Configuration conflicts for {virtualHostName}: {conflict_message}') + logging.CyberCPLogFileWriter.writeToFile( + f'Configuration conflicts for {virtualHostName}: {conflict_message}') return 0, conflict_message - + # Create challenge directory if it doesn't exist challenge_dir = '/usr/local/lsws/Example/html/.well-known/acme-challenge' try: @@ -362,7 +364,7 @@ class sslUtilities: except OSError as e: logging.CyberCPLogFileWriter.writeToFile(f'Error creating challenge directory: {str(e)}') return 0, f'Error creating challenge directory: {str(e)}' - + # Handle configuration based on server type if ProcessUtilities.decideServer() == ProcessUtilities.OLS: # OpenLiteSpeed configuration @@ -390,35 +392,37 @@ context /.well-known/acme-challenge { # Read current configuration with open(completePathToConfigFile, 'r') as f: lines = f.readlines() - + # Write new configuration with open(completePathToConfigFile, 'w') as f: check = 0 for line in lines: f.write(line) if line.find('DocumentRoot /home/') > -1 and check == 0: - f.write(' Alias /.well-known/acme-challenge /usr/local/lsws/Example/html/.well-known/acme-challenge\n') + f.write( + ' Alias /.well-known/acme-challenge /usr/local/lsws/Example/html/.well-known/acme-challenge\n') check = 1 except IOError as e: logging.CyberCPLogFileWriter.writeToFile(f'Error writing Apache configuration: {str(e)}') return 0, f'Error writing Apache configuration: {str(e)}' - + # Restart LiteSpeed try: from plogical import installUtilities installUtilities.installUtilities.reStartLiteSpeed() - logging.CyberCPLogFileWriter.writeToFile(f'Successfully configured ACME challenge for {virtualHostName}') + logging.CyberCPLogFileWriter.writeToFile( + f'Successfully configured ACME challenge for {virtualHostName}') return 1, 'Successfully configured ACME challenge' except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Error restarting LiteSpeed: {str(e)}') return 0, f'Error restarting LiteSpeed: {str(e)}' - + except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f'Unexpected error in PatchVhostConf: {str(e)}') return 0, f'Unexpected error: {str(e)}' @staticmethod - def installSSLForDomain(virtualHostName, adminEmail='example@example.org'): + def installSSLForDomain(virtualHostName, adminEmail='domain@cyberpanel.net'): try: website = Websites.objects.get(domain=virtualHostName) @@ -461,7 +465,7 @@ context /.well-known/acme-challenge { writeDataToFile.writelines(certFile) writeDataToFile.writelines(certChain) writeDataToFile.writelines(sslProtocol) - writeDataToFile.writelines(enableECDHE) + writeDataToFile.writelines(enableECDHE) writeDataToFile.writelines(renegProtection) writeDataToFile.writelines(sslSessionCache) writeDataToFile.writelines(enableSpdy) @@ -500,7 +504,7 @@ context /.well-known/acme-challenge { writeDataToFile.writelines(certFile) writeDataToFile.writelines(certChain) writeDataToFile.writelines(sslProtocol) - writeDataToFile.writelines(enableECDHE) + writeDataToFile.writelines(enableECDHE) writeDataToFile.writelines(renegProtection) writeDataToFile.writelines(sslSessionCache) writeDataToFile.writelines(enableSpdy) @@ -674,6 +678,14 @@ context /.well-known/acme-challenge { import json import socket + # Replace example.org emails with domain-specific email + if adminEmail and ('example.org' in adminEmail or 'example.com' in adminEmail): + import re + # Remove special characters and create domain-based email + clean_domain = re.sub(r'[^a-zA-Z0-9]', '', virtualHostName) + adminEmail = f'{clean_domain}@cyberpanel.net' + logging.CyberCPLogFileWriter.writeToFile(f'Replacing invalid email with {adminEmail}') + Status = 1 if sslUtilities.CheckIfSSLNeedsToBeIssued(virtualHostName) == sslUtilities.ISSUE_SSL: @@ -696,23 +708,27 @@ context /.well-known/acme-challenge { try: # Start with just the main domain domains = [virtualHostName] - + # Check if www subdomain has DNS records before adding it (skip for hostnames) if not isHostname and sslUtilities.checkDNSRecords(f'www.{virtualHostName}'): domains.append(f'www.{virtualHostName}') - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has DNS records, including in SSL request") + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has DNS records, including in SSL request") elif not isHostname: - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has no DNS records, excluding from SSL request") - + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has no DNS records, excluding from SSL request") + if aliasDomain: domains.append(aliasDomain) # Check if www.aliasDomain has DNS records if sslUtilities.checkDNSRecords(f'www.{aliasDomain}'): domains.append(f'www.{aliasDomain}') - logging.CyberCPLogFileWriter.writeToFile(f"www.{aliasDomain} has DNS records, including in SSL request") + logging.CyberCPLogFileWriter.writeToFile( + f"www.{aliasDomain} has DNS records, including in SSL request") else: - logging.CyberCPLogFileWriter.writeToFile(f"www.{aliasDomain} has no DNS records, excluding from SSL request") - + logging.CyberCPLogFileWriter.writeToFile( + f"www.{aliasDomain} has no DNS records, excluding from SSL request") + # Check if Cloudflare is used use_dns = False try: @@ -721,7 +737,7 @@ context /.well-known/acme-challenge { use_dns = True except: pass - + acme = CustomACME(virtualHostName, adminEmail, staging=False, provider='letsencrypt') if acme.issue_certificate(domains, use_dns=use_dns): logging.CyberCPLogFileWriter.writeToFile( @@ -734,7 +750,7 @@ context /.well-known/acme-challenge { error_details = str(e.__dict__) else: error_details = error_msg - + logging.CyberCPLogFileWriter.writeToFile( f"Let's Encrypt failed for {virtualHostName}: {error_msg}" ) @@ -746,23 +762,27 @@ context /.well-known/acme-challenge { try: # Start with just the main domain domains = [virtualHostName] - + # Check if www subdomain has DNS records before adding it (skip for hostnames) if not isHostname and sslUtilities.checkDNSRecords(f'www.{virtualHostName}'): domains.append(f'www.{virtualHostName}') - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has DNS records, including in SSL request") + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has DNS records, including in SSL request") elif not isHostname: - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has no DNS records, excluding from SSL request") - + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has no DNS records, excluding from SSL request") + if aliasDomain: domains.append(aliasDomain) # Check if www.aliasDomain has DNS records if sslUtilities.checkDNSRecords(f'www.{aliasDomain}'): domains.append(f'www.{aliasDomain}') - logging.CyberCPLogFileWriter.writeToFile(f"www.{aliasDomain} has DNS records, including in SSL request") + logging.CyberCPLogFileWriter.writeToFile( + f"www.{aliasDomain} has DNS records, including in SSL request") else: - logging.CyberCPLogFileWriter.writeToFile(f"www.{aliasDomain} has no DNS records, excluding from SSL request") - + logging.CyberCPLogFileWriter.writeToFile( + f"www.{aliasDomain} has no DNS records, excluding from SSL request") + acme = CustomACME(virtualHostName, adminEmail, staging=False, provider='zerossl') if acme.issue_certificate(domains, use_dns=use_dns): logging.CyberCPLogFileWriter.writeToFile( @@ -790,37 +810,41 @@ context /.well-known/acme-challenge { try: # Build domain list for acme.sh domain_list = " -d " + virtualHostName - + # Check if www subdomain has DNS records (skip for hostnames) if not isHostname and sslUtilities.checkDNSRecords(f'www.{virtualHostName}'): domain_list += " -d www." + virtualHostName - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has DNS records, including in acme.sh SSL request") + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has DNS records, including in acme.sh SSL request") elif not isHostname: - logging.CyberCPLogFileWriter.writeToFile(f"www.{virtualHostName} has no DNS records, excluding from acme.sh SSL request") - + logging.CyberCPLogFileWriter.writeToFile( + f"www.{virtualHostName} has no DNS records, excluding from acme.sh SSL request") + command = acmePath + " --issue" + domain_list \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --staging' \ + ' --webroot-path /usr/local/lsws/Example/html' - + try: result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, shell=True) if result.returncode == 0: command = acmePath + " --issue" + domain_list \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' \ + ' --webroot-path /usr/local/lsws/Example/html' - + try: result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) - + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, shell=True) + if result.returncode == 0: logging.CyberCPLogFileWriter.writeToFile( "Successfully obtained SSL for: " + virtualHostName + " and: www." + virtualHostName, 0) @@ -840,18 +864,18 @@ context /.well-known/acme-challenge { try: # Build domain list for acme.sh with alias domains domain_list = " -d " + virtualHostName - + # Check if www subdomain has DNS records if sslUtilities.checkDNSRecords(f'www.{virtualHostName}'): domain_list += " -d www." + virtualHostName - + # Add alias domain domain_list += " -d " + aliasDomain - + # Check if www.aliasDomain has DNS records if sslUtilities.checkDNSRecords(f'www.{aliasDomain}'): domain_list += " -d www." + aliasDomain - + command = acmePath + " --issue" + domain_list \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + ' -w /usr/local/lsws/Example/html -k ec-256 --force --server letsencrypt' @@ -860,8 +884,9 @@ context /.well-known/acme-challenge { result = subprocess.run(command, capture_output=True, universal_newlines=True, shell=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) - + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, shell=True) + if result.returncode == 0: return 1 return 0 @@ -893,35 +918,37 @@ def issueSSLForDomain(domain, adminEmail, sslpath, aliasDomain=None, isHostname= logging.CyberCPLogFileWriter.writeToFile(f"Certificate for {domain} expires in {diff.days} days") except Exception as e: logging.CyberCPLogFileWriter.writeToFile(f"Could not check certificate expiry: {str(e)}") - + logging.CyberCPLogFileWriter.writeToFile(f"Certificate exists for {domain}, attempting renewal...") - + # Try to renew using acme.sh acmePath = '/root/.acme.sh/acme.sh' if os.path.exists(acmePath): # First set the webroot path for the domain command = f'{acmePath} --update-account --accountemail {adminEmail}' subprocess.call(command, shell=True) - + # Build domain list for renewal renewal_domains = f'-d {domain}' if not isHostname and sslUtilities.checkDNSRecords(f'www.{domain}'): renewal_domains += f' -d www.{domain}' - + # For expired certificates, use --issue --force instead of --renew if is_expired: - logging.CyberCPLogFileWriter.writeToFile(f"Certificate is expired, using --issue --force for {domain}") + logging.CyberCPLogFileWriter.writeToFile( + f"Certificate is expired, using --issue --force for {domain}") command = f'{acmePath} --issue {renewal_domains} --webroot /usr/local/lsws/Example/html --force' else: # Try to renew with explicit webroot command = f'{acmePath} --renew {renewal_domains} --webroot /usr/local/lsws/Example/html --force' - + try: result = subprocess.run(command, capture_output=True, text=True, shell=True) except TypeError: # Fallback for Python < 3.7 - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) - + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True, shell=True) + if result.returncode == 0: logging.CyberCPLogFileWriter.writeToFile(f"Successfully renewed SSL for {domain}") if sslUtilities.installSSLForDomain(domain, adminEmail) == 1: @@ -932,7 +959,7 @@ def issueSSLForDomain(domain, adminEmail, sslpath, aliasDomain=None, isHostname= error_details = sslUtilities.parseACMEError(error_output) logging.CyberCPLogFileWriter.writeToFile(f"Renewal failed for {domain}. Error: {error_details}") logging.CyberCPLogFileWriter.writeToFile(f"Full error output: {error_output}") - + if sslUtilities.obtainSSLForADomain(domain, adminEmail, sslpath, aliasDomain, isHostname) == 1: if sslUtilities.installSSLForDomain(domain, adminEmail) == 1: return [1, "None"] @@ -948,13 +975,16 @@ def issueSSLForDomain(domain, adminEmail, sslpath, aliasDomain=None, isHostname= if os.path.exists(pathToStoreSSLFullChain): import OpenSSL - x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, open(pathToStoreSSLFullChain, 'r').read()) + x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, + open(pathToStoreSSLFullChain, 'r').read()) SSLProvider = x509.get_issuer().get_components()[1][1].decode('utf-8') if SSLProvider != 'Denial': if sslUtilities.installSSLForDomain(domain) == 1: - logging.CyberCPLogFileWriter.writeToFile("We are not able to get new SSL for " + domain + ". But there is an existing SSL, it might only be for the main domain (excluding www).") - return [1, "We are not able to get new SSL for " + domain + ". But there is an existing SSL, it might only be for the main domain (excluding www)." + " [issueSSLForDomain]"] + logging.CyberCPLogFileWriter.writeToFile( + "We are not able to get new SSL for " + domain + ". But there is an existing SSL, it might only be for the main domain (excluding www).") + return [1, + "We are not able to get new SSL for " + domain + ". But there is an existing SSL, it might only be for the main domain (excluding www)." + " [issueSSLForDomain]"] command = 'openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=' + domain + '" -keyout ' + pathToStoreSSLPrivKey + ' -out ' + pathToStoreSSLFullChain cmd = shlex.split(command) diff --git a/plogical/sslv2.py b/plogical/sslv2.py index a55434698..285795df8 100644 --- a/plogical/sslv2.py +++ b/plogical/sslv2.py @@ -10,6 +10,7 @@ import socket from plogical.acl import ACLManager from plogical.processUtilities import ProcessUtilities + try: from websiteFunctions.models import ChildDomains, Websites except: @@ -17,7 +18,6 @@ except: class sslUtilities: - Server_root = "/usr/local/lsws" redisConf = '/usr/local/lsws/conf/dvhost_redis.conf' @@ -56,8 +56,7 @@ class sslUtilities: logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [IO Error with main config file [checkSSLListener]]") return str(msg) return 0 - - + @staticmethod def checkSSLIPv6Listener(): try: @@ -67,7 +66,8 @@ class sslUtilities: return 1 except BaseException as msg: - logging.CyberCPLogFileWriter.writeToFile(str(msg) + " [IO Error with main config file [checkSSLIPv6Listener]]") + logging.CyberCPLogFileWriter.writeToFile( + str(msg) + " [IO Error with main config file [checkSSLIPv6Listener]]") return str(msg) return 0 @@ -84,7 +84,7 @@ class sslUtilities: return [0, "347 " + str(msg) + " [issueSSLForDomain]"] @staticmethod - def installSSLForDomain(virtualHostName, adminEmail='example@example.org'): + def installSSLForDomain(virtualHostName, adminEmail='domain@cyberpanel.net'): try: website = Websites.objects.get(domain=virtualHostName) @@ -127,7 +127,7 @@ class sslUtilities: writeDataToFile.writelines(certFile) writeDataToFile.writelines(certChain) writeDataToFile.writelines(sslProtocol) - writeDataToFile.writelines(enableECDHE) + writeDataToFile.writelines(enableECDHE) writeDataToFile.writelines(renegProtection) writeDataToFile.writelines(sslSessionCache) writeDataToFile.writelines(enableSpdy) @@ -166,7 +166,7 @@ class sslUtilities: writeDataToFile.writelines(certFile) writeDataToFile.writelines(certChain) writeDataToFile.writelines(sslProtocol) - writeDataToFile.writelines(enableECDHE) + writeDataToFile.writelines(enableECDHE) writeDataToFile.writelines(renegProtection) writeDataToFile.writelines(sslSessionCache) writeDataToFile.writelines(enableSpdy) @@ -332,7 +332,6 @@ class sslUtilities: ProcessUtilities.executioner(command) return 1 - @staticmethod def FindIfDomainInCloudflare(virtualHostName): try: @@ -403,6 +402,14 @@ class sslUtilities: @staticmethod def obtainSSLForADomain(virtualHostName, adminEmail, sslpath, aliasDomain=None): + # Replace example.org emails with domain-specific email + if adminEmail and ('example.org' in adminEmail or 'example.com' in adminEmail): + import re + # Remove special characters and create domain-based email + clean_domain = re.sub(r'[^a-zA-Z0-9]', '', virtualHostName) + adminEmail = f'{clean_domain}@cyberpanel.net' + logging.CyberCPLogFileWriter.writeToFile(f'Replacing invalid email with {adminEmail}') + sender_email = 'root@%s' % (socket.gethostname()) CF_Check = 0 @@ -419,7 +426,6 @@ class sslUtilities: if SSLProvider != 'Denial': return 1, 'This domain already have a valid SSL.' - CF_Check, message = sslUtilities.FindIfDomainInCloudflare(virtualHostName) DNS_TO_USE = '' @@ -456,8 +462,8 @@ class sslUtilities: command = acmePath + f" --issue -d {virtualHostName} -d *.{virtualHostName}" \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + f' --dns {DNS_TO_USE} -k ec-256 --force --server letsencrypt --dnssleep 20' - #ResultText = open(logging.CyberCPLogFileWriter.fileName, 'r').read() - #CurrentMessage = "Trying to obtain SSL for: " + virtualHostName + " and: www." + virtualHostName + # ResultText = open(logging.CyberCPLogFileWriter.fileName, 'r').read() + # CurrentMessage = "Trying to obtain SSL for: " + virtualHostName + " and: www." + virtualHostName # logging.CyberCPLogFileWriter.writeToFile(CurrentMessage, 0) logging.CyberCPLogFileWriter.writeToFile(command, 0) @@ -480,7 +486,7 @@ class sslUtilities: + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + f' --dns {DNS_TO_USE} -k ec-256 --force --server letsencrypt --dnssleep 20' - #ResultText = open(logging.CyberCPLogFileWriter.fileName, 'r').read() + # ResultText = open(logging.CyberCPLogFileWriter.fileName, 'r').read() CurrentMessage = '%s\nTrying to obtain SSL for: %s' % (finalText, virtualHostName) finalText = '%s\nTrying to obtain SSL for: %s' % (finalText, virtualHostName) @@ -494,8 +500,10 @@ class sslUtilities: 'SSL Notification for %s.' % (virtualHostName)) except subprocess.CalledProcessError: - logging.CyberCPLogFileWriter.writeToFile('Failed to obtain SSL, issuing self-signed SSL for: ' + virtualHostName, 0) - logging.CyberCPLogFileWriter.SendEmail(sender_email, adminEmail, 'Failed to obtain SSL, issuing self-signed SSL for: ' + virtualHostName, + logging.CyberCPLogFileWriter.writeToFile( + 'Failed to obtain SSL, issuing self-signed SSL for: ' + virtualHostName, 0) + logging.CyberCPLogFileWriter.SendEmail(sender_email, adminEmail, + 'Failed to obtain SSL, issuing self-signed SSL for: ' + virtualHostName, 'SSL Notification for %s.' % (virtualHostName)) return 0, output else: @@ -510,7 +518,7 @@ class sslUtilities: "Trying to obtain SSL for: " + virtualHostName + ", www." + virtualHostName + ", " + aliasDomain + " and www." + aliasDomain + ",") command = acmePath + " --issue -d " + virtualHostName + " -d www." + virtualHostName \ - + ' -d ' + aliasDomain + ' -d www.' + aliasDomain\ + + ' -d ' + aliasDomain + ' -d www.' + aliasDomain \ + ' --cert-file ' + existingCertPath + '/cert.pem' + ' --key-file ' + existingCertPath + '/privkey.pem' \ + ' --fullchain-file ' + existingCertPath + '/fullchain.pem' + f' --dns {DNS_TO_USE} -k ec-256 --force --server letsencrypt --dnssleep 20' diff --git a/websiteFunctions/views.py b/websiteFunctions/views.py index ef0a7deb4..9647a2fea 100644 --- a/websiteFunctions/views.py +++ b/websiteFunctions/views.py @@ -9,7 +9,6 @@ import json import plogical.CyberCPLogFileWriter as logging from plogical.acl import ACLManager - from plogical.httpProc import httpProc from websiteFunctions.models import wpplugins from websiteFunctions.website import WebsiteManager @@ -26,6 +25,7 @@ from plogical.processUtilities import ProcessUtilities import os import re + def loadWebsitesHome(request): val = request.session['userID'] admin = Administrator.objects.get(pk=val) @@ -33,6 +33,7 @@ def loadWebsitesHome(request): {"type": admin.type}) return proc.render() + def createWebsite(request): try: userID = request.session['userID'] @@ -40,14 +41,17 @@ def createWebsite(request): return wm.createWebsite(request, userID) except KeyError: return redirect(loadLoginPage) + + def WPCreate(request): try: userID = request.session['userID'] wm = WebsiteManager() - return wm.WPCreate(request, userID,) + return wm.WPCreate(request, userID, ) except KeyError: return redirect(loadLoginPage) + def ListWPSites(request): try: userID = request.session['userID'] @@ -57,6 +61,7 @@ def ListWPSites(request): except KeyError: return redirect(loadLoginPage) + def WPHome(request): try: userID = request.session['userID'] @@ -67,6 +72,8 @@ def WPHome(request): return wm.WPHome(request, userID, WPid, DeleteID) except KeyError: return redirect(loadLoginPage) + + def RestoreHome(request): try: userID = request.session['userID'] @@ -88,6 +95,7 @@ def RemoteBackupConfig(request): except KeyError: return redirect(loadLoginPage) + def BackupfileConfig(request): try: userID = request.session['userID'] @@ -99,6 +107,7 @@ def BackupfileConfig(request): except KeyError: return redirect(loadLoginPage) + def AddRemoteBackupsite(request): try: userID = request.session['userID'] @@ -106,18 +115,20 @@ def AddRemoteBackupsite(request): ID = request.GET.get('ID') DeleteSiteID = request.GET.get('DeleteID') wm = WebsiteManager() - return wm.AddRemoteBackupsite(request, userID, ID,DeleteSiteID ) + return wm.AddRemoteBackupsite(request, userID, ID, DeleteSiteID) except KeyError: return redirect(loadLoginPage) + def WordpressPricing(request): try: userID = request.session['userID'] wm = WebsiteManager() - return wm.WordpressPricing(request, userID,) + return wm.WordpressPricing(request, userID, ) except KeyError: return redirect(loadLoginPage) + def RestoreBackups(request): try: userID = request.session['userID'] @@ -128,6 +139,7 @@ def RestoreBackups(request): except KeyError: return redirect(loadLoginPage) + def AutoLogin(request): try: userID = request.session['userID'] @@ -136,6 +148,8 @@ def AutoLogin(request): except KeyError: return redirect(loadLoginPage) + + def ConfigurePlugins(request): try: userID = request.session['userID'] @@ -147,13 +161,14 @@ def ConfigurePlugins(request): jobobj.delete() Deleted = 1 except BaseException as msg: - logging.CyberCPLogFileWriter.writeToFile("DeleteFileID ....... %s....msg.....%s" % (DeleteFileID,msg)) + logging.CyberCPLogFileWriter.writeToFile("DeleteFileID ....... %s....msg.....%s" % (DeleteFileID, msg)) Deleted = 0 wm = WebsiteManager() return wm.ConfigurePlugins(request, userID) except KeyError: return redirect(loadLoginPage) + def Addnewplugin(request): try: userID = request.session['userID'] @@ -163,15 +178,13 @@ def Addnewplugin(request): return redirect(loadLoginPage) - - def SearchOnkeyupPlugin(request): try: userID = request.session['userID'] result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -193,7 +206,7 @@ def AddNewpluginAjax(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -226,7 +239,7 @@ def deletesPlgin(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -248,7 +261,7 @@ def Addplugineidt(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -270,7 +283,7 @@ def submitWorpressCreation(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -292,7 +305,7 @@ def FetchWPdata(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -314,7 +327,7 @@ def GetCurrentPlugins(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -337,7 +350,7 @@ def fetchstaging(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -352,13 +365,14 @@ def fetchstaging(request): except KeyError: return redirect(loadLoginPage) + def fetchDatabase(request): try: userID = request.session['userID'] result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -373,6 +387,7 @@ def fetchDatabase(request): except KeyError: return redirect(loadLoginPage) + def SaveUpdateConfig(request): try: userID = request.session['userID'] @@ -394,27 +409,29 @@ def SaveUpdateConfig(request): except KeyError: return redirect(loadLoginPage) + def DeploytoProduction(request): try: userID = request.session['userID'] - #result = pluginManager.preWebsiteCreation(request) + # result = pluginManager.preWebsiteCreation(request) - #if result != 200: + # if result != 200: # return result wm = WebsiteManager() return wm.DeploytoProduction(userID, json.loads(request.body)) - #result = pluginManager.postWebsiteCreation(request, coreResult) - #if result != 200: + # result = pluginManager.postWebsiteCreation(request, coreResult) + # if result != 200: # return result - #return coreResult + # return coreResult except KeyError: return redirect(loadLoginPage) + def WPCreateBackup(request): try: userID = request.session['userID'] @@ -437,7 +454,6 @@ def WPCreateBackup(request): return redirect(loadLoginPage) - def RestoreWPbackupNow(request): try: userID = request.session['userID'] @@ -480,6 +496,7 @@ def SaveBackupConfig(request): except KeyError: return redirect(loadLoginPage) + def SaveBackupSchedule(request): try: userID = request.session['userID'] @@ -500,6 +517,7 @@ def SaveBackupSchedule(request): except KeyError: return redirect(loadLoginPage) + def AddWPsiteforRemoteBackup(request): try: userID = request.session['userID'] @@ -607,14 +625,13 @@ def dataintegrity(request): return redirect(loadLoginPage) - def GetCurrentThemes(request): try: userID = request.session['userID'] result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -634,7 +651,7 @@ def UpdateWPSettings(request): try: userID = request.session['userID'] data = json.loads(request.body) - + wm = WebsiteManager() return wm.UpdateWPSettings(userID, data) except KeyError: @@ -647,7 +664,7 @@ def UpdatePlugins(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -669,7 +686,7 @@ def UpdateThemes(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -691,7 +708,7 @@ def DeletePlugins(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -713,7 +730,7 @@ def DeleteThemes(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -735,7 +752,7 @@ def ChangeStatus(request): result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -795,7 +812,6 @@ def CreateStagingNow(request): return redirect(loadLoginPage) - def modifyWebsite(request): try: userID = request.session['userID'] @@ -807,6 +823,7 @@ def modifyWebsite(request): except KeyError: return redirect(loadLoginPage) + def deleteWebsite(request): try: userID = request.session['userID'] @@ -815,6 +832,7 @@ def deleteWebsite(request): except KeyError: return redirect(loadLoginPage) + def CreateNewDomain(request): try: userID = request.session['userID'] @@ -823,6 +841,7 @@ def CreateNewDomain(request): except KeyError: return redirect(loadLoginPage) + def siteState(request): try: userID = request.session['userID'] @@ -831,6 +850,7 @@ def siteState(request): except KeyError: return redirect(loadLoginPage) + def listWebsites(request): try: userID = request.session['userID'] @@ -839,6 +859,7 @@ def listWebsites(request): except KeyError: return redirect(loadLoginPage) + def listChildDomains(request): try: userID = request.session['userID'] @@ -847,13 +868,14 @@ def listChildDomains(request): except KeyError: return redirect(loadLoginPage) + def submitWebsiteCreation(request): try: userID = request.session['userID'] result = pluginManager.preWebsiteCreation(request) - if result != 200: + if result != 200: return result wm = WebsiteManager() @@ -868,6 +890,7 @@ def submitWebsiteCreation(request): except KeyError: return redirect(loadLoginPage) + def submitDomainCreation(request): try: userID = request.session['userID'] @@ -887,6 +910,7 @@ def submitDomainCreation(request): except KeyError: return redirect(loadLoginPage) + def fetchDomains(request): try: userID = request.session['userID'] @@ -895,6 +919,7 @@ def fetchDomains(request): except KeyError: return redirect(loadLoginPage) + def searchWebsites(request): try: userID = request.session['userID'] @@ -903,6 +928,7 @@ def searchWebsites(request): except KeyError: return redirect(loadLoginPage) + def searchChilds(request): try: userID = request.session['userID'] @@ -911,6 +937,7 @@ def searchChilds(request): except KeyError: return redirect(loadLoginPage) + def getFurtherAccounts(request): try: userID = request.session['userID'] @@ -919,6 +946,7 @@ def getFurtherAccounts(request): except KeyError: return redirect(loadLoginPage) + def fetchWebsitesList(request): try: userID = request.session['userID'] @@ -927,6 +955,7 @@ def fetchWebsitesList(request): except KeyError: return redirect(loadLoginPage) + def fetchChildDomainsMain(request): try: userID = request.session['userID'] @@ -935,6 +964,7 @@ def fetchChildDomainsMain(request): except KeyError: return redirect(loadLoginPage) + def submitWebsiteDeletion(request): try: userID = request.session['userID'] @@ -954,6 +984,7 @@ def submitWebsiteDeletion(request): except KeyError: return redirect(loadLoginPage) + def submitDomainDeletion(request): try: @@ -975,6 +1006,7 @@ def submitDomainDeletion(request): except KeyError: return redirect(loadLoginPage) + def convertDomainToSite(request): try: @@ -985,6 +1017,7 @@ def convertDomainToSite(request): except KeyError: return redirect(loadLoginPage) + def submitWebsiteStatus(request): try: @@ -1006,6 +1039,7 @@ def submitWebsiteStatus(request): except KeyError: return redirect(loadLoginPage) + def submitWebsiteModify(request): try: @@ -1016,6 +1050,7 @@ def submitWebsiteModify(request): except KeyError: return redirect(loadLoginPage) + def saveWebsiteChanges(request): try: @@ -1037,6 +1072,7 @@ def saveWebsiteChanges(request): except KeyError: return redirect(loadLoginPage) + def domain(request, domain): try: @@ -1061,6 +1097,7 @@ def domain(request, domain): except KeyError: return redirect(loadLoginPage) + def launchChild(request, domain, childDomain): try: userID = request.session['userID'] @@ -1069,6 +1106,7 @@ def launchChild(request, domain, childDomain): except KeyError: return redirect(loadLoginPage) + def getDataFromLogFile(request): try: userID = request.session['userID'] @@ -1077,6 +1115,7 @@ def getDataFromLogFile(request): except KeyError: return redirect(loadLoginPage) + def fetchErrorLogs(request): try: userID = request.session['userID'] @@ -1085,6 +1124,7 @@ def fetchErrorLogs(request): except KeyError: return redirect(loadLoginPage) + def getDataFromConfigFile(request): try: userID = request.session['userID'] @@ -1093,6 +1133,7 @@ def getDataFromConfigFile(request): except KeyError: return redirect(loadLoginPage) + def saveConfigsToFile(request): try: @@ -1114,6 +1155,7 @@ def saveConfigsToFile(request): except KeyError: return redirect(loadLoginPage) + def getRewriteRules(request): try: userID = request.session['userID'] @@ -1122,6 +1164,7 @@ def getRewriteRules(request): except KeyError: return redirect(loadLoginPage) + def saveRewriteRules(request): try: @@ -1143,6 +1186,7 @@ def saveRewriteRules(request): except KeyError: return redirect(loadLoginPage) + def saveSSL(request): try: @@ -1164,6 +1208,7 @@ def saveSSL(request): except KeyError: return redirect(loadLoginPage) + def changePHP(request): try: @@ -1185,6 +1230,7 @@ def changePHP(request): except KeyError: return redirect(loadLoginPage) + def listCron(request): try: userID = request.session['userID'] @@ -1193,6 +1239,7 @@ def listCron(request): except KeyError: return redirect(loadLoginPage) + def getWebsiteCron(request): try: userID = request.session['userID'] @@ -1201,6 +1248,7 @@ def getWebsiteCron(request): except KeyError: return redirect(loadLoginPage) + def getCronbyLine(request): try: userID = request.session['userID'] @@ -1209,6 +1257,7 @@ def getCronbyLine(request): except KeyError: return redirect(loadLoginPage) + def saveCronChanges(request): try: userID = request.session['userID'] @@ -1217,6 +1266,7 @@ def saveCronChanges(request): except KeyError: return redirect(loadLoginPage) + def remCronbyLine(request): try: userID = request.session['userID'] @@ -1236,6 +1286,7 @@ def remCronbyLine(request): except KeyError: return redirect(loadLoginPage) + def addNewCron(request): try: userID = request.session['userID'] @@ -1255,6 +1306,7 @@ def addNewCron(request): except KeyError: return redirect(loadLoginPage) + def domainAlias(request, domain): try: userID = request.session['userID'] @@ -1263,6 +1315,7 @@ def domainAlias(request, domain): except KeyError: return redirect(loadLoginPage) + def submitAliasCreation(request): try: userID = request.session['userID'] @@ -1282,6 +1335,7 @@ def submitAliasCreation(request): except KeyError: return redirect(loadLoginPage) + def issueAliasSSL(request): try: userID = request.session['userID'] @@ -1290,6 +1344,7 @@ def issueAliasSSL(request): except KeyError: return redirect(loadLoginPage) + def delateAlias(request): try: userID = request.session['userID'] @@ -1309,6 +1364,7 @@ def delateAlias(request): except KeyError: return redirect(loadLoginPage) + def changeOpenBasedir(request): try: @@ -1330,6 +1386,7 @@ def changeOpenBasedir(request): except KeyError: return redirect(loadLoginPage) + def wordpressInstall(request, domain): try: userID = request.session['userID'] @@ -1338,6 +1395,7 @@ def wordpressInstall(request, domain): except KeyError: return redirect(loadLoginPage) + def installWordpress(request): try: userID = request.session['userID'] @@ -1346,6 +1404,7 @@ def installWordpress(request): except KeyError: return redirect(loadLoginPage) + def installWordpressStatus(request): try: userID = request.session['userID'] @@ -1354,6 +1413,7 @@ def installWordpressStatus(request): except KeyError: return redirect(loadLoginPage) + def joomlaInstall(request, domain): try: userID = request.session['userID'] @@ -1362,6 +1422,7 @@ def joomlaInstall(request, domain): except KeyError: return redirect(loadLoginPage) + def installJoomla(request): try: userID = request.session['userID'] @@ -1370,6 +1431,7 @@ def installJoomla(request): except KeyError: return redirect(loadLoginPage) + def setupGit(request, domain): try: userID = request.session['userID'] @@ -1378,6 +1440,7 @@ def setupGit(request, domain): except KeyError: return redirect(loadLoginPage) + def setupGitRepo(request): try: userID = request.session['userID'] @@ -1386,6 +1449,7 @@ def setupGitRepo(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def gitNotify(request, domain): try: @@ -1394,6 +1458,7 @@ def gitNotify(request, domain): except KeyError: return redirect(loadLoginPage) + def detachRepo(request): try: userID = request.session['userID'] @@ -1402,6 +1467,7 @@ def detachRepo(request): except KeyError: return redirect(loadLoginPage) + def changeBranch(request): try: userID = request.session['userID'] @@ -1410,6 +1476,7 @@ def changeBranch(request): except KeyError: return redirect(loadLoginPage) + def installPrestaShop(request, domain): try: userID = request.session['userID'] @@ -1418,6 +1485,7 @@ def installPrestaShop(request, domain): except KeyError: return redirect(loadLoginPage) + def installMagento(request, domain): try: userID = request.session['userID'] @@ -1426,6 +1494,7 @@ def installMagento(request, domain): except KeyError: return redirect(loadLoginPage) + def magentoInstall(request): try: userID = request.session['userID'] @@ -1434,6 +1503,7 @@ def magentoInstall(request): except KeyError: return redirect(loadLoginPage) + def installMautic(request, domain): try: userID = request.session['userID'] @@ -1442,6 +1512,7 @@ def installMautic(request, domain): except KeyError: return redirect(loadLoginPage) + def mauticInstall(request): try: userID = request.session['userID'] @@ -1450,6 +1521,7 @@ def mauticInstall(request): except KeyError: return redirect(loadLoginPage) + def prestaShopInstall(request): try: userID = request.session['userID'] @@ -1458,6 +1530,7 @@ def prestaShopInstall(request): except KeyError: return redirect(loadLoginPage) + def sshAccess(request, domain): try: # from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter @@ -1517,6 +1590,7 @@ def sshAccess(request, domain): except KeyError: return redirect(loadLoginPage) + def saveSSHAccessChanges(request): try: userID = request.session['userID'] @@ -1552,6 +1626,7 @@ def syncToMaster(request, domain, childDomain): except KeyError: return redirect(loadLoginPage) + def startSync(request): try: userID = request.session['userID'] @@ -1578,6 +1653,7 @@ def manageGIT(request, domain): except KeyError: return redirect(loadLoginPage) + def fetchFolderDetails(request): try: userID = request.session['userID'] @@ -1586,6 +1662,7 @@ def fetchFolderDetails(request): except KeyError: return redirect(loadLoginPage) + def initRepo(request): try: userID = request.session['userID'] @@ -1594,6 +1671,7 @@ def initRepo(request): except KeyError: return redirect(loadLoginPage) + def setupRemote(request): try: userID = request.session['userID'] @@ -1602,6 +1680,7 @@ def setupRemote(request): except KeyError: return redirect(loadLoginPage) + def changeGitBranch(request): try: userID = request.session['userID'] @@ -1610,6 +1689,7 @@ def changeGitBranch(request): except KeyError: return redirect(loadLoginPage) + def createNewBranch(request): try: userID = request.session['userID'] @@ -1618,6 +1698,7 @@ def createNewBranch(request): except KeyError: return redirect(loadLoginPage) + def commitChanges(request): try: userID = request.session['userID'] @@ -1626,6 +1707,7 @@ def commitChanges(request): except KeyError: return redirect(loadLoginPage) + def gitPull(request): try: userID = request.session['userID'] @@ -1634,6 +1716,7 @@ def gitPull(request): except KeyError: return redirect(loadLoginPage) + def gitPush(request): try: userID = request.session['userID'] @@ -1642,6 +1725,7 @@ def gitPush(request): except KeyError: return redirect(loadLoginPage) + def attachRepoGIT(request): try: userID = request.session['userID'] @@ -1650,6 +1734,7 @@ def attachRepoGIT(request): except KeyError: return redirect(loadLoginPage) + def removeTracking(request): try: userID = request.session['userID'] @@ -1658,6 +1743,7 @@ def removeTracking(request): except KeyError: return redirect(loadLoginPage) + def fetchGitignore(request): try: userID = request.session['userID'] @@ -1666,6 +1752,7 @@ def fetchGitignore(request): except KeyError: return redirect(loadLoginPage) + def saveGitIgnore(request): try: userID = request.session['userID'] @@ -1674,6 +1761,7 @@ def saveGitIgnore(request): except KeyError: return redirect(loadLoginPage) + def fetchCommits(request): try: userID = request.session['userID'] @@ -1682,6 +1770,7 @@ def fetchCommits(request): except KeyError: return redirect(loadLoginPage) + def fetchFiles(request): try: userID = request.session['userID'] @@ -1690,6 +1779,7 @@ def fetchFiles(request): except KeyError: return redirect(loadLoginPage) + def fetchChangesInFile(request): try: userID = request.session['userID'] @@ -1698,6 +1788,7 @@ def fetchChangesInFile(request): except KeyError: return redirect(loadLoginPage) + def saveGitConfigurations(request): try: userID = request.session['userID'] @@ -1706,6 +1797,7 @@ def saveGitConfigurations(request): except KeyError: return redirect(loadLoginPage) + def fetchGitLogs(request): try: userID = request.session['userID'] @@ -1714,6 +1806,7 @@ def fetchGitLogs(request): except KeyError: return redirect(loadLoginPage) + def getSSHConfigs(request): try: userID = request.session['userID'] @@ -1722,6 +1815,7 @@ def getSSHConfigs(request): except KeyError: return redirect(loadLoginPage) + def deleteSSHKey(request): try: userID = request.session['userID'] @@ -1730,6 +1824,7 @@ def deleteSSHKey(request): except KeyError: return redirect(loadLoginPage) + def addSSHKey(request): try: userID = request.session['userID'] @@ -1738,6 +1833,7 @@ def addSSHKey(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def webhook(request, domain): try: @@ -1764,6 +1860,7 @@ def getSwitchStatus(request): except KeyError: return redirect(loadLoginPage) + def switchServer(request): try: userID = request.session['userID'] @@ -1772,6 +1869,7 @@ def switchServer(request): except KeyError: return redirect(loadLoginPage) + def statusFunc(request): try: userID = request.session['userID'] @@ -1783,6 +1881,7 @@ def statusFunc(request): except KeyError: return redirect(loadLoginPage) + def tuneSettings(request): try: userID = request.session['userID'] @@ -1792,6 +1891,7 @@ def tuneSettings(request): except KeyError: return redirect(loadLoginPage) + def saveApacheConfigsToFile(request): try: userID = request.session['userID'] @@ -1812,6 +1912,7 @@ def CreateDockerPackage(request): except BaseException as msg: return HttpResponse(msg) + def CreateDockerPackage(request): try: userID = request.session['userID'] @@ -1820,6 +1921,8 @@ def CreateDockerPackage(request): return wm.CreateDockerPackage(request, userID, None, DeleteID) except KeyError: return redirect(loadLoginPage) + + def AssignPackage(request): try: userID = request.session['userID'] @@ -1828,6 +1931,8 @@ def AssignPackage(request): return wm.AssignPackage(request, userID, None, DeleteID) except KeyError: return redirect(loadLoginPage) + + def CreateDockersite(request): try: userID = request.session['userID'] @@ -1836,6 +1941,7 @@ def CreateDockersite(request): except KeyError: return redirect(loadLoginPage) + def AddDockerpackage(request): try: userID = request.session['userID'] @@ -1844,6 +1950,8 @@ def AddDockerpackage(request): return wm.AddDockerpackage(userID, data) except KeyError: return redirect(loadLoginPage) + + def Getpackage(request): try: userID = request.session['userID'] @@ -1852,6 +1960,8 @@ def Getpackage(request): return wm.Getpackage(userID, data) except KeyError: return redirect(loadLoginPage) + + def Updatepackage(request): try: userID = request.session['userID'] @@ -1860,6 +1970,8 @@ def Updatepackage(request): return wm.Updatepackage(userID, data) except KeyError: return redirect(loadLoginPage) + + def AddAssignment(request): try: userID = request.session['userID'] @@ -1869,6 +1981,7 @@ def AddAssignment(request): except KeyError: return redirect(loadLoginPage) + def submitDockerSiteCreation(request): try: userID = request.session['userID'] @@ -1878,6 +1991,7 @@ def submitDockerSiteCreation(request): except KeyError: return redirect(loadLoginPage) + def ListDockerSites(request): try: userID = request.session['userID'] @@ -1887,6 +2001,7 @@ def ListDockerSites(request): except KeyError: return redirect(loadLoginPage) + def fetchDockersite(request): try: userID = request.session['userID'] @@ -1896,6 +2011,7 @@ def fetchDockersite(request): except KeyError: return redirect(loadLoginPage) + def Dockersitehome(request, dockerapp): try: userID = request.session['userID'] @@ -1904,6 +2020,7 @@ def Dockersitehome(request, dockerapp): except KeyError: return redirect(loadLoginPage) + def fetchWPDetails(request): try: userID = request.session['userID'] @@ -1915,6 +2032,7 @@ def fetchWPDetails(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def startContainer(request): try: @@ -1924,6 +2042,7 @@ def startContainer(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def stopContainer(request): try: @@ -1933,6 +2052,7 @@ def stopContainer(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def restartContainer(request): try: @@ -1942,6 +2062,7 @@ def restartContainer(request): except KeyError: return redirect(loadLoginPage) + @csrf_exempt def get_website_resources(request): try: @@ -1957,7 +2078,7 @@ def get_website_resources(request): # Verify domain ownership currentACL = ACLManager.loadedACL(userID) - + from websiteFunctions.models import Websites try: website = Websites.objects.get(domain=domain) @@ -1980,6 +2101,7 @@ def get_website_resources(request): logging.CyberCPLogFileWriter.writeToFile(f'Error in get_website_resources: {str(msg)}') return JsonResponse({'status': 0, 'error_message': str(msg)}) + @csrf_exempt def get_terminal_jwt(request): import logging @@ -2046,6 +2168,7 @@ def get_terminal_jwt(request): logger.error(f"Exception in get_terminal_jwt: {str(e)}") return JsonResponse({'status': 0, 'error_message': str(e)}) + def fetchWPBackups(request): try: userID = request.session['userID']