Working setup for CentOS.
This commit is contained in:
@@ -1,7 +1,3 @@
|
|||||||
[general]
|
|
||||||
tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
|
|
||||||
tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
|
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
# Select database engine : postgres or mysql
|
# Select database engine : postgres or mysql
|
||||||
engine = postgres
|
engine = postgres
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ class Amavis(base.Installer):
|
|||||||
return "/etc/amavisd"
|
return "/etc/amavisd"
|
||||||
return "/etc/amavis"
|
return "/etc/amavis"
|
||||||
|
|
||||||
|
def get_daemon_name(self):
|
||||||
|
"""Return appropriate daemon name."""
|
||||||
|
if package.backend.FORMAT == "rpm":
|
||||||
|
return "amavisd"
|
||||||
|
return "amavis"
|
||||||
|
|
||||||
def get_config_files(self):
|
def get_config_files(self):
|
||||||
"""Return appropriate config files."""
|
"""Return appropriate config files."""
|
||||||
if package.backend.FORMAT == "deb":
|
if package.backend.FORMAT == "deb":
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class Clamav(base.Installer):
|
|||||||
"""ClamAV installer."""
|
"""ClamAV installer."""
|
||||||
|
|
||||||
appname = "clamav"
|
appname = "clamav"
|
||||||
daemon_name = "clamav-daemon"
|
|
||||||
packages = {
|
packages = {
|
||||||
"deb": ["clamav-daemon"],
|
"deb": ["clamav-daemon"],
|
||||||
"rpm": [
|
"rpm": [
|
||||||
@@ -55,17 +54,23 @@ class Clamav(base.Installer):
|
|||||||
user = "clamupdate"
|
user = "clamupdate"
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"perl -pi -e 's/^Example/#Example/' /etc/freshclam.conf")
|
"perl -pi -e 's/^Example/#Example/' /etc/freshclam.conf")
|
||||||
utils.exec_cmd(
|
# Check if not present before
|
||||||
"""cat <<EOM >> /usr/lib/systemd/system/clamd@.service
|
path = "/usr/lib/systemd/system/clamd@.service"
|
||||||
|
code, output = utils.exec_cmd(
|
||||||
|
"grep 'WantedBy=multi-user.target' {}".format(path))
|
||||||
|
if code:
|
||||||
|
utils.exec_cmd(
|
||||||
|
"""cat <<EOM >> {}
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOM
|
EOM
|
||||||
""")
|
""".format(path))
|
||||||
|
|
||||||
if utils.dist_name == "ubuntu":
|
if utils.dist_name() == "ubuntu":
|
||||||
# Stop freshclam daemon to allow manual download
|
# Stop freshclam daemon to allow manual download
|
||||||
utils.exec_cmd("service clamav-freshclam stop")
|
utils.exec_cmd("service clamav-freshclam stop")
|
||||||
utils.exec_cmd("freshclam", sudo_user=user)
|
utils.exec_cmd("freshclam", sudo_user=user)
|
||||||
utils.exec_cmd("service clamav-freshclam start")
|
utils.exec_cmd("service clamav-freshclam start")
|
||||||
else:
|
else:
|
||||||
utils.exec_cmd("freshclam", sudo_user=user)
|
utils.exec_cmd("freshclam", sudo_user=user, login=False)
|
||||||
|
|||||||
@@ -52,6 +52,14 @@ class Dovecot(base.Installer):
|
|||||||
"""Additional variables."""
|
"""Additional variables."""
|
||||||
context = super(Dovecot, self).get_template_context()
|
context = super(Dovecot, self).get_template_context()
|
||||||
pw = pwd.getpwnam(self.user)
|
pw = pwd.getpwnam(self.user)
|
||||||
|
if "centos" in utils.dist_name():
|
||||||
|
protocols = "protocols = imap lmtp sieve"
|
||||||
|
extra_protocols = self.config.get("dovecot", "extra_protocols")
|
||||||
|
if extra_protocols:
|
||||||
|
protocols += " {}".format(extra_protocols)
|
||||||
|
else:
|
||||||
|
# Protocols are automatically guessed on debian/ubuntu
|
||||||
|
protocols = ""
|
||||||
context.update({
|
context.update({
|
||||||
"db_driver": self.db_driver,
|
"db_driver": self.db_driver,
|
||||||
"mailboxes_owner_uid": pw[2],
|
"mailboxes_owner_uid": pw[2],
|
||||||
@@ -59,6 +67,7 @@ class Dovecot(base.Installer):
|
|||||||
"modoboa_dbname": self.config.get("modoboa", "dbname"),
|
"modoboa_dbname": self.config.get("modoboa", "dbname"),
|
||||||
"modoboa_dbuser": self.config.get("modoboa", "dbuser"),
|
"modoboa_dbuser": self.config.get("modoboa", "dbuser"),
|
||||||
"modoboa_dbpassword": self.config.get("modoboa", "dbpassword"),
|
"modoboa_dbpassword": self.config.get("modoboa", "dbpassword"),
|
||||||
|
"protocols": protocols
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
# Enable installed protocols
|
# Enable installed protocols
|
||||||
!include_try /usr/share/dovecot/protocols.d/*.protocol
|
!include_try /usr/share/dovecot/protocols.d/*.protocol
|
||||||
|
%protocols
|
||||||
|
|
||||||
# A comma separated list of IPs or hosts where to listen in for connections.
|
# A comma separated list of IPs or hosts where to listen in for connections.
|
||||||
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
|
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ home = %modoboa_venv_path
|
|||||||
chdir = %modoboa_instance_path
|
chdir = %modoboa_instance_path
|
||||||
module = instance.wsgi:application
|
module = instance.wsgi:application
|
||||||
master = true
|
master = true
|
||||||
harakiri = 60
|
|
||||||
processes = %nb_processes
|
processes = %nb_processes
|
||||||
vhost = true
|
vhost = true
|
||||||
no-default-app = true
|
no-default-app = true
|
||||||
socket = %uwsgi_socket_path
|
socket = %uwsgi_socket_path
|
||||||
|
chmod-socket = 660
|
||||||
|
vacuum = true
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class Modoboa(base.Installer):
|
|||||||
context.update({
|
context.update({
|
||||||
"dovecot_mailboxes_owner": (
|
"dovecot_mailboxes_owner": (
|
||||||
self.config.get("dovecot", "mailboxes_owner")),
|
self.config.get("dovecot", "mailboxes_owner")),
|
||||||
"radicale_enabled": "#" if "modoboa-radicale" in extensions else ""
|
"radicale_enabled": "" if "modoboa-radicale" in extensions else "#"
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -128,6 +128,9 @@ class Modoboa(base.Installer):
|
|||||||
"modoboa_stats.RRD_ROOTDIR": rrd_root_dir,
|
"modoboa_stats.RRD_ROOTDIR": rrd_root_dir,
|
||||||
"modoboa_pdfcredentials.STORAGE_DIR": pdf_storage_dir,
|
"modoboa_pdfcredentials.STORAGE_DIR": pdf_storage_dir,
|
||||||
}
|
}
|
||||||
|
for path in ["/var/log/maillog", "/var/log/mail.log"]:
|
||||||
|
if os.path.exists(path):
|
||||||
|
settings["modoboa_stats.LOGFILE"] = path
|
||||||
|
|
||||||
for name, value in settings.items():
|
for name, value in settings.items():
|
||||||
query = (
|
query = (
|
||||||
|
|||||||
@@ -44,11 +44,12 @@ class Nginx(base.Installer):
|
|||||||
if os.path.exists(link):
|
if os.path.exists(link):
|
||||||
return
|
return
|
||||||
os.symlink(dst, link)
|
os.symlink(dst, link)
|
||||||
group = "www-data"
|
group = self.config.get("modoboa", "user")
|
||||||
|
user = "www-data"
|
||||||
else:
|
else:
|
||||||
dst = os.path.join(
|
dst = os.path.join(
|
||||||
self.config_dir, "conf.d", "{}.conf".format(hostname))
|
self.config_dir, "conf.d", "{}.conf".format(hostname))
|
||||||
utils.copy_from_template(src, dst, context)
|
utils.copy_from_template(src, dst, context)
|
||||||
group = "nginx"
|
group = "uwsgi"
|
||||||
system.add_user_to_group(
|
user = "nginx"
|
||||||
group, self.config.get("modoboa", "user"))
|
system.add_user_to_group(user, group)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
"""Postfix related tools."""
|
"""Postfix related tools."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import configparser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser as configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .. import package
|
from .. import package
|
||||||
@@ -29,6 +33,17 @@ class Postfix(base.Installer):
|
|||||||
|
|
||||||
def install_packages(self):
|
def install_packages(self):
|
||||||
"""Preconfigure postfix package installation."""
|
"""Preconfigure postfix package installation."""
|
||||||
|
if "centos" in utils.dist_name():
|
||||||
|
config = configparser.SafeConfigParser()
|
||||||
|
with open("/etc/yum.repos.d/CentOS-Base.repo") as fp:
|
||||||
|
config.readfp(fp)
|
||||||
|
config.set("centosplus", "enabled", "1")
|
||||||
|
config.set("centosplus", "includepkgs", "postfix-*")
|
||||||
|
config.set("base", "exclude", "postfix-*")
|
||||||
|
config.set("updates", "exclude", "postfix-*")
|
||||||
|
with open("/etc/yum.repos.d/CentOS-Base.repo", "w") as fp:
|
||||||
|
config.write(fp)
|
||||||
|
|
||||||
package.backend.preconfigure(
|
package.backend.preconfigure(
|
||||||
"postfix", "main_mailer_type", "select", "No configuration")
|
"postfix", "main_mailer_type", "select", "No configuration")
|
||||||
super(Postfix, self).install_packages()
|
super(Postfix, self).install_packages()
|
||||||
@@ -71,6 +86,16 @@ class Postfix(base.Installer):
|
|||||||
" ".join(extensions), db_url, self.config_dir))
|
" ".join(extensions), db_url, self.config_dir))
|
||||||
utils.exec_cmd(cmd)
|
utils.exec_cmd(cmd)
|
||||||
|
|
||||||
|
# Check chroot directory
|
||||||
|
chroot_dir = "/var/spool/postfix/etc"
|
||||||
|
chroot_files = ["services", "resolv.conf"]
|
||||||
|
if not os.path.exists(chroot_dir):
|
||||||
|
os.mkdir(chroot_dir)
|
||||||
|
for f in chroot_files:
|
||||||
|
path = os.path.join(chroot_dir, f)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
utils.copy_file(os.path.join("/etc", f), path)
|
||||||
|
|
||||||
# Generate EDH parameters
|
# Generate EDH parameters
|
||||||
if not os.path.exists("{}/dh2048.pem".format(self.config_dir)):
|
if not os.path.exists("{}/dh2048.pem".format(self.config_dir)):
|
||||||
cmd = "openssl dhparam -out dh2048.pem 2048"
|
cmd = "openssl dhparam -out dh2048.pem 2048"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Razor(base.Installer):
|
|||||||
utils.copy_file(
|
utils.copy_file(
|
||||||
os.path.join(path, "razor-agent.conf"), self.config_dir)
|
os.path.join(path, "razor-agent.conf"), self.config_dir)
|
||||||
utils.exec_cmd("razor-admin -home {} -discover".format(path),
|
utils.exec_cmd("razor-admin -home {} -discover".format(path),
|
||||||
sudo_user=user)
|
sudo_user=user, login=False)
|
||||||
utils.exec_cmd("razor-admin -home {} -register".format(path),
|
utils.exec_cmd("razor-admin -home {} -register".format(path),
|
||||||
sudo_user=user)
|
sudo_user=user, login=False)
|
||||||
# FIXME: move log file to /var/log ?
|
# FIXME: move log file to /var/log ?
|
||||||
|
|||||||
@@ -55,5 +55,7 @@ class Spamassassin(base.Installer):
|
|||||||
def post_run(self):
|
def post_run(self):
|
||||||
"""Additional tasks."""
|
"""Additional tasks."""
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"pyzor discover", sudo_user=self.config.get("amavis", "user"))
|
"pyzor discover", sudo_user=self.config.get("amavis", "user"),
|
||||||
|
login=False
|
||||||
|
)
|
||||||
install("razor", self.config)
|
install("razor", self.config)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Uwsgi(base.Installer):
|
|||||||
"modoboa_venv_path": self.config.get("modoboa", "venv_path"),
|
"modoboa_venv_path": self.config.get("modoboa", "venv_path"),
|
||||||
"modoboa_instance_path": (
|
"modoboa_instance_path": (
|
||||||
self.config.get("modoboa", "instance_path")),
|
self.config.get("modoboa", "instance_path")),
|
||||||
"uwsgi_socket_path": self.socket_path
|
"uwsgi_socket_path": self.socket_path,
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -58,9 +58,11 @@ class Uwsgi(base.Installer):
|
|||||||
os.symlink(dst, link)
|
os.symlink(dst, link)
|
||||||
else:
|
else:
|
||||||
system.add_user_to_group(
|
system.add_user_to_group(
|
||||||
"uwsgi", self.config.get("modoboa", "user"))
|
self.config.get("modoboa", "user"), "uwsgi")
|
||||||
|
utils.exec_cmd("chmod -R g+w {}/media".format(
|
||||||
|
self.config.get("modoboa", "instance_path")))
|
||||||
pattern = (
|
pattern = (
|
||||||
"s/emperor-tyrant = true/emperor-tyrant false/")
|
"s/emperor-tyrant = true/emperor-tyrant = false/")
|
||||||
utils.exec_cmd(
|
utils.exec_cmd(
|
||||||
"perl -pi -e '{}' /etc/uwsgi.ini".format(pattern))
|
"perl -pi -e '{}' /etc/uwsgi.ini".format(pattern))
|
||||||
|
|
||||||
|
|||||||
49
modoboa_installer/ssl.py
Normal file
49
modoboa_installer/ssl.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
"""SSL tools."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateBackend(object):
|
||||||
|
"""Base class."""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
"""Set path to certificates."""
|
||||||
|
self.config = config
|
||||||
|
for base_dir in ["/etc/pki/tls", "/etc/ssl"]:
|
||||||
|
if os.path.exists(base_dir):
|
||||||
|
self.config.set(
|
||||||
|
"general", "tls_key_file",
|
||||||
|
"{}/private/%(hostname)s.key".format(base_dir))
|
||||||
|
self.config.set(
|
||||||
|
"general", "tls_cert_file",
|
||||||
|
"{}/certs/%(hostname)s.cert".format(base_dir))
|
||||||
|
return
|
||||||
|
raise RuntimeError("Cannot find a directory to store certificate")
|
||||||
|
|
||||||
|
|
||||||
|
class SelfSignedCertificate(CertificateBackend):
|
||||||
|
"""Create a self signed certificate."""
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
"""Create a certificate."""
|
||||||
|
if os.path.exists(self.config.get("general", "tls_key_file")):
|
||||||
|
answer = utils.user_input(
|
||||||
|
"Overwrite the existing SSL certificate? (y/N) ")
|
||||||
|
if not answer.lower().startswith("y"):
|
||||||
|
return
|
||||||
|
utils.printcolor(
|
||||||
|
"Generating new self-signed certificate", utils.YELLOW)
|
||||||
|
utils.exec_cmd(
|
||||||
|
"openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 "
|
||||||
|
"-subj '/CN={}' -keyout {} -out {}".format(
|
||||||
|
self.config.get("general", "hostname"),
|
||||||
|
self.config.get("general", "tls_key_file"),
|
||||||
|
self.config.get("general", "tls_cert_file"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_backend(config):
|
||||||
|
"""Return the appropriate backend."""
|
||||||
|
return SelfSignedCertificate(config)
|
||||||
@@ -33,7 +33,7 @@ def user_input(message):
|
|||||||
return answer
|
return answer
|
||||||
|
|
||||||
|
|
||||||
def exec_cmd(cmd, sudo_user=None, pinput=None, **kwargs):
|
def exec_cmd(cmd, sudo_user=None, pinput=None, login=True, **kwargs):
|
||||||
"""Execute a shell command.
|
"""Execute a shell command.
|
||||||
Run a command using the current user. Set :keyword:`sudo_user` if
|
Run a command using the current user. Set :keyword:`sudo_user` if
|
||||||
you need different privileges.
|
you need different privileges.
|
||||||
@@ -45,7 +45,7 @@ def exec_cmd(cmd, sudo_user=None, pinput=None, **kwargs):
|
|||||||
"""
|
"""
|
||||||
sudo_user = ENV.get("sudo_user", sudo_user)
|
sudo_user = ENV.get("sudo_user", sudo_user)
|
||||||
if sudo_user is not None:
|
if sudo_user is not None:
|
||||||
cmd = "sudo -i -u %s %s" % (sudo_user, cmd)
|
cmd = "sudo {}-u {} {}".format("-i " if login else "", sudo_user, cmd)
|
||||||
if "shell" not in kwargs:
|
if "shell" not in kwargs:
|
||||||
kwargs["shell"] = True
|
kwargs["shell"] = True
|
||||||
if pinput is not None:
|
if pinput is not None:
|
||||||
|
|||||||
4
run.py
4
run.py
@@ -11,6 +11,7 @@ except ImportError:
|
|||||||
from modoboa_installer import scripts
|
from modoboa_installer import scripts
|
||||||
from modoboa_installer import utils
|
from modoboa_installer import utils
|
||||||
from modoboa_installer import package
|
from modoboa_installer import package
|
||||||
|
from modoboa_installer import ssl
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -30,6 +31,8 @@ def main():
|
|||||||
config = configparser.SafeConfigParser()
|
config = configparser.SafeConfigParser()
|
||||||
with open("installer.cfg") as fp:
|
with open("installer.cfg") as fp:
|
||||||
config.readfp(fp)
|
config.readfp(fp)
|
||||||
|
if not config.has_section("general"):
|
||||||
|
config.add_section("general")
|
||||||
config.set("general", "hostname", args.hostname)
|
config.set("general", "hostname", args.hostname)
|
||||||
utils.printcolor(
|
utils.printcolor(
|
||||||
"Your mail server {} will be installed with the following components:"
|
"Your mail server {} will be installed with the following components:"
|
||||||
@@ -52,6 +55,7 @@ def main():
|
|||||||
"and come back later ;)", utils.BLUE)
|
"and come back later ;)", utils.BLUE)
|
||||||
utils.printcolor("Starting...", utils.GREEN)
|
utils.printcolor("Starting...", utils.GREEN)
|
||||||
package.backend.install("sudo")
|
package.backend.install("sudo")
|
||||||
|
ssl.get_backend(config).create()
|
||||||
scripts.install("modoboa", config)
|
scripts.install("modoboa", config)
|
||||||
scripts.install("postfix", config)
|
scripts.install("postfix", config)
|
||||||
scripts.install("amavis", config)
|
scripts.install("amavis", config)
|
||||||
|
|||||||
Reference in New Issue
Block a user