diff --git a/cmdeploy/src/cmdeploy/__init__.py b/cmdeploy/src/cmdeploy/__init__.py index ba77c79b..aae202ca 100644 --- a/cmdeploy/src/cmdeploy/__init__.py +++ b/cmdeploy/src/cmdeploy/__init__.py @@ -19,7 +19,7 @@ from pyinfra.facts.server import Sysctl from pyinfra.facts.systemd import SystemdEnabled from pyinfra.operations import apt, files, pip, server, systemd -from .acmetool import deploy_acmetool +from .acmetool import AcmetoolDeployer from .deployer import Deployer from .www import build_webpages, find_merge_conflict, get_paths @@ -913,8 +913,14 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None: line="nameserver 9.9.9.9", ) + tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"] + unbound_deployer = UnboundDeployer() iroh_deployer = IrohDeployer(enable_iroh_relay=config.enable_iroh_relay) + + # Deploy acmetool to have TLS certificates. + acmetool_deployer = AcmetoolDeployer(email=config.acme_email, domains=tls_domains) + opendkim_deployer = OpendkimDeployer(mail_domain=mail_domain) # Dovecot should be started before Postfix @@ -929,6 +935,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None: all_deployers = [ unbound_deployer, iroh_deployer, + acmetool_deployer, opendkim_deployer, dovecot_deployer, postfix_deployer, @@ -1013,12 +1020,9 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None: iroh_deployer.configure() iroh_deployer.activate() - # Deploy acmetool to have TLS certificates. - tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"] - deploy_acmetool( - email=config.acme_email, - domains=tls_domains, - ) + acmetool_deployer.install() + acmetool_deployer.configure() + acmetool_deployer.activate() apt.packages( # required for setfacl for echobot diff --git a/cmdeploy/src/cmdeploy/acmetool/__init__.py b/cmdeploy/src/cmdeploy/acmetool/__init__.py index 26b0f4a2..9052aced 100644 --- a/cmdeploy/src/cmdeploy/acmetool/__init__.py +++ b/cmdeploy/src/cmdeploy/acmetool/__init__.py @@ -2,104 +2,123 @@ import importlib.resources from pyinfra.operations import apt, files, server, systemd +from ..deployer import Deployer -def deploy_acmetool(email="", domains=[]): - """Deploy acmetool.""" - apt.packages( - name="Install acmetool", - packages=["acmetool"], - ) - files.file( - name="Remove old acmetool cronjob, it is replaced with systemd timer.", - path="/etc/cron.d/acmetool", - present=False, - ) +class AcmetoolDeployer(Deployer): + def __init__(self, *, email, domains, **kwargs): + super().__init__(**kwargs) + self.domains = domains + self.email = email + self.need_restart_redirector = False + self.need_restart_reconcile_service = False + self.need_restart_reconcile_timer = False - files.put( - name="Install acmetool hook.", - src=importlib.resources.files(__package__).joinpath("acmetool.hook").open("rb"), - dest="/etc/acme/hooks/nginx", - user="root", - group="root", - mode="755", - ) - files.file( - name="Remove acmetool hook from the wrong location where it was previously installed.", - path="/usr/lib/acme/hooks/nginx", - present=False, - ) + @staticmethod + def install_impl(): + apt.packages( + name="Install acmetool", + packages=["acmetool"], + ) - files.template( - src=importlib.resources.files(__package__).joinpath("response-file.yaml.j2"), - dest="/var/lib/acme/conf/responses", - user="root", - group="root", - mode="644", - email=email, - ) + files.file( + name="Remove old acmetool cronjob, it is replaced with systemd timer.", + path="/etc/cron.d/acmetool", + present=False, + ) - files.template( - src=importlib.resources.files(__package__).joinpath("target.yaml.j2"), - dest="/var/lib/acme/conf/target", - user="root", - group="root", - mode="644", - ) + files.put( + name="Install acmetool hook.", + src=importlib.resources.files(__package__).joinpath("acmetool.hook").open("rb"), + dest="/etc/acme/hooks/nginx", + user="root", + group="root", + mode="755", + ) + files.file( + name="Remove acmetool hook from the wrong location where it was previously installed.", + path="/usr/lib/acme/hooks/nginx", + present=False, + ) - service_file = files.put( - src=importlib.resources.files(__package__).joinpath( - "acmetool-redirector.service" - ), - dest="/etc/systemd/system/acmetool-redirector.service", - user="root", - group="root", - mode="644", - ) + def configure_impl(self): + files.template( + src=importlib.resources.files(__package__).joinpath("response-file.yaml.j2"), + dest="/var/lib/acme/conf/responses", + user="root", + group="root", + mode="644", + email=self.email, + ) - systemd.service( - name="Setup acmetool-redirector service", - service="acmetool-redirector.service", - running=True, - enabled=True, - restarted=service_file.changed, - ) + files.template( + src=importlib.resources.files(__package__).joinpath("target.yaml.j2"), + dest="/var/lib/acme/conf/target", + user="root", + group="root", + mode="644", + ) - reconcile_service_file = files.put( - src=importlib.resources.files(__package__).joinpath( - "acmetool-reconcile.service" - ), - dest="/etc/systemd/system/acmetool-reconcile.service", - user="root", - group="root", - mode="644", - ) + service_file = files.put( + src=importlib.resources.files(__package__).joinpath( + "acmetool-redirector.service" + ), + dest="/etc/systemd/system/acmetool-redirector.service", + user="root", + group="root", + mode="644", + ) + self.need_restart_redirector = service_file.changed - systemd.service( - name="Setup acmetool-reconcile service", - service="acmetool-reconcile.service", - running=False, - enabled=False, - daemon_reload=reconcile_service_file.changed, - ) + reconcile_service_file = files.put( + src=importlib.resources.files(__package__).joinpath( + "acmetool-reconcile.service" + ), + dest="/etc/systemd/system/acmetool-reconcile.service", + user="root", + group="root", + mode="644", + ) + self.need_restart_reconcile_service = reconcile_service_file.changed - reconcile_timer_file = files.put( - src=importlib.resources.files(__package__).joinpath("acmetool-reconcile.timer"), - dest="/etc/systemd/system/acmetool-reconcile.timer", - user="root", - group="root", - mode="644", - ) + reconcile_timer_file = files.put( + src=importlib.resources.files(__package__).joinpath("acmetool-reconcile.timer"), + dest="/etc/systemd/system/acmetool-reconcile.timer", + user="root", + group="root", + mode="644", + ) + self.need_restart_reconcile_timer = reconcile_timer_file.changed - systemd.service( - name="Setup acmetool-reconcile timer", - service="acmetool-reconcile.timer", - running=True, - enabled=True, - daemon_reload=reconcile_timer_file.changed, - ) + def activate_impl(self): + systemd.service( + name="Setup acmetool-redirector service", + service="acmetool-redirector.service", + running=True, + enabled=True, + restarted=self.need_restart_redirector, + ) + self.need_restart_redirector = False - server.shell( - name=f"Request certificate for: {', '.join(domains)}", - commands=[f"acmetool want --xlog.severity=debug {' '.join(domains)}"], - ) + systemd.service( + name="Setup acmetool-reconcile service", + service="acmetool-reconcile.service", + running=False, + enabled=False, + daemon_reload=self.need_restart_reconcile_service, + ) + self.need_restart_reconcile_service = False + + systemd.service( + name="Setup acmetool-reconcile timer", + service="acmetool-reconcile.timer", + running=True, + enabled=True, + daemon_reload=self.need_restart_reconcile_timer, + ) + self.need_restart_reconcile_timer = False + + server.shell( + name=f"Request certificate for: {', '.join(self.domains)}", + commands=[f"acmetool want --xlog.severity=debug {' '.join(self.domains)}"], + )