From 2217c891ad6af3a91cdb04648f4f4956afecf9b2 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Thu, 12 Sep 2024 11:04:21 -0500 Subject: [PATCH] Update pkidestroy to support ACME pkidestroy has been updated to support removing ACME from PKI server. If it is the last subsystem on the server, the server will be removed as well. The ACMESubsystem.create() has been modified to create a base dir (i.e. /var/lib/pki//) which is used by PKIServer.load_subsystems() to determine if the subsystem exists. The code that creates the conf and logs folders has been moved into create_conf() and create_logs(), respectively. The pki-server acme-remove has been updated to provide options to remove the conf and logs folders. The tests that use pkispawn to install ACME have been updated to use pkidestroy to remove ACME. --- .github/workflows/acme-basic-test.yml | 32 ++++++++++-- .github/workflows/acme-postgresql-test.yml | 4 +- .github/workflows/acme-separate-test.yml | 35 ++++++++++--- base/server/python/pki/server/__init__.py | 2 +- base/server/python/pki/server/cli/acme.py | 34 +++++++++---- .../python/pki/server/deployment/__init__.py | 49 +++++++++++++++++++ base/server/python/pki/server/subsystem.py | 47 +++++++++++++++++- 7 files changed, 176 insertions(+), 27 deletions(-) diff --git a/.github/workflows/acme-basic-test.yml b/.github/workflows/acme-basic-test.yml index 35f3670fe58..2f6ccbd8c56 100644 --- a/.github/workflows/acme-basic-test.yml +++ b/.github/workflows/acme-basic-test.yml @@ -119,6 +119,7 @@ jobs: # TODO: review permissions cat > expected << EOF + drwxrwx--- pkiuser pkiuser acme lrwxrwxrwx pkiuser pkiuser alias -> /var/lib/pki/pki-tomcat/conf/alias lrwxrwxrwx pkiuser pkiuser bin -> /usr/share/tomcat/bin drwxrwx--- pkiuser pkiuser ca @@ -175,7 +176,7 @@ jobs: # TODO: review permissions cat > expected << EOF - drwxr-xr-x pkiuser pkiuser acme + drwxrwx--- pkiuser pkiuser acme drwxr-x--- pkiuser pkiuser backup drwxrwx--- pkiuser pkiuser ca -rw-rw-r-- pkiuser pkiuser catalina.$DATE.log @@ -188,6 +189,23 @@ jobs: diff expected output + - name: Check ACME base dir + if: always() + run: | + docker exec pki ls -l /var/lib/pki/pki-tomcat/acme \ + | sed \ + -e '/^total/d' \ + -e 's/^\(\S*\) *\S* *\(\S*\) *\(\S*\) *\S* *\S* *\S* *\S* *\(.*\)$/\1 \2 \3 \4/' \ + | tee output + + # TODO: review permissions + cat > expected << EOF + lrwxrwxrwx pkiuser pkiuser conf -> /var/lib/pki/pki-tomcat/conf/acme + lrwxrwxrwx pkiuser pkiuser logs -> /var/lib/pki/pki-tomcat/logs/acme + EOF + + diff expected output + - name: Check ACME conf dir run: | # check file types, owners, and permissions @@ -221,6 +239,11 @@ jobs: run: | docker exec pki cat /etc/pki/pki-tomcat/acme/realm.conf + - name: Check ACME logs dir + if: always() + run: | + docker exec pki ls -l /var/log/pki/pki-tomcat/acme + - name: Check initial ACME accounts run: | docker exec ds ldapsearch \ @@ -664,9 +687,7 @@ jobs: diff expected actual - name: Remove ACME from PKI container - run: | - docker exec pki pki-server acme-undeploy --wait - docker exec pki pki-server acme-remove + run: docker exec pki pkidestroy -s ACME -v - name: Remove CA from PKI container run: docker exec pki pkidestroy -s CA -v @@ -700,6 +721,7 @@ jobs: # TODO: review permissions cat > expected << EOF drwxrwx--- pkiuser pkiuser Catalina + drwxrwx--- pkiuser pkiuser acme drwxrwx--- pkiuser pkiuser alias drwxrwx--- pkiuser pkiuser ca -rw-r--r-- pkiuser pkiuser catalina.policy @@ -729,7 +751,7 @@ jobs: # TODO: review permissions cat > expected << EOF - drwxr-xr-x pkiuser pkiuser acme + drwxrwx--- pkiuser pkiuser acme drwxr-x--- pkiuser pkiuser backup drwxrwx--- pkiuser pkiuser ca -rw-rw-r-- pkiuser pkiuser catalina.$DATE.log diff --git a/.github/workflows/acme-postgresql-test.yml b/.github/workflows/acme-postgresql-test.yml index 235429c44fb..ff7950e34b6 100644 --- a/.github/workflows/acme-postgresql-test.yml +++ b/.github/workflows/acme-postgresql-test.yml @@ -539,9 +539,7 @@ jobs: diff expected actual - name: Remove ACME from PKI container - run: | - docker exec pki pki-server acme-undeploy --wait - docker exec pki pki-server acme-remove + run: docker exec pki pkidestroy -s ACME -v - name: Remove CA from PKI container run: docker exec pki pkidestroy -s CA -v diff --git a/.github/workflows/acme-separate-test.yml b/.github/workflows/acme-separate-test.yml index 227f0084416..5455a0e7f49 100644 --- a/.github/workflows/acme-separate-test.yml +++ b/.github/workflows/acme-separate-test.yml @@ -178,6 +178,7 @@ jobs: # TODO: review permissions cat > expected << EOF + drwxrwx--- pkiuser pkiuser acme lrwxrwxrwx pkiuser pkiuser alias -> /var/lib/pki/pki-tomcat/conf/alias lrwxrwxrwx pkiuser pkiuser bin -> /usr/share/tomcat/bin drwxr-x--- pkiuser pkiuser common @@ -231,6 +232,7 @@ jobs: # TODO: review permissions cat > expected << EOF + drwxrwx--- pkiuser pkiuser acme drwxr-x--- pkiuser pkiuser backup -rw-r--r-- pkiuser pkiuser catalina.$DATE.log -rw-r--r-- pkiuser pkiuser host-manager.$DATE.log @@ -240,6 +242,23 @@ jobs: diff expected output + - name: Check ACME base dir + if: always() + run: | + docker exec acme ls -l /var/lib/pki/pki-tomcat/acme \ + | sed \ + -e '/^total/d' \ + -e 's/^\(\S*\) *\S* *\(\S*\) *\(\S*\) *\S* *\S* *\S* *\S* *\(.*\)$/\1 \2 \3 \4/' \ + | tee output + + # TODO: review permissions + cat > expected << EOF + lrwxrwxrwx pkiuser pkiuser conf -> /var/lib/pki/pki-tomcat/conf/acme + lrwxrwxrwx pkiuser pkiuser logs -> /var/lib/pki/pki-tomcat/logs/acme + EOF + + diff expected output + - name: Check ACME conf dir run: | # check file types, owners, and permissions @@ -273,6 +292,11 @@ jobs: run: | docker exec acme cat /etc/pki/pki-tomcat/acme/realm.conf + - name: Check ACME logs dir + if: always() + run: | + docker exec acme ls -l /var/log/pki/pki-tomcat/acme + - name: Check initial ACME accounts run: | docker exec acmeds ldapsearch \ @@ -724,14 +748,10 @@ jobs: diff expected actual - name: Remove ACME - run: | - docker exec acme pki-server acme-undeploy --wait -v - docker exec acme pki-server acme-remove -v - docker exec acme pki-server stop --wait -v - docker exec acme pki-server remove -v + run: docker exec acme pkidestroy -s ACME -v - name: Remove CA - run: docker exec ca pkidestroy -i pki-tomcat -s CA -v + run: docker exec ca pkidestroy -s CA -v - name: Check ACME server base dir after removal run: | @@ -762,6 +782,7 @@ jobs: # TODO: review permissions cat > expected << EOF drwxr-x--- pkiuser pkiuser Catalina + drwxrwx--- pkiuser pkiuser acme drwxrwx--- pkiuser pkiuser alias -rw-rw---- pkiuser pkiuser catalina.policy lrwxrwxrwx pkiuser pkiuser catalina.properties -> /usr/share/pki/server/conf/catalina.properties @@ -789,7 +810,7 @@ jobs: # TODO: review permissions cat > expected << EOF - drwxr-xr-x pkiuser pkiuser acme + drwxrwx--- pkiuser pkiuser acme drwxr-x--- pkiuser pkiuser backup -rw-r--r-- pkiuser pkiuser catalina.$DATE.log -rw-r--r-- pkiuser pkiuser host-manager.$DATE.log diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index fbc8f6159c4..197a615f56e 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -55,7 +55,7 @@ ETC_SYSTEMD_DIR = '/etc/systemd' LIB_SYSTEMD_DIR = '/lib/systemd' -SUBSYSTEM_TYPES = ['ca', 'kra', 'ocsp', 'tks', 'tps'] +SUBSYSTEM_TYPES = ['ca', 'kra', 'ocsp', 'tks', 'tps', 'acme'] DEFAULT_DIR_MODE = 0o0770 DEFAULT_FILE_MODE = 0o0660 diff --git a/base/server/python/pki/server/cli/acme.py b/base/server/python/pki/server/cli/acme.py index 07a9202d7ed..589e1133289 100644 --- a/base/server/python/pki/server/cli/acme.py +++ b/base/server/python/pki/server/cli/acme.py @@ -100,6 +100,8 @@ def execute(self, argv): subsystem = pki.server.subsystem.ACMESubsystem(instance) subsystem.create(force=force) + subsystem.create_conf(force=force) + subsystem.create_logs(force=force) class ACMERemoveCLI(pki.cli.CLI): @@ -108,9 +110,11 @@ def __init__(self): super().__init__('remove', 'Remove ACME subsystem') def print_help(self): - print('Usage: pki-server acme-remove [OPTIONS] [name]') + print('Usage: pki-server acme-remove [OPTIONS]') print() print(' -i, --instance Instance ID (default: pki-tomcat).') + print(' --remove-conf Remove config folder.') + print(' --remove-logs Remove logs folder.') print(' --force Force removal.') print(' -v, --verbose Run in verbose mode.') print(' --debug Run in debug mode.') @@ -120,9 +124,9 @@ def print_help(self): def execute(self, argv): try: - opts, args = getopt.gnu_getopt(argv, 'i:v', [ + opts, _ = getopt.gnu_getopt(argv, 'i:v', [ 'instance=', - 'force', + 'remove-conf', 'remove-logs', 'force', 'verbose', 'debug', 'help']) except getopt.GetoptError as e: @@ -130,14 +134,21 @@ def execute(self, argv): self.print_help() sys.exit(1) - name = 'acme' instance_name = 'pki-tomcat' + remove_conf = False + remove_logs = False force = False for o, a in opts: if o in ('-i', '--instance'): instance_name = a + elif o == '--remove-conf': + remove_conf = True + + elif o == '--remove-logs': + remove_logs = True + elif o == '--force': force = True @@ -156,9 +167,6 @@ def execute(self, argv): self.print_help() sys.exit(1) - if len(args) > 0: - name = args[0] - instance = pki.server.PKIServerFactory.create(instance_name) if not instance.exists(): @@ -166,9 +174,15 @@ def execute(self, argv): instance.load() - acme_conf_dir = os.path.join(instance.conf_dir, name) - logger.info('Removing %s', acme_conf_dir) - pki.util.rmtree(acme_conf_dir, force=force) + subsystem = pki.server.subsystem.ACMESubsystem(instance) + + if remove_logs: + subsystem.remove_logs(force=force) + + if remove_conf: + subsystem.remove_conf(force=force) + + subsystem.remove(force=force) class ACMEDeployCLI(pki.cli.CLI): diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index 604c6f9e848..2396d40c3b7 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -5187,6 +5187,8 @@ def create_acme_subsystem(self): subsystem = pki.server.subsystem.ACMESubsystem(self.instance) subsystem.create() + subsystem.create_conf() + subsystem.create_logs() return subsystem @@ -5371,6 +5373,49 @@ def spawn_acme(self): self.deploy_acme_webapp(subsystem) + def undeploy_acme_webapp(self, subsystem): + ''' + See also pki-server acme-undeploy. + ''' + + logger.info('Undeploying ACME webapp') + + subsystem.disable(wait=True) + + def remove_acme_subsystem(self, subsystem): + ''' + See also pki-server acme-remove. + ''' + + logger.info('Removing ACME subsystem') + + if self.remove_logs: + subsystem.remove_logs(force=self.force) + + if self.remove_conf: + subsystem.remove_conf(force=self.force) + + subsystem.remove(force=self.force) + + def destroy_acme(self): + + subsystem = self.instance.remove_subsystem('acme') + + self.undeploy_acme_webapp(subsystem) + self.remove_acme_subsystem(subsystem) + + if len(self.instance.get_subsystems()) == 0: + # if this is the last subsystem, stop the server + self.instance.stop( + wait=True, + max_wait=self.startup_timeout, + timeout=self.request_timeout) + + # then remove the server + self.instance.remove( + remove_conf=self.remove_conf, + remove_logs=self.remove_logs) + def create_est_subsystem(self): ''' See also pki-server est-create. @@ -5567,6 +5612,10 @@ def destroy(self): print('Uninstalling ' + self.subsystem_type + ' from ' + self.instance.base_dir + '.') + if self.subsystem_type == 'ACME': + self.destroy_acme() + return + scriptlet = pki.server.deployment.scriptlets.initialization.PkiScriptlet() scriptlet.deployer = self scriptlet.instance = self.instance diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index 99801a81870..3deaff89695 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -2768,28 +2768,63 @@ def realm_conf(self): def create(self, exist_ok=False, force=False): - self.instance.makedirs(self.conf_dir, exist_ok=exist_ok) + # Create /var/lib/pki// + self.instance.makedirs(self.base_dir, exist_ok=exist_ok, force=force) + + def create_conf(self, exist_ok=False, force=False): + + # Create /etc/pki// + self.instance.makedirs(self.conf_dir, exist_ok=exist_ok, force=force) + + # Link /var/lib/pki///conf + # to /etc/pki// + + conf_link = os.path.join(self.base_dir, 'conf') + self.instance.symlink( + self.conf_dir, + conf_link, + exist_ok=exist_ok) default_conf_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'conf') + # Copy /usr/share/pki/acme/conf/database.conf + # to /etc/pki///database.conf self.instance.copy( os.path.join(default_conf_dir, 'database.conf'), self.database_conf, exist_ok=exist_ok, force=force) + # Copy /usr/share/pki/acme/conf/issuer.conf + # to /etc/pki///issuer.conf self.instance.copy( os.path.join(default_conf_dir, 'issuer.conf'), self.issuer_conf, exist_ok=exist_ok, force=force) + # Copy /usr/share/pki/acme/conf/realm.conf + # to /etc/pki///realm.conf self.instance.copy( os.path.join(default_conf_dir, 'realm.conf'), self.realm_conf, exist_ok=exist_ok, force=force) + def create_logs(self, exist_ok=False, force=False): + + # Create /var/log/pki// + self.instance.makedirs(self.logs_dir, exist_ok=exist_ok, force=force) + + # Link /var/lib/pki///logs + # to /var/log/pki// + + logs_link = os.path.join(self.base_dir, 'logs') + self.instance.symlink( + self.logs_dir, + logs_link, + exist_ok=exist_ok) + def get_database_config(self, database_type=None): template_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'database') @@ -2856,6 +2891,13 @@ def update_realm_config(self, config): logger.info('Updating %s', self.realm_conf) self.instance.store_properties(self.realm_conf, config) + def save(self): + + # override PKISubsystem.save() since ACME does not use CS.cfg + # and registry.cfg + + pass + class ESTSubsystem(PKISubsystem): @@ -3029,4 +3071,7 @@ def create(cls, instance, name): if name == 'tps': return TPSSubsystem(instance) + if name == 'acme': + return ACMESubsystem(instance) + return PKISubsystem(instance, name)