568 lines
24 KiB

import os
import sys
import random
from ee.core.variables import EEVariables
from ee.core.aptget import EEAptGet
from ee.cli.plugins.eestack import EEStack
from ee.core.shellexec import EEShellExec
from ee.core.shellexec import CommandExecutionError
from ee.core.fileutils import EEFileUtils
from ee.core.git import EEGit
from ee.core.services import EEService
from ee.cli.plugins.mysqlstack import EEMysqlStack
from ee.cli.plugins.nginxstack import EENginxStack
from ee.cli.plugins.phpstack import EEPhpStack
from ee.core.logging import Log
from ee.cli.main import app
class EEMailStack(EEStack):
"""
EasyEngine MAIL stack
"""
packages_name = EEVariables.ee_mail
app = app
log = app.log
def __init__(self, packages_name=None):
"""
Initialize packages list in stack
pkgs_name : list of packages to be intialized for operations
in stack
"""
self.packages_name = self._get_stack()
super(EEMailStack, self).__init__(self.packages_name)
def _get_stack(self):
return EEMailStack.packages_name
def _requirement_check(self):
"""
Check if requirements for this EEWebmailAdmin stack are fullfilled.
"""
if not EENginxStack(self).is_installed():
self.log.info("Installing nginxstack")
EENginxStack(self).install_stack()
if not EEPhpStack(self).is_installed():
self.log.info("Installing phpstack")
EEPhpStack(self).install_stack()
if not EEMysqlStack(self).is_installed():
self.log.info("Installing mysqlstack")
EEMysqlStack(self).install_stack()
def _add_repo(self):
"""
Add repository for packages to be downloaded from
"""
self.log.info("Adding MAIL repository, please wait...")
EEAptGet.update(self)
def _pre_install_stack(self):
"""
Defines pre-install activities done before installing mail stack
"""
# Add mail repository
self._requirement_check()
self._add_repo()
self.log.debug(self, 'Executing the command debconf-set-selections.')
try:
EEShellExec.cmd_exec(self, "echo \"dovecot-core dovecot-core/"
"create-ssl-cert boolean yes\" "
"| debconf-set-selections")
EEShellExec.cmd_exec(self, "echo \"dovecot-core dovecot-core"
"/ssl-cert-name string $(hostname -f)\""
" | debconf-set-selections")
except CommandExecutionError as e:
self.log.error("Failed to initialize dovecot packages")
def _post_install_stack(self):
"""
Defines activities done after installing mail stack
"""
self.log.debug(self, "Adding user")
try:
EEShellExec.cmd_exec(self, "adduser --uid 5000 --home /var"
"/vmail --disabled-password --gecos "
"'' vmail")
except CommandExecutionError as e:
self.log.error(self, "Unable to add vmail user for mail server")
try:
EEShellExec.cmd_exec(self, "openssl req -new -x509 -days"
" 3650 "
"-nodes -subj /commonName={hostname}"
"/emailAddress={email} -out /etc/ssl"
"/certs/dovecot."
"pem -keyout "
"/etc/ssl/private/dovecot.pem"
.format(hostname=EEVariables.ee_fqdn,
email=EEVariables.ee_email))
except CommandExecutionError as e:
self.log.error(self, "Unable to generate PEM key for dovecot")
self.log.debug(self, "Setting Privileges to "
"/etc/ssl/private/dovecot.pem file ")
EEFileUtils.chmod(self, "/etc/ssl/private/dovecot.pem", 0o600)
# Custom Dovecot configuration by EasyEngine
data = dict()
self.log.debug(self, "Writting configuration into file"
"/etc/dovecot/conf.d/auth-sql.conf.ext ")
ee_dovecot = open('/etc/dovecot/conf.d/auth-sql.conf.ext',
encoding='utf-8', mode='w')
app.render((data), 'auth-sql-conf.mustache',
out=ee_dovecot)
ee_dovecot.close()
data = dict(email=EEVariables.ee_email)
self.log.debug(self, "Writting configuration into file"
"/etc/dovecot/conf.d/99-ee.conf ")
ee_dovecot = open('/etc/dovecot/conf.d/99-ee.conf',
encoding='utf-8', mode='w')
app.render((data), 'dovecot.mustache', out=ee_dovecot)
ee_dovecot.close()
try:
EEShellExec.cmd_exec(self, "sed -i \"s/\\!include "
"auth-system.conf.ext/#\\!include "
"auth-system.conf.ext/\" "
"/etc/dovecot/conf.d/10-auth.conf")
EEShellExec.cmd_exec(self, "sed -i \"s\'/etc/dovecot/"
"dovecot.pem\'/etc/ssl/certs/"
"dovecot.pem"
"\'\" /etc/dovecot/conf.d/"
"10-ssl.conf")
EEShellExec.cmd_exec(self, "sed -i \"s\'/etc/dovecot/"
"private/dovecot.pem\'/etc/ssl/"
"private"
"/dovecot.pem\'\" /etc/dovecot/"
"conf.d/"
"10-ssl.conf")
# Custom Postfix configuration needed with Dovecot
# Changes in master.cf
# TODO: Find alternative for sed in Python
EEShellExec.cmd_exec(self, "sed -i \'s/#submission/"
"submission/\'"
" /etc/postfix/master.cf")
EEShellExec.cmd_exec(self, "sed -i \'s/#smtps/smtps/\'"
" /etc/postfix/master.cf")
EEShellExec.cmd_exec(self, "postconf -e \"smtpd_sasl_type "
"= dovecot\"")
EEShellExec.cmd_exec(self, "postconf -e \"smtpd_sasl_path "
"= private/auth\"")
EEShellExec.cmd_exec(self, "postconf -e \""
"smtpd_sasl_auth_enable = "
"yes\"")
EEShellExec.cmd_exec(self, "postconf -e \""
" smtpd_relay_restrictions ="
" permit_sasl_authenticated, "
" permit_mynetworks, "
" reject_unauth_destination\"")
EEShellExec.cmd_exec(self, "postconf -e \""
"smtpd_tls_mandatory_"
"protocols = !SSLv2,!SSLv3\"")
EEShellExec.cmd_exec(self, "postconf -e \"smtp_tls_"
"mandatory_protocols = !SSLv2,"
"!SSLv3\"")
EEShellExec.cmd_exec(self, "postconf -e \"smtpd_tls"
"_protocols = !SSLv2,!SSLv3\"")
EEShellExec.cmd_exec(self, "postconf -e \"smtp_tls"
"_protocols = !SSLv2,!SSLv3\"")
EEShellExec.cmd_exec(self, "postconf -e \"mydestination "
"= localhost\"")
EEShellExec.cmd_exec(self, "postconf -e \"virtual"
"_transport "
"= lmtp:unix:private/dovecot-lmtp\"")
EEShellExec.cmd_exec(self, "postconf -e \"virtual_uid_"
"maps = static:5000\"")
EEShellExec.cmd_exec(self, "postconf -e \"virtual_gid_"
"maps = static:5000\"")
EEShellExec.cmd_exec(self, "postconf -e \""
" virtual_mailbox_domains = "
"mysql:/etc/postfix/mysql/virtual_"
"domains_maps.cf\"")
EEShellExec.cmd_exec(self, "postconf -e \"virtual_mailbox"
"_maps"
" = mysql:/etc/postfix/mysql/virtual_"
"mailbox_maps.cf\"")
EEShellExec.cmd_exec(self, "postconf -e \"virtual_alias"
"_maps "
"= mysql:/etc/postfix/mysql/virtual_"
"alias_maps.cf\"")
EEShellExec.cmd_exec(self, "openssl req -new -x509 -days "
" 3650 -nodes -subj /commonName="
"{hostname}/emailAddress={email}"
" -out /etc/ssl/certs/postfix.pem"
" -keyout /etc/ssl/private/"
"postfix.pem"
.format(hostname=EEVariables.ee_fqdn,
email=EEVariables.ee_email))
EEShellExec.cmd_exec(self, "chmod 0600 /etc/ssl/private"
"/postfix.pem")
EEShellExec.cmd_exec(self, "postconf -e \"smtpd_tls_cert_"
"file = /etc/ssl/certs/postfix.pem\"")
EEShellExec.cmd_exec(self, "postconf -e \"smtpd_tls_key_"
"file = /etc/ssl/private/"
"postfix.pem\"")
except CommandExecutionError as e:
self.log.error(self, "Failed to update Dovecot configuration")
# Sieve configuration
if not os.path.exists('/var/lib/dovecot/sieve/'):
self.log.debug(self, 'Creating directory '
'/var/lib/dovecot/sieve/ ')
os.makedirs('/var/lib/dovecot/sieve/')
# Custom sieve configuration by EasyEngine
data = dict()
self.log.debug(self, "Writting configuration of EasyEngine into "
"file /var/lib/dovecot/sieve/default.sieve")
ee_sieve = open('/var/lib/dovecot/sieve/default.sieve',
encoding='utf-8', mode='w')
app.render((data), 'default-sieve.mustache',
out=ee_sieve)
ee_sieve.close()
# Compile sieve rules
self.log.debug(self, "Setting Privileges to dovecot ")
# EEShellExec.cmd_exec(self, "chown -R vmail:vmail /var/lib"
# "/dovecot")
EEFileUtils.chown(self, "/var/lib/dovecot", 'vmail', 'vmail',
recursive=True)
try:
EEShellExec.cmd_exec(self, "sievec /var/lib/dovecot/"
"/sieve/default.sieve")
except CommandExecutionError as e:
raise SiteError("Failed to compile default.sieve")
EEGit.add(self, ["/etc/postfix", "/etc/dovecot"],
msg="Installed mail server")
EEService.restart_service(self, 'dovecot')
EEService.reload_service(self, 'postfix')
def install_stack(self):
"""
Install MAIL stack
"""
self.log.info("Installing MAIL stack, please wait...")
self._pre_install_stack()
super(EEMailStack, self).install_stack()
self._post_install_stack()
EEWebmailAdmin(self).install_stack()
def remove_stack(self):
"""
Remove MAIL stack
"""
self.log.info("Removing MAIL stack, please wait...")
super(EEMailStack, self).remove_stack()
def purge_stack(self):
self.log.info("Purging MAIL stack, please wait...")
super(EEMailStack, self).purge_stack()
import string
import shutil
from ee.core.download import EEDownload
from ee.core.extract import EEExtract
from ee.core.mysql import EEMysql
class EEWebmailAdmin (EEStack):
"""
EasyEngine Web Mail stack
VimbAdmin + Roundcube
"""
packages_name = {
'Vimbadmin': "https://github.com/opensolutions/ViMbAdmin/archive/{0}.tar.gz".format(EEVariables.ee_vimbadmin),
'Roundcube': "https://github.com/roundcube/roundcubemail/releases/download/{0}/roundcubemail-{0}.tar.gz".format(EEVariables.ee_roundcube)
}
app = app
log = app.log
def __init__(self, package_name=None, package_url=None):
"""
Initialize packages list in stack
pkgs_name : list of packages to be intialized for operations
in stack
package_url : list of urls from where packages to be fetched
"""
self.packages_name = self._get_stack()
super(EEWebmailAdmin, self).__init__(self.packages_name)
def _get_stack(self):
return EEWebmailAdmin.packages_name
def _get_package_url(self, package_name):
return self.packages_name[package_name]
def _get_package_path(self, package):
return "/tmp/{0}.tar.gz".format(package)
def _pre_install(self):
"""
"""
for pkg in self.packages_name:
print([self._get_package_url(pkg), '/tmp/{0}.tar.gz'.format(pkg), pkg])
print()
EEDownload.download(self, [[self._get_package_url(pkg), '/tmp/{0}.tar.gz'.format(pkg), pkg]])
def _post_install(self):
"""
"""
pass
def _install_vimbadmin(self):
"""
"""
self.log.debug(self, "Extracting ViMbAdmin.tar.gz to "
"location /tmp/")
EEExtract.extract(self, self._get_package_path('Vimbadmin'), '/tmp/')
if not os.path.exists('{0}22222/htdocs/'
.format(EEVariables.ee_webroot)):
self.log.debug(self, "Creating directory "
"{0}22222/htdocs/"
.format(EEVariables.ee_webroot))
os.makedirs('{0}22222/htdocs/'
.format(EEVariables.ee_webroot))
shutil.move('/tmp/ViMbAdmin-{0}/'
.format(EEVariables.ee_vimbadmin),
'{0}22222/htdocs/vimbadmin/'
.format(EEVariables.ee_webroot))
# Donwload composer and install ViMbAdmin
self.log.debug(self, "Downloading composer "
"https://getcomposer.org/installer | php ")
try:
EEShellExec.cmd_exec(self, "cd {0}22222/htdocs"
"/vimbadmin; curl"
" -sS https://getcomposer.org/"
"installer |"
" php".format(EEVariables.ee_webroot))
self.log.debug(self, "Installating of composer")
EEShellExec.cmd_exec(self, "cd {0}22222/htdocs"
"/vimbadmin && "
"php composer.phar install "
"--prefer-dist"
" --no-dev && rm -f {0}22222/htdocs"
"/vimbadmin/composer.phar"
.format(EEVariables.ee_webroot))
except CommandExecutionError as e:
raise SiteError("Failed to setup ViMbAdmin")
# Configure vimbadmin database
vm_passwd = ''.join(random.sample(string.ascii_letters, 8))
self.log.debug(self, "Creating vimbadmin database if not exist")
EEMysql.execute(self, "create database if not exists"
" vimbadmin")
self.log.debug(self, " grant all privileges on `vimbadmin`.* to"
" `vimbadmin`@`{0}` IDENTIFIED BY"
" ' '".format(app.config.get('mysql',
'grant-host')))
EEMysql.execute(self, "grant all privileges on `vimbadmin`.* "
" to `vimbadmin`@`{0}` IDENTIFIED BY"
" '{1}'".format(app.config.get('mysql',
'grant-host'), vm_passwd),
errormsg="Cannot grant "
"user privileges", log=False)
vm_salt = (''.join(random.sample(string.ascii_letters +
string.ascii_letters, 64)))
# Custom Vimbadmin configuration by EasyEngine
data = dict(salt=vm_salt, host=EEVariables.ee_mysql_host,
password=vm_passwd,
php_user=EEVariables.ee_php_user)
self.log.debug(self, 'Writting the ViMbAdmin configuration to '
'file {0}22222/htdocs/vimbadmin/application/'
'configs/application.ini'
.format(EEVariables.ee_webroot))
ee_vmb = open('{0}22222/htdocs/vimbadmin/application/'
'configs/application.ini'
.format(EEVariables.ee_webroot),
encoding='utf-8', mode='w')
app.render((data), 'vimbadmin.mustache',
out=ee_vmb)
ee_vmb.close()
shutil.copyfile("{0}22222/htdocs/vimbadmin/public/"
".htaccess.dist"
.format(EEVariables.ee_webroot),
"{0}22222/htdocs/vimbadmin/public/"
".htaccess".format(EEVariables.ee_webroot))
self.log.debug(self, "Executing command "
"{0}22222/htdocs/vimbadmin/bin"
"/doctrine2-cli.php orm:schema-tool:"
"create".format(EEVariables.ee_webroot))
try:
EEShellExec.cmd_exec(self, "{0}22222/htdocs/vimbadmin"
"/bin/doctrine2-cli.php "
"orm:schema-tool:create"
.format(EEVariables.ee_webroot))
except CommandExecutionError as e:
raise SiteError("Unable to create ViMbAdmin schema")
EEFileUtils.chown(self, '{0}22222'
.format(EEVariables.ee_webroot),
EEVariables.ee_php_user,
EEVariables.ee_php_user,
recursive=True)
# Copy Dovecot and Postfix templates which are depednet on
# Vimbadmin
if not os.path.exists('/etc/postfix/mysql/'):
self.log.debug(self, "Creating directory "
"/etc/postfix/mysql/")
os.makedirs('/etc/postfix/mysql/')
if EEVariables.ee_mysql_host is "localhost":
data = dict(password=vm_passwd, host="127.0.0.1")
else:
data = dict(password=vm_passwd,
host=EEVariables.ee_mysql_host)
vm_config = open('/etc/postfix/mysql/virtual_alias_maps.cf',
encoding='utf-8', mode='w')
app.render((data), 'virtual_alias_maps.mustache',
out=vm_config)
vm_config.close()
self.log.debug(self, "Writting configuration to "
"/etc/postfix/mysql"
"/virtual_domains_maps.cf file")
vm_config = open('/etc/postfix/mysql/virtual_domains_maps.cf',
encoding='utf-8', mode='w')
app.render((data), 'virtual_domains_maps.mustache',
out=vm_config)
vm_config.close()
self.log.debug(self, "Writting configuration to "
"/etc/postfix/mysql"
"/virtual_mailbox_maps.cf file")
vm_config = open('/etc/postfix/mysql/virtual_mailbox_maps.cf',
encoding='utf-8', mode='w')
app.render((data), 'virtual_mailbox_maps.mustache',
out=vm_config)
vm_config.close()
self.log.debug(self, "Writting configration"
" to /etc/dovecot/dovecot-sql.conf.ext file ")
vm_config = open('/etc/dovecot/dovecot-sql.conf.ext',
encoding='utf-8', mode='w')
app.render((data), 'dovecot-sql-conf.mustache',
out=vm_config)
vm_config.close()
def _install_roundcube(self):
"""
Install and configure roundcube for mailstack
"""
# Extract RoundCubemail
self.log.debug(self, "Extracting file /tmp/roundcube.tar.gz "
"to location /tmp/ ")
EEExtract.extract(self, self._get_package_path('Roundcube'), '/tmp/')
if not os.path.exists('{0}roundcubemail'
.format(EEVariables.ee_webroot)):
self.log.debug(self, "Creating new directory "
" {0}roundcubemail/"
.format(EEVariables.ee_webroot))
os.makedirs('{0}roundcubemail/'
.format(EEVariables.ee_webroot))
shutil.move('/tmp/roundcubemail-{0}/'
.format(EEVariables.ee_roundcube),
'{0}roundcubemail/htdocs'
.format(EEVariables.ee_webroot))
# Install Roundcube depednet pear packages
EEShellExec.cmd_exec(self, "pear install Mail_Mime Net_SMTP"
" Mail_mimeDecode Net_IDNA2-beta "
"Auth_SASL Net_Sieve Crypt_GPG")
# Configure roundcube database
rc_passwd = ''.join(random.sample(string.ascii_letters, 8))
self.log.debug(self, "Creating Database roundcubemail")
EEMysql.execute(self, "create database if not exists "
" roundcubemail")
self.log.debug(self, "grant all privileges"
" on `roundcubemail`.* to "
" `roundcube`@`{0}` IDENTIFIED BY "
"' '".format(app.config.get(
'mysql', 'grant-host')))
EEMysql.execute(self, "grant all privileges"
" on `roundcubemail`.* to "
" `roundcube`@`{0}` IDENTIFIED BY "
"'{1}'".format(app.config.get(
'mysql', 'grant-host'),
rc_passwd))
EEShellExec.cmd_exec(self, "mysql roundcubemail < {0}"
"roundcubemail/htdocs/SQL/mysql"
".initial.sql"
.format(EEVariables.ee_webroot))
shutil.copyfile("{0}roundcubemail/htdocs/config/"
"config.inc.php.sample"
.format(EEVariables.ee_webroot),
"{0}roundcubemail/htdocs/config/"
"config.inc.php"
.format(EEVariables.ee_webroot))
EEShellExec.cmd_exec(self, "sed -i \"s\'mysql://roundcube:"
"pass@localhost/roundcubemail\'mysql://"
"roundcube:{0}@{1}/"
"roundcubemail\'\" {2}roundcubemail"
"/htdocs/config/config."
"inc.php"
.format(rc_passwd,
EEVariables.ee_mysql_host,
EEVariables.ee_webroot))
# Sieve plugin configuration in roundcube
EEShellExec.cmd_exec(self, "bash -c \"sed -i \\\"s:\$config\["
"\'plugins\'\] "
"= array(:\$config\['plugins'\] = "
"array(\\n \'sieverules\',:\\\" "
"{0}roundcubemail/htdocs/config"
.format(EEVariables.ee_webroot)
+ "/config.inc.php\"")
EEShellExec.cmd_exec(self, "echo \"\$config['sieverules_port']"
"=4190;\" >> {0}roundcubemail"
.format(EEVariables.ee_webroot)
+ "/htdocs/config/config.inc.php")
data = dict(site_name='webmail', www_domain='webmail',
static=False,
basic=True, wp=False, w3tc=False, wpfc=False,
wpsc=False, multisite=False, wpsubdir=False,
webroot=EEVariables.ee_webroot, ee_db_name='',
ee_db_user='', ee_db_pass='', ee_db_host='',
rc=True)
self.log.debug(self, 'Writting the nginx configuration for '
'RoundCubemail')
ee_rc = open('/etc/nginx/sites-available/webmail',
encoding='utf-8', mode='w')
app.render((data), 'virtualconf.mustache',
out=ee_rc)
ee_rc.close()
def install_stack(self):
"""
"""
self._pre_install()
self._install_vimbadmin()
self._install_roundcube()