Fix custom installation email components bug: Skip email operations when services not installed

This commit resolves the issue where CyberPanel attempts to configure email/DKIM settings
even when email services were explicitly disabled during custom installation, causing
hostname SSL setup and website creation to fail with "No such file or directory: '/etc/postfix/main.cf'" errors.

Changes:
- Added emailServicesInstalled() utility function to check for /home/cyberpanel/postfix marker
- OnBoardingHostName(): Wrap email operations (issueSSLForMailServer, postfix commands) with checks
- OnBoardingHostName(): Allow hostname setup to complete without email services
- issueSSLForMailServer(): Add early return if email services not installed
- issueSSLForMailServer(): Verify /etc/postfix directory exists before operations
- issueSSLForMailServer(): Check /etc/postfix/main.cf exists before reading
- setupAutoDiscover(): Add early return if email services not installed
- setupAutoDiscover(): Check /etc/postfix/main.cf exists before accessing
- mailUtilities.configureOpenDKIM(): Verify main.cf exists before configuration

Impact:
- Hostname SSL setup now completes successfully without email components
- Website creation works correctly on custom installs without email
- No more file not found errors for /etc/postfix/main.cf
- Graceful degradation: operations skip email setup with log messages

Fixes: Custom installation hostname SSL 404 error
Fixes: Website creation DKIM failure on custom installs
Related: Ticket #RMKRFFGKC
This commit is contained in:
usmannasir
2025-11-22 03:48:11 +05:00
parent bbd0c4e136
commit 836a6e26a7
2 changed files with 108 additions and 52 deletions

View File

@@ -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

View File

@@ -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')