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]
|
||||
# Select database engine : postgres or mysql
|
||||
engine = postgres
|
||||
|
||||
@@ -25,6 +25,12 @@ class Amavis(base.Installer):
|
||||
return "/etc/amavisd"
|
||||
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):
|
||||
"""Return appropriate config files."""
|
||||
if package.backend.FORMAT == "deb":
|
||||
|
||||
@@ -12,7 +12,6 @@ class Clamav(base.Installer):
|
||||
"""ClamAV installer."""
|
||||
|
||||
appname = "clamav"
|
||||
daemon_name = "clamav-daemon"
|
||||
packages = {
|
||||
"deb": ["clamav-daemon"],
|
||||
"rpm": [
|
||||
@@ -55,17 +54,23 @@ class Clamav(base.Installer):
|
||||
user = "clamupdate"
|
||||
utils.exec_cmd(
|
||||
"perl -pi -e 's/^Example/#Example/' /etc/freshclam.conf")
|
||||
# Check if not present before
|
||||
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 >> /usr/lib/systemd/system/clamd@.service
|
||||
"""cat <<EOM >> {}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOM
|
||||
""")
|
||||
""".format(path))
|
||||
|
||||
if utils.dist_name == "ubuntu":
|
||||
if utils.dist_name() == "ubuntu":
|
||||
# Stop freshclam daemon to allow manual download
|
||||
utils.exec_cmd("service clamav-freshclam stop")
|
||||
utils.exec_cmd("freshclam", sudo_user=user)
|
||||
utils.exec_cmd("service clamav-freshclam start")
|
||||
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."""
|
||||
context = super(Dovecot, self).get_template_context()
|
||||
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({
|
||||
"db_driver": self.db_driver,
|
||||
"mailboxes_owner_uid": pw[2],
|
||||
@@ -59,6 +67,7 @@ class Dovecot(base.Installer):
|
||||
"modoboa_dbname": self.config.get("modoboa", "dbname"),
|
||||
"modoboa_dbuser": self.config.get("modoboa", "dbuser"),
|
||||
"modoboa_dbpassword": self.config.get("modoboa", "dbpassword"),
|
||||
"protocols": protocols
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
# Enable installed protocols
|
||||
!include_try /usr/share/dovecot/protocols.d/*.protocol
|
||||
%protocols
|
||||
|
||||
# A comma separated list of IPs or hosts where to listen in for connections.
|
||||
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
|
||||
|
||||
@@ -6,8 +6,9 @@ home = %modoboa_venv_path
|
||||
chdir = %modoboa_instance_path
|
||||
module = instance.wsgi:application
|
||||
master = true
|
||||
harakiri = 60
|
||||
processes = %nb_processes
|
||||
vhost = true
|
||||
no-default-app = true
|
||||
socket = %uwsgi_socket_path
|
||||
chmod-socket = 660
|
||||
vacuum = true
|
||||
|
||||
@@ -108,7 +108,7 @@ class Modoboa(base.Installer):
|
||||
context.update({
|
||||
"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
|
||||
|
||||
@@ -128,6 +128,9 @@ class Modoboa(base.Installer):
|
||||
"modoboa_stats.RRD_ROOTDIR": rrd_root_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():
|
||||
query = (
|
||||
|
||||
@@ -44,11 +44,12 @@ class Nginx(base.Installer):
|
||||
if os.path.exists(link):
|
||||
return
|
||||
os.symlink(dst, link)
|
||||
group = "www-data"
|
||||
group = self.config.get("modoboa", "user")
|
||||
user = "www-data"
|
||||
else:
|
||||
dst = os.path.join(
|
||||
self.config_dir, "conf.d", "{}.conf".format(hostname))
|
||||
utils.copy_from_template(src, dst, context)
|
||||
group = "nginx"
|
||||
system.add_user_to_group(
|
||||
group, self.config.get("modoboa", "user"))
|
||||
group = "uwsgi"
|
||||
user = "nginx"
|
||||
system.add_user_to_group(user, group)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
"""Postfix related tools."""
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
import os
|
||||
|
||||
from .. import package
|
||||
@@ -29,6 +33,17 @@ class Postfix(base.Installer):
|
||||
|
||||
def install_packages(self):
|
||||
"""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(
|
||||
"postfix", "main_mailer_type", "select", "No configuration")
|
||||
super(Postfix, self).install_packages()
|
||||
@@ -71,6 +86,16 @@ class Postfix(base.Installer):
|
||||
" ".join(extensions), db_url, self.config_dir))
|
||||
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
|
||||
if not os.path.exists("{}/dh2048.pem".format(self.config_dir)):
|
||||
cmd = "openssl dhparam -out dh2048.pem 2048"
|
||||
|
||||
@@ -36,7 +36,7 @@ class Razor(base.Installer):
|
||||
utils.copy_file(
|
||||
os.path.join(path, "razor-agent.conf"), self.config_dir)
|
||||
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),
|
||||
sudo_user=user)
|
||||
sudo_user=user, login=False)
|
||||
# FIXME: move log file to /var/log ?
|
||||
|
||||
@@ -55,5 +55,7 @@ class Spamassassin(base.Installer):
|
||||
def post_run(self):
|
||||
"""Additional tasks."""
|
||||
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)
|
||||
|
||||
@@ -34,7 +34,7 @@ class Uwsgi(base.Installer):
|
||||
"modoboa_venv_path": self.config.get("modoboa", "venv_path"),
|
||||
"modoboa_instance_path": (
|
||||
self.config.get("modoboa", "instance_path")),
|
||||
"uwsgi_socket_path": self.socket_path
|
||||
"uwsgi_socket_path": self.socket_path,
|
||||
})
|
||||
return context
|
||||
|
||||
@@ -58,9 +58,11 @@ class Uwsgi(base.Installer):
|
||||
os.symlink(dst, link)
|
||||
else:
|
||||
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 = (
|
||||
"s/emperor-tyrant = true/emperor-tyrant false/")
|
||||
"s/emperor-tyrant = true/emperor-tyrant = false/")
|
||||
utils.exec_cmd(
|
||||
"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
|
||||
|
||||
|
||||
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.
|
||||
Run a command using the current user. Set :keyword:`sudo_user` if
|
||||
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)
|
||||
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:
|
||||
kwargs["shell"] = True
|
||||
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 utils
|
||||
from modoboa_installer import package
|
||||
from modoboa_installer import ssl
|
||||
|
||||
|
||||
def main():
|
||||
@@ -30,6 +31,8 @@ def main():
|
||||
config = configparser.SafeConfigParser()
|
||||
with open("installer.cfg") as fp:
|
||||
config.readfp(fp)
|
||||
if not config.has_section("general"):
|
||||
config.add_section("general")
|
||||
config.set("general", "hostname", args.hostname)
|
||||
utils.printcolor(
|
||||
"Your mail server {} will be installed with the following components:"
|
||||
@@ -52,6 +55,7 @@ def main():
|
||||
"and come back later ;)", utils.BLUE)
|
||||
utils.printcolor("Starting...", utils.GREEN)
|
||||
package.backend.install("sudo")
|
||||
ssl.get_backend(config).create()
|
||||
scripts.install("modoboa", config)
|
||||
scripts.install("postfix", config)
|
||||
scripts.install("amavis", config)
|
||||
|
||||
Reference in New Issue
Block a user