refactor: Add AcmetoolDeployer

- This splits the existing deploy_acmetool() routine into methods for
  the install, configure, and activate stages.
This commit is contained in:
cliffmccarthy
2025-09-08 08:16:59 -05:00
parent 6afd31fb17
commit afc1be2671
2 changed files with 119 additions and 96 deletions

View File

@@ -19,7 +19,7 @@ from pyinfra.facts.server import Sysctl
from pyinfra.facts.systemd import SystemdEnabled from pyinfra.facts.systemd import SystemdEnabled
from pyinfra.operations import apt, files, pip, server, systemd from pyinfra.operations import apt, files, pip, server, systemd
from .acmetool import deploy_acmetool from .acmetool import AcmetoolDeployer
from .deployer import Deployer from .deployer import Deployer
from .www import build_webpages, find_merge_conflict, get_paths 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", line="nameserver 9.9.9.9",
) )
tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"]
unbound_deployer = UnboundDeployer() unbound_deployer = UnboundDeployer()
iroh_deployer = IrohDeployer(enable_iroh_relay=config.enable_iroh_relay) 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) opendkim_deployer = OpendkimDeployer(mail_domain=mail_domain)
# Dovecot should be started before Postfix # Dovecot should be started before Postfix
@@ -929,6 +935,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
all_deployers = [ all_deployers = [
unbound_deployer, unbound_deployer,
iroh_deployer, iroh_deployer,
acmetool_deployer,
opendkim_deployer, opendkim_deployer,
dovecot_deployer, dovecot_deployer,
postfix_deployer, postfix_deployer,
@@ -1013,12 +1020,9 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
iroh_deployer.configure() iroh_deployer.configure()
iroh_deployer.activate() iroh_deployer.activate()
# Deploy acmetool to have TLS certificates. acmetool_deployer.install()
tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"] acmetool_deployer.configure()
deploy_acmetool( acmetool_deployer.activate()
email=config.acme_email,
domains=tls_domains,
)
apt.packages( apt.packages(
# required for setfacl for echobot # required for setfacl for echobot

View File

@@ -2,104 +2,123 @@ import importlib.resources
from pyinfra.operations import apt, files, server, systemd 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( class AcmetoolDeployer(Deployer):
name="Remove old acmetool cronjob, it is replaced with systemd timer.", def __init__(self, *, email, domains, **kwargs):
path="/etc/cron.d/acmetool", super().__init__(**kwargs)
present=False, 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( @staticmethod
name="Install acmetool hook.", def install_impl():
src=importlib.resources.files(__package__).joinpath("acmetool.hook").open("rb"), apt.packages(
dest="/etc/acme/hooks/nginx", name="Install acmetool",
user="root", packages=["acmetool"],
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,
)
files.template( files.file(
src=importlib.resources.files(__package__).joinpath("response-file.yaml.j2"), name="Remove old acmetool cronjob, it is replaced with systemd timer.",
dest="/var/lib/acme/conf/responses", path="/etc/cron.d/acmetool",
user="root", present=False,
group="root", )
mode="644",
email=email,
)
files.template( files.put(
src=importlib.resources.files(__package__).joinpath("target.yaml.j2"), name="Install acmetool hook.",
dest="/var/lib/acme/conf/target", src=importlib.resources.files(__package__).joinpath("acmetool.hook").open("rb"),
user="root", dest="/etc/acme/hooks/nginx",
group="root", user="root",
mode="644", 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( def configure_impl(self):
src=importlib.resources.files(__package__).joinpath( files.template(
"acmetool-redirector.service" src=importlib.resources.files(__package__).joinpath("response-file.yaml.j2"),
), dest="/var/lib/acme/conf/responses",
dest="/etc/systemd/system/acmetool-redirector.service", user="root",
user="root", group="root",
group="root", mode="644",
mode="644", email=self.email,
) )
systemd.service( files.template(
name="Setup acmetool-redirector service", src=importlib.resources.files(__package__).joinpath("target.yaml.j2"),
service="acmetool-redirector.service", dest="/var/lib/acme/conf/target",
running=True, user="root",
enabled=True, group="root",
restarted=service_file.changed, mode="644",
) )
reconcile_service_file = files.put( service_file = files.put(
src=importlib.resources.files(__package__).joinpath( src=importlib.resources.files(__package__).joinpath(
"acmetool-reconcile.service" "acmetool-redirector.service"
), ),
dest="/etc/systemd/system/acmetool-reconcile.service", dest="/etc/systemd/system/acmetool-redirector.service",
user="root", user="root",
group="root", group="root",
mode="644", mode="644",
) )
self.need_restart_redirector = service_file.changed
systemd.service( reconcile_service_file = files.put(
name="Setup acmetool-reconcile service", src=importlib.resources.files(__package__).joinpath(
service="acmetool-reconcile.service", "acmetool-reconcile.service"
running=False, ),
enabled=False, dest="/etc/systemd/system/acmetool-reconcile.service",
daemon_reload=reconcile_service_file.changed, user="root",
) group="root",
mode="644",
)
self.need_restart_reconcile_service = reconcile_service_file.changed
reconcile_timer_file = files.put( reconcile_timer_file = files.put(
src=importlib.resources.files(__package__).joinpath("acmetool-reconcile.timer"), src=importlib.resources.files(__package__).joinpath("acmetool-reconcile.timer"),
dest="/etc/systemd/system/acmetool-reconcile.timer", dest="/etc/systemd/system/acmetool-reconcile.timer",
user="root", user="root",
group="root", group="root",
mode="644", mode="644",
) )
self.need_restart_reconcile_timer = reconcile_timer_file.changed
systemd.service( def activate_impl(self):
name="Setup acmetool-reconcile timer", systemd.service(
service="acmetool-reconcile.timer", name="Setup acmetool-redirector service",
running=True, service="acmetool-redirector.service",
enabled=True, running=True,
daemon_reload=reconcile_timer_file.changed, enabled=True,
) restarted=self.need_restart_redirector,
)
self.need_restart_redirector = False
server.shell( systemd.service(
name=f"Request certificate for: {', '.join(domains)}", name="Setup acmetool-reconcile service",
commands=[f"acmetool want --xlog.severity=debug {' '.join(domains)}"], 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)}"],
)