From 083c6aedc6d8046c19f637ec34723812f292a0e9 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Tue, 2 Jun 2020 20:34:17 +1000 Subject: [PATCH] acme: add ipa-acme-manage command Add the ipa-acme-manage command which can be used to enable or disable the IPA ACME service. It must be used on each server. In the future we will implement deployment-wide configuration (including enable/disable) of the ACME service via IPA API, with configuration stored in and replicated by LDAP. But until then, we need a simple command for administrators to use. Part of: https://pagure.io/freeipa/issue/4751 Reviewed-By: Rob Crittenden --- freeipa.spec.in | 2 + install/tools/Makefile.am | 2 + install/tools/ipa-acme-manage.in | 8 +++ install/tools/man/Makefile.am | 1 + install/tools/man/ipa-acme-manage.1 | 33 ++++++++++++ ipaserver/install/ipa_acme_manage.py | 79 ++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+) create mode 100755 install/tools/ipa-acme-manage.in create mode 100644 install/tools/man/ipa-acme-manage.1 create mode 100644 ipaserver/install/ipa_acme_manage.py diff --git a/freeipa.spec.in b/freeipa.spec.in index dac9567e3..74e026a4a 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -1194,6 +1194,7 @@ fi %{_sbindir}/ipa-pkinit-manage %{_sbindir}/ipa-crlgen-manage %{_sbindir}/ipa-cert-fix +%{_sbindir}/ipa-acme-manage %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit %{_libexecdir}/certmonger/ipa-server-guard %dir %{_libexecdir}/ipa @@ -1262,6 +1263,7 @@ fi %{_mandir}/man1/ipa-pkinit-manage.1* %{_mandir}/man1/ipa-crlgen-manage.1* %{_mandir}/man1/ipa-cert-fix.1* +%{_mandir}/man1/ipa-acme-manage.1* %files -n python3-ipaserver diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am index 12562e48f..1a0100a0d 100644 --- a/install/tools/Makefile.am +++ b/install/tools/Makefile.am @@ -35,6 +35,7 @@ dist_noinst_DATA = \ ipa-httpd-kdcproxy.in \ ipa-pki-retrieve-key.in \ ipa-pki-wait-running.in \ + ipa-acme-manage.in \ $(NULL) nodist_sbin_SCRIPTS = \ @@ -63,6 +64,7 @@ nodist_sbin_SCRIPTS = \ ipa-pkinit-manage \ ipa-crlgen-manage \ ipa-cert-fix \ + ipa-acme-manage \ $(NULL) appdir = $(libexecdir)/ipa/ diff --git a/install/tools/ipa-acme-manage.in b/install/tools/ipa-acme-manage.in new file mode 100755 index 000000000..f1925e822 --- /dev/null +++ b/install/tools/ipa-acme-manage.in @@ -0,0 +1,8 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2020 FreeIPA Contributors see COPYING for license +# + +from ipaserver.install.ipa_acme_manage import IPAACMEManage + +IPAACMEManage.run_cli() diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am index 6f5fd17a4..d1ea046e0 100644 --- a/install/tools/man/Makefile.am +++ b/install/tools/man/Makefile.am @@ -29,6 +29,7 @@ dist_man1_MANS = \ ipa-pkinit-manage.1 \ ipa-crlgen-manage.1 \ ipa-cert-fix.1 \ + ipa-acme-manage.1 \ $(NULL) dist_man8_MANS = \ diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1 new file mode 100644 index 000000000..abf29498e --- /dev/null +++ b/install/tools/man/ipa-acme-manage.1 @@ -0,0 +1,33 @@ +.\" +.\" Copyright (C) 2020 FreeIPA Contributors see COPYING for license +.\" +.TH "ipa-acme-manage" "1" "Jun 2 2020" "FreeIPA" "FreeIPA Manual Pages" +.SH "NAME" +ipa\-acme\-manage \- Manage the FreeIPA ACME service +.SH "SYNOPSIS" +ipa\-acme\-manage enable|disable +.SH "DESCRIPTION" + +Use the \fIipa-acme-manage\fR command to enable or disable the ACME +service on a FreeIPA CA server. + +In a FreeIPA topology all CA servers capable of ACME will +have the ACME service deployed. The service is not enabled +by default. It is expected that the ACME service will either be +enabled on all CA servers, or disabled on all CA servers. However +it must be enabled or disabled on each individual server. + +.SH "COMMANDS" +.TP +\fBenable\fR +Enable the ACME service on this host. +.TP +\fBdisable\fR +Disable the ACME service on this host. + +.SH "EXIT STATUS" +0 if the command was successful + +1 if the host is not a CA server + +2 if the host is not a FreeIPA server diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py new file mode 100644 index 000000000..96630b05a --- /dev/null +++ b/ipaserver/install/ipa_acme_manage.py @@ -0,0 +1,79 @@ +# +# Copyright (C) 2020 FreeIPA Contributors see COPYING for license +# + +import enum +import pathlib + +from ipaplatform.paths import paths +from ipapython.admintool import AdminTool +from ipapython.directivesetter import DirectiveSetter +from ipaserver.install import cainstance +from ipaserver.install.installutils import is_ipa_configured + +# Manages the FreeIPA ACME service on a per-server basis. +# +# This program is a stop-gap until the deployment-wide management of +# the ACME service is implemented. So we will eventually have API +# calls for managing the ACME service, e.g. `ipa acme-enable'. +# After that is implemented, we can either deprecate and eventually +# remove this program, or make it a wrapper for the API commands. + + +class Command(enum.Enum): + ENABLE = 'enable' + DISABLE = 'disable' + + +class IPAACMEManage(AdminTool): + command_name = "ipa-acme-manage" + usage = "%prog [enable|disable]" + description = "Manage the IPA ACME service" + + def validate_options(self): + # needs root now - if/when this program changes to an API + # wrapper we will no longer need root. + super(IPAACMEManage, self).validate_options(needs_root=True) + + if len(self.args) < 1: + self.option_parser.error(f'missing command argument') + else: + try: + self.command = Command(self.args[0]) + except ValueError: + self.option_parser.error(f'unknown command "{self.args[0]}"') + + def run(self): + if not is_ipa_configured(): + print("IPA is not configured.") + return 2 + + if not cainstance.is_ca_installed_locally(): + print("CA is not installed on this server.") + return 1 + + if self.command == Command.ENABLE: + directive = 'enabled' + value = 'true' + elif self.command == Command.DISABLE: + directive = 'enabled' + value = 'false' + else: + raise RuntimeError('programmer error: unhandled enum case') + + with DirectiveSetter( + paths.PKI_ACME_ENGINE_CONF, + separator='=', + quotes=False, + ) as ds: + ds.set(directive, value) + + # Work around a limitation in PKI ACME service file watching + # where renames (what DirectiveSetter does) are not detected. + # It will be fixed, but keeping the workaround will do no harm. + pathlib.Path(paths.PKI_ACME_ENGINE_CONF).touch() + + # Nothing else to do; the Dogtag ACME service monitors engine.conf + # for updates and reconfigures itself as required. + + return 0