diff --git a/plogical/mailUtilities.py b/plogical/mailUtilities.py index 8f7bae67c..1cfb39a43 100644 --- a/plogical/mailUtilities.py +++ b/plogical/mailUtilities.py @@ -582,6 +582,12 @@ InternalHosts refile:/etc/opendkim/TrustedHosts postfixFilePath = "/etc/postfix/main.cf" + # Check if postfix main.cf exists before configuring + if not os.path.exists(postfixFilePath): + logging.CyberCPLogFileWriter.writeToFile(f"configureOpenDKIM: {postfixFilePath} not found, skipping postfix DKIM configuration") + print("1,Postfix not installed") + return + configData = """ smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = $smtpd_milters diff --git a/plogical/virtualHostUtilities.py b/plogical/virtualHostUtilities.py index c08d62d14..12e2af6d7 100644 --- a/plogical/virtualHostUtilities.py +++ b/plogical/virtualHostUtilities.py @@ -53,7 +53,16 @@ class virtualHostUtilities: redisConf = '/usr/local/lsws/conf/dvhost_redis.conf' vhostConfPath = '/usr/local/lsws/conf' + @staticmethod + def emailServicesInstalled(): + """ + Check if email services (Postfix/OpenDKIM) are installed and configured. + Returns True if email services are available, False otherwise. + This checks for the marker file /home/cyberpanel/postfix which is created + during email services installation. + """ + return os.path.exists('/home/cyberpanel/postfix') @staticmethod def OnBoardingHostName(Domain, tempStatusPath, skipRDNSCheck): @@ -82,29 +91,33 @@ class virtualHostUtilities: except: CurrentHostName = '' - if skipRDNSCheck: - pass - else: - if os.path.exists('/home/cyberpanel/postfix'): - pass - else: - message = 'This server does not come with postfix installed. [404]' - print(message) - logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) - logging.CyberCPLogFileWriter.writeToFile(message) - return 0 + # Check if email services are installed + # If not installed and rDNS check is required, log warning but continue + # Email-specific operations will be skipped later + emailServicesAvailable = virtualHostUtilities.emailServicesInstalled() + + if not skipRDNSCheck and not emailServicesAvailable: + message = 'Email services not installed. Hostname setup will continue without email configuration.' + print(message) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) #### - # Get postfix hostname with error handling - try: - PostFixHostname = mailUtilities.FetchPostfixHostname() - except Exception as e: - message = f'Failed to fetch postfix hostname: {str(e)} [404]' - logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) - logging.CyberCPLogFileWriter.writeToFile(message) - return 0 + # Get postfix hostname with error handling (only if email services are installed) + PostFixHostname = None + if emailServicesAvailable: + try: + PostFixHostname = mailUtilities.FetchPostfixHostname() + except Exception as e: + message = f'Failed to fetch postfix hostname: {str(e)}, continuing without email setup' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + emailServicesAvailable = False # Disable email operations if we can't fetch postfix hostname + else: + # Set a default hostname when email services are not available + PostFixHostname = Domain # Get server IP with error handling try: @@ -391,51 +404,66 @@ class virtualHostUtilities: logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Hostname SSL issued,50') + # Only setup mail server SSL if email services are installed + if emailServicesAvailable: + virtualHostUtilities.issueSSLForMailServer(Domain, path) - virtualHostUtilities.issueSSLForMailServer(Domain, path) + try: + with open(filePath, 'r') as f: + x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read()) - try: - with open(filePath, 'r') as f: - x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read()) - - # Safely extract SSL provider from issuer components - issuer_components = x509.get_issuer().get_components() - SSLProvider = 'Denial' # Default to Denial if we can't find the provider - - # Look for the Organization (O) field in the issuer - for component in issuer_components: - if component[0] == b'O': # Organization field - SSLProvider = component[1].decode('utf-8') - break - elif component[0] == b'CN' and SSLProvider == 'Denial': # Fallback to CN if O not found - SSLProvider = component[1].decode('utf-8') - except (FileNotFoundError, IndexError, OpenSSL.crypto.Error) as e: - SSLProvider = 'Denial' - logging.CyberCPLogFileWriter.writeToFile(f"Mail server SSL check error: {str(e)}") + # Safely extract SSL provider from issuer components + issuer_components = x509.get_issuer().get_components() + SSLProvider = 'Denial' # Default to Denial if we can't find the provider - if SSLProvider == 'Denial': - message = 'Failed to issue Mail server SSL, either its DNS record is not propagated or the domain is behind Cloudflare. [404]' - logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) - logging.CyberCPLogFileWriter.writeToFile(message) - config['hostname'] = Domain - config['onboarding'] = 3 - config['skipRDNSCheck'] = skipRDNSCheck - admin.config = json.dumps(config) - admin.save() - return 0 + # Look for the Organization (O) field in the issuer + for component in issuer_components: + if component[0] == b'O': # Organization field + SSLProvider = component[1].decode('utf-8') + break + elif component[0] == b'CN' and SSLProvider == 'Denial': # Fallback to CN if O not found + SSLProvider = component[1].decode('utf-8') + except (FileNotFoundError, IndexError, OpenSSL.crypto.Error) as e: + SSLProvider = 'Denial' + logging.CyberCPLogFileWriter.writeToFile(f"Mail server SSL check error: {str(e)}") + + if SSLProvider == 'Denial': + message = 'Failed to issue Mail server SSL, either its DNS record is not propagated or the domain is behind Cloudflare. [404]' + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, message) + logging.CyberCPLogFileWriter.writeToFile(message) + config['hostname'] = Domain + config['onboarding'] = 3 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + return 0 + else: + config['hostname'] = Domain + config['onboarding'] = 1 + config['skipRDNSCheck'] = skipRDNSCheck + admin.config = json.dumps(config) + admin.save() + # First update the postfix hash database, then restart services + command = 'postmap -F hash:/etc/postfix/vmail_ssl.map && systemctl restart postfix && systemctl restart dovecot' + ProcessUtilities.executioner(command, 'root', True) + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Completed. [200]') else: + # Email services not installed, skip mail server SSL setup + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Email services not installed, skipping mail server SSL setup.') config['hostname'] = Domain config['onboarding'] = 1 config['skipRDNSCheck'] = skipRDNSCheck admin.config = json.dumps(config) admin.save() - # First update the postfix hash database, then restart services - command = 'postmap -F hash:/etc/postfix/vmail_ssl.map && systemctl restart postfix && systemctl restart dovecot' - ProcessUtilities.executioner(command, 'root', True) - logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Completed. [200]') + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Hostname setup completed (without email configuration). [200]') @staticmethod def setupAutoDiscover(mailDomain, tempStatusPath, virtualHostName, admin): + # Check if email services are installed before proceeding + if not virtualHostUtilities.emailServicesInstalled(): + logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Email services not installed, skipping mail domain setup.') + logging.CyberCPLogFileWriter.writeToFile('setupAutoDiscover: Email services not installed, skipping.') + return if mailDomain: logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Creating mail child domain..,80') @@ -479,6 +507,11 @@ local_name %s { postFixPath = '/etc/postfix/main.cf' + # Check if main.cf exists before accessing it + if not os.path.exists(postFixPath): + logging.CyberCPLogFileWriter.writeToFile(f"setupAutoDiscover: {postFixPath} not found, skipping postfix TLS SNI configuration") + return + postFixContent = open(postFixPath, 'r').read() if postFixContent.find('tls_server_sni_maps') == -1: @@ -1108,6 +1141,17 @@ local_name %s { @staticmethod def issueSSLForMailServer(virtualHost, path): try: + # Check if email services are installed before proceeding + if not virtualHostUtilities.emailServicesInstalled(): + logging.CyberCPLogFileWriter.writeToFile("Email services not installed, skipping mail server SSL setup") + print("1,Email services not installed") + return 1, 'Email services not installed' + + # Verify critical email directories exist + if not os.path.exists('/etc/postfix'): + logging.CyberCPLogFileWriter.writeToFile("/etc/postfix directory not found, skipping mail server SSL") + print("1,Postfix directory not found") + return 1, 'Postfix directory not found' srcFullChain = '/etc/letsencrypt/live/' + virtualHost + '/fullchain.pem' srcPrivKey = '/etc/letsencrypt/live/' + virtualHost + '/privkey.pem' @@ -1183,6 +1227,12 @@ local_name %s { filePath = "/etc/postfix/main.cf" + # Check if main.cf exists before trying to read it + if not os.path.exists(filePath): + logging.CyberCPLogFileWriter.writeToFile(f"{filePath} not found, skipping postfix hostname update") + print("1,Postfix main.cf not found") + return 1, 'Postfix main.cf not found' + data = open(filePath, 'r').readlines() writeFile = open(filePath, 'w')