diff --git a/.github/workflows/acme-basic-test.yml b/.github/workflows/acme-basic-test.yml index 35a823ecbd3..0e6c468d7a8 100644 --- a/.github/workflows/acme-basic-test.yml +++ b/.github/workflows/acme-basic-test.yml @@ -100,17 +100,28 @@ jobs: - name: Install ACME in PKI container run: | - docker exec pki pki-server acme-create - docker exec pki pki-server acme-database-mod \ - --type ds \ - -D url=ldap://ds.example.com:3389 - docker exec pki pki-server acme-issuer-mod \ - --type pki \ - -D url=https://pki.example.com:8443 - docker exec pki pki-server acme-realm-mod \ - --type ds \ - -D url=ldap://ds.example.com:3389 - docker exec pki pki-server acme-deploy --wait + docker exec pki pkispawn \ + -f /usr/share/pki/server/examples/installation/acme.cfg \ + -s ACME \ + -D acme_database_url=ldap://ds.example.com:3389 \ + -D acme_issuer_url=https://pki.example.com:8443 \ + -D acme_realm_url=ldap://ds.example.com:3389 \ + -v + + - name: Check ACME database config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/database.conf + + - name: Check ACME issuer config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/issuer.conf + + - name: Check ACME realm config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/realm.conf - name: Check initial ACME accounts run: | diff --git a/.github/workflows/acme-postgresql-test.yml b/.github/workflows/acme-postgresql-test.yml index cdbdb321298..f00e543f146 100644 --- a/.github/workflows/acme-postgresql-test.yml +++ b/.github/workflows/acme-postgresql-test.yml @@ -149,19 +149,32 @@ jobs: - name: Install ACME in PKI container run: | - docker exec pki pki-server acme-create - docker exec pki pki-server acme-database-mod \ - --type postgresql \ - -Dpassword=mysecretpassword \ - -Durl='jdbc:postgresql://postgresql.example.com:5432/acme?ssl=true&sslmode=require' - docker exec pki pki-server acme-issuer-mod \ - --type pki \ - -D url=https://pki.example.com:8443 - docker exec pki pki-server acme-realm-mod \ - --type postgresql \ - -Dpassword=mysecretpassword \ - -Durl='jdbc:postgresql://postgresql.example.com:5432/acme?ssl=true&sslmode=require' - docker exec pki pki-server acme-deploy --wait + docker exec pki pkispawn \ + -f /usr/share/pki/server/examples/installation/acme.cfg \ + -s ACME \ + -D acme_database_type=postgresql \ + -D acme_database_url="jdbc:postgresql://postgresql.example.com:5432/acme?ssl=true&sslmode=require" \ + -D acme_database_password=mysecretpassword \ + -D acme_issuer_url=https://pki.example.com:8443 \ + -D acme_realm_type=postgresql \ + -D acme_realm_url="jdbc:postgresql://postgresql.example.com:5432/acme?ssl=true&sslmode=require" \ + -D acme_realm_password=mysecretpassword \ + -v + + - name: Check ACME database config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/database.conf + + - name: Check ACME issuer config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/issuer.conf + + - name: Check ACME realm config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/acme/realm.conf - name: Run PKI healthcheck in PKI container run: docker exec pki pki-healthcheck --failures-only diff --git a/.github/workflows/acme-switchover-test.yml b/.github/workflows/acme-switchover-test.yml index bd9a81eba65..f7fbcb5848a 100644 --- a/.github/workflows/acme-switchover-test.yml +++ b/.github/workflows/acme-switchover-test.yml @@ -80,13 +80,16 @@ jobs: docker exec pki pki-server acme-create docker exec pki pki-server acme-database-mod \ --type ds \ - -D url=ldap://ds.example.com:3389 + -D url=ldap://ds.example.com:3389 \ + -D bindPassword=Secret.123 docker exec pki pki-server acme-issuer-mod \ --type pki \ - -D url=https://pki.example.com:8443 + -D url=https://pki.example.com:8443 \ + -D password=Secret.123 docker exec pki pki-server acme-realm-mod \ --type ds \ - -D url=ldap://ds.example.com:3389 + -D url=ldap://ds.example.com:3389 \ + -D bindPassword=Secret.123 docker exec pki bash -c "echo baseURL=http://server1.example.com:8080/acme >> /var/lib/pki/pki-tomcat/conf/acme/engine.conf" docker exec pki pki-server acme-deploy --wait diff --git a/base/acme/database/ds/database.conf b/base/acme/database/ds/database.conf index a43f4e0b430..802ae50cc11 100644 --- a/base/acme/database/ds/database.conf +++ b/base/acme/database/ds/database.conf @@ -2,5 +2,5 @@ class=org.dogtagpki.acme.database.DSDatabase url=ldap://localhost.localdomain:389 authType=BasicAuth bindDN=cn=Directory Manager -bindPassword=Secret.123 +bindPassword= baseDN=dc=acme,dc=pki,dc=example,dc=com diff --git a/base/acme/database/ldap/database.conf b/base/acme/database/ldap/database.conf index a30be64ef95..8611885867d 100644 --- a/base/acme/database/ldap/database.conf +++ b/base/acme/database/ldap/database.conf @@ -2,5 +2,5 @@ class=org.dogtagpki.acme.database.LDAPDatabase url=ldap://localhost.localdomain:389 authType=BasicAuth bindDN=cn=Directory Manager -bindPassword=Secret.123 +bindPassword= baseDN=dc=acme,dc=pki,dc=example,dc=com diff --git a/base/acme/database/openldap/database.conf b/base/acme/database/openldap/database.conf index ee589f12125..1c0c7370c53 100644 --- a/base/acme/database/openldap/database.conf +++ b/base/acme/database/openldap/database.conf @@ -2,5 +2,5 @@ class=org.dogtagpki.acme.database.OpenLDAPDatabase url=ldap://localhost.localdomain:389 authType=BasicAuth bindDN=cn=Manager,dc=example,dc=com -bindPassword=Secret.123 +bindPassword= baseDN=dc=acme,dc=pki,dc=example,dc=com diff --git a/base/acme/database/postgresql/database.conf b/base/acme/database/postgresql/database.conf index 16a00cd1be8..927927d9b79 100644 --- a/base/acme/database/postgresql/database.conf +++ b/base/acme/database/postgresql/database.conf @@ -1,4 +1,4 @@ class=org.dogtagpki.acme.database.PostgreSQLDatabase url=jdbc:postgresql://localhost.localdomain:5432/acme user=acme -password=Secret.123 +password= diff --git a/base/acme/issuer/pki/issuer.conf b/base/acme/issuer/pki/issuer.conf index 233d12ca897..26a3c60ef74 100644 --- a/base/acme/issuer/pki/issuer.conf +++ b/base/acme/issuer/pki/issuer.conf @@ -2,4 +2,4 @@ class=org.dogtagpki.acme.issuer.PKIIssuer url=https://localhost.localdomain:8443 profile=acmeServerCert username=caadmin -password=Secret.123 +password= diff --git a/base/acme/openshift/pki-acme-database.yaml b/base/acme/openshift/pki-acme-database.yaml index a182af69f69..6dd559cd369 100644 --- a/base/acme/openshift/pki-acme-database.yaml +++ b/base/acme/openshift/pki-acme-database.yaml @@ -14,7 +14,7 @@ stringData: # url: ldap://ds:389 # authType: BasicAuth # bindDN: cn=Directory Manager - # bindPassword: Secret.123 + # bindPassword: ... # baseDN: dc=acme,dc=pki,dc=example,dc=com # # OpenLDAP Database @@ -23,12 +23,12 @@ stringData: # url: ldap://openldap:389 # authType: BasicAuth # bindDN: cn=Manager,dc=example,dc=com - # bindPassword: Secret.123 + # bindPassword: ... # baseDN: dc=acme,dc=pki,dc=example,dc=com # # PostgreSQL Database # ------------------- # class: org.dogtagpki.acme.database.PostgreSQLDatabase - # password: Secret.123 + # password: ... # url: jdbc:postgresql://postgresql:5432/acme # user: acme diff --git a/base/acme/openshift/pki-acme-issuer.yaml b/base/acme/openshift/pki-acme-issuer.yaml index 138c5a9cb99..7961bb810ef 100644 --- a/base/acme/openshift/pki-acme-issuer.yaml +++ b/base/acme/openshift/pki-acme-issuer.yaml @@ -14,4 +14,4 @@ stringData: # url: https://pki-ca:8443 # profile: acmeServerCert # username: caadmin - # password: Secret.123 + # password: ... diff --git a/base/acme/openshift/pki-acme-realm.yaml b/base/acme/openshift/pki-acme-realm.yaml index 9199710f394..4adc7c73784 100644 --- a/base/acme/openshift/pki-acme-realm.yaml +++ b/base/acme/openshift/pki-acme-realm.yaml @@ -14,13 +14,13 @@ stringData: # url: ldap://ds:389 # authType: BasicAuth # bindDN: cn=Directory Manager - # bindPassword: Secret.123 + # bindPassword: ... # usersDN: ou=people,dc=acme,dc=pki,dc=example,dc=com # groupsDN: ou=groups,dc=acme,dc=pki,dc=example,dc=com # # PostgreSQL Realm # ---------------- # class: org.dogtagpki.acme.realm.PostgreSQLRealm - # password: Secret.123 + # password: ... # url: jdbc:postgresql://postgresql:5432/acme # user: acme diff --git a/base/acme/realm/ds/realm.conf b/base/acme/realm/ds/realm.conf index c2c5e484a18..eaf305b9b51 100644 --- a/base/acme/realm/ds/realm.conf +++ b/base/acme/realm/ds/realm.conf @@ -2,6 +2,6 @@ class=org.dogtagpki.acme.realm.DSRealm url=ldap://localhost.localdomain:389 authType=BasicAuth bindDN=cn=Directory Manager -bindPassword=Secret.123 +bindPassword= usersDN=ou=people,dc=acme,dc=pki,dc=example,dc=com groupsDN=ou=groups,dc=acme,dc=pki,dc=example,dc=com diff --git a/base/acme/realm/in-memory/realm.conf b/base/acme/realm/in-memory/realm.conf index 6dc4a01dbf0..12bfa76d251 100644 --- a/base/acme/realm/in-memory/realm.conf +++ b/base/acme/realm/in-memory/realm.conf @@ -1,3 +1,3 @@ class=org.dogtagpki.acme.realm.InMemoryRealm username=admin -password=Secret.123 +password= diff --git a/base/acme/realm/postgresql/realm.conf b/base/acme/realm/postgresql/realm.conf index 0e37e6ca576..0714c9d5811 100644 --- a/base/acme/realm/postgresql/realm.conf +++ b/base/acme/realm/postgresql/realm.conf @@ -1,4 +1,4 @@ class=org.dogtagpki.acme.realm.PostgreSQLRealm url=jdbc:postgresql://localhost.localdomain:5432/acme user=acme -password=Secret.123 +password= diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index c806aa714cf..642fb019f19 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -33,6 +33,11 @@ sensitive_parameters= pki_server_database_password pki_server_pkcs12_password pki_token_password + acme_database_bind_password + acme_database_password + acme_issuer_password + acme_realm_bind_password + acme_realm_password pki_instance_name=pki-tomcat pki_http_port=8080 @@ -80,7 +85,6 @@ pki_ds_ldap_port=389 pki_ds_ldaps_port=636 pki_ds_password= pki_ds_remove_data=True -pki_ds_setup=True pki_ds_secure_connection=False pki_ds_secure_connection_ca_nickname=Directory Server CA certificate pki_ds_secure_connection_ca_pem_file= @@ -94,13 +98,14 @@ pki_issuing_ca_uri=https://%(pki_issuing_ca_hostname)s:%(pki_issuing_ca_https_po pki_issuing_ca=%(pki_issuing_ca_uri)s pki_replication_password= pki_status_request_timeout= -pki_security_domain_setup=True + pki_security_domain_hostname=%(pki_hostname)s pki_security_domain_https_port=8443 pki_security_domain_uri=https://%(pki_security_domain_hostname)s:%(pki_security_domain_https_port)s pki_security_domain_name=%(pki_dns_domainname)s Security Domain pki_security_domain_password= pki_security_domain_user=caadmin + #for supporting server cert SAN injection pki_san_inject=False pki_san_for_server_cert= @@ -247,8 +252,6 @@ CATALINA_HOME=/usr/share/tomcat pki_tomcat_bin_path=%(CATALINA_HOME)s/bin pki_tomcat_lib_path=%(CATALINA_HOME)s/lib -pki_registry_enable=True - ############################################################################### ## CA Configuration: ## ## ## @@ -329,9 +332,11 @@ pki_admin_uid=caadmin pki_audit_signing_nickname=auditSigningCert cert-%(pki_instance_name)s CA pki_audit_signing_subject_dn=cn=CA Audit Signing Certificate,ou=%(pki_instance_name)s,o=%(pki_security_domain_name)s +pki_ds_setup=True pki_ds_base_dn=o=%(pki_instance_name)s-CA pki_ds_database=%(pki_instance_name)s-CA pki_ds_hostname=%(pki_hostname)s + pki_subsystem_name=CA %(pki_hostname)s %(pki_https_port)s pki_share_db=False pki_master_crl_enable=True @@ -360,6 +365,9 @@ pki_request_id_generator=random # Cert request ID length in bits pki_request_id_length=128 +pki_security_domain_setup=True +pki_registry_enable=True + ############################################################################### ## KRA Configuration: ## ## ## @@ -441,9 +449,11 @@ pki_admin_uid=kraadmin pki_audit_signing_nickname=auditSigningCert cert-%(pki_instance_name)s KRA pki_audit_signing_subject_dn=cn=KRA Audit Signing Certificate,ou=%(pki_instance_name)s,o=%(pki_security_domain_name)s +pki_ds_setup=True pki_ds_base_dn=o=%(pki_instance_name)s-KRA pki_ds_database=%(pki_instance_name)s-KRA pki_ds_hostname=%(pki_hostname)s + pki_subsystem_name=KRA %(pki_hostname)s %(pki_https_port)s pki_share_db=True pki_share_dbuser_dn=uid=pkidbuser,ou=people,%(pki_ds_base_dn)s @@ -460,6 +470,9 @@ pki_request_id_generator=random # Key request ID length in bits pki_request_id_length=128 +pki_security_domain_setup=True +pki_registry_enable=True + ############################################################################### ## OCSP Configuration: ## ## ## @@ -528,13 +541,17 @@ pki_admin_uid=ocspadmin pki_audit_signing_nickname=auditSigningCert cert-%(pki_instance_name)s OCSP pki_audit_signing_subject_dn=cn=OCSP Audit Signing Certificate,ou=%(pki_instance_name)s,o=%(pki_security_domain_name)s +pki_ds_setup=True pki_ds_base_dn=o=%(pki_instance_name)s-OCSP pki_ds_database=%(pki_instance_name)s-OCSP pki_ds_hostname=%(pki_hostname)s + pki_subsystem_name=OCSP %(pki_hostname)s %(pki_https_port)s pki_share_db=True pki_share_dbuser_dn=uid=pkidbuser,ou=people,%(pki_ds_base_dn)s +pki_security_domain_setup=True +pki_registry_enable=True ############################################################################### ## TKS Configuration: ## @@ -552,13 +569,19 @@ pki_admin_subject_dn=cn=PKI Administrator,e=%(pki_admin_email)s,ou=%(pki_instanc pki_admin_uid=tksadmin pki_audit_signing_nickname=auditSigningCert cert-%(pki_instance_name)s TKS pki_audit_signing_subject_dn=cn=TKS Audit Signing Certificate,ou=%(pki_instance_name)s,o=%(pki_security_domain_name)s + +pki_ds_setup=True pki_ds_base_dn=o=%(pki_instance_name)s-TKS pki_ds_database=%(pki_instance_name)s-TKS pki_ds_hostname=%(pki_hostname)s + pki_subsystem_name=TKS %(pki_hostname)s %(pki_https_port)s pki_share_db=True pki_share_dbuser_dn=uid=pkidbuser,ou=people,%(pki_ds_base_dn)s +pki_security_domain_setup=True +pki_registry_enable=True + ############################################################################### ## TPS Configuration: ## ## ## @@ -574,9 +597,12 @@ pki_admin_subject_dn=cn=PKI Administrator,e=%(pki_admin_email)s,ou=%(pki_instanc pki_admin_uid=tpsadmin pki_audit_signing_nickname=auditSigningCert cert-%(pki_instance_name)s TPS pki_audit_signing_subject_dn=cn=TPS Audit Signing Certificate,ou=%(pki_instance_name)s,o=%(pki_security_domain_name)s + +pki_ds_setup=True pki_ds_base_dn=o=%(pki_instance_name)s-TPS pki_ds_database=%(pki_instance_name)s-TPS pki_ds_hostname=%(pki_hostname)s + pki_subsystem_name=TPS %(pki_hostname)s %(pki_https_port)s pki_authdb_hostname=%(pki_hostname)s pki_authdb_port=389 @@ -589,3 +615,51 @@ pki_import_shared_secret=False pki_share_db=True pki_share_dbuser_dn=uid=pkidbuser,ou=people,%(pki_ds_base_dn)s pki_source_phone_home_xml=/usr/share/pki/%(pki_subsystem_type)s/conf/phoneHome.xml + +pki_security_domain_setup=True +pki_registry_enable=True + +[ACME] +pki_ds_setup=False +pki_security_domain_setup=False +pki_registry_enable=False + +# Database params: +# - acme_database_type +# - acme_database_url +# - acme_database_auth_type +# - acme_database_bind_dn +# - acme_database_bind_password +# - acme_database_bind_nickname +# - acme_database_user +# - acme_database_password +# - acme_database_base_dn +# +# See /usr/share/pki/acme/database//database.conf + +# Issuer params: +# - acme_issuer_type +# - acme_issuer_url +# - acme_issuer_nickname +# - acme_issuer_extensions +# - acme_issuer_username +# - acme_issuer_password +# - acme_issuer_password_file +# - acme_issuer_profile +# +# See /usr/share/pki/acme/issuer//issuer.conf + +# Realm params: +# - acme_realm_type +# - acme_realm_url +# - acme_realm_auth_type +# - acme_realm_bind_dn +# - acme_realm_bind_password +# - acme_realm_nickname +# - acme_realm_user +# - acme_realm_username +# - acme_realm_password +# - acme_realm_users_dn +# - acme_realm_groups_dn +# +# See /usr/share/pki/acme/realm//realm.conf diff --git a/base/server/examples/installation/acme.cfg b/base/server/examples/installation/acme.cfg new file mode 100644 index 00000000000..3426740113d --- /dev/null +++ b/base/server/examples/installation/acme.cfg @@ -0,0 +1,15 @@ +[DEFAULT] +pki_server_database_password=Secret.123 + +[ACME] +acme_database_type=ds +acme_database_url=ldap://localhost.localdomain:3389 +acme_database_bind_password=Secret.123 + +acme_issuer_type=pki +acme_issuer_url=https://localhost.localdomain:8443 +acme_issuer_password=Secret.123 + +acme_realm_type=ds +acme_realm_url=ldap://localhost.localdomain:3389 +acme_realm_bind_password=Secret.123 diff --git a/base/server/python/pki/server/cli/acme.py b/base/server/python/pki/server/cli/acme.py index a082df73e3f..07a9202d7ed 100644 --- a/base/server/python/pki/server/cli/acme.py +++ b/base/server/python/pki/server/cli/acme.py @@ -17,34 +17,6 @@ import pki.server import pki.server.cli.subsystem -# TODO: auto-populate this map from /usr/share/pki/acme/database -DATABASE_CLASSES = { - 'ds': 'org.dogtagpki.acme.database.DSDatabase', - 'in-memory': 'org.dogtagpki.acme.database.InMemoryDatabase', - 'ldap': 'org.dogtagpki.acme.database.LDAPDatabase', - 'openldap': 'org.dogtagpki.acme.database.OpenLDAPDatabase', - 'postgresql': 'org.dogtagpki.acme.database.PostgreSQLDatabase' -} - -DATABASE_TYPES = {value: key for key, value in DATABASE_CLASSES.items()} - -# TODO: auto-populate this map from /usr/share/pki/acme/issuer -ISSUER_CLASSES = { - 'nss': 'org.dogtagpki.acme.issuer.NSSIssuer', - 'pki': 'org.dogtagpki.acme.issuer.PKIIssuer' -} - -ISSUER_TYPES = {value: key for key, value in ISSUER_CLASSES.items()} - -# TODO: auto-populate this map from /usr/share/pki/acme/realm -REALM_CLASSES = { - 'ds': 'org.dogtagpki.acme.realm.DSRealm', - 'in-memory': 'org.dogtagpki.acme.realm.InMemoryRealm', - 'postgresql': 'org.dogtagpki.acme.realm.PostgreSQLRealm' -} - -REALM_TYPES = {value: key for key, value in REALM_CLASSES.items()} - logger = logging.getLogger(__name__) @@ -72,7 +44,7 @@ def __init__(self): super().__init__('create', 'Create ACME subsystem') def print_help(self): - print('Usage: pki-server acme-create [OPTIONS] [name]') + print('Usage: pki-server acme-create [OPTIONS]') print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' --force Force creation.') @@ -84,7 +56,7 @@ 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=', 'database=', 'issuer=', 'force', 'verbose', 'debug', 'help']) @@ -94,7 +66,6 @@ def execute(self, argv): self.print_help() sys.exit(1) - name = 'acme' instance_name = 'pki-tomcat' force = False @@ -120,9 +91,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(): @@ -130,34 +98,8 @@ def execute(self, argv): instance.load() - acme_conf_dir = os.path.join(instance.conf_dir, name) - instance.makedirs(acme_conf_dir, exist_ok=True) - - acme_share_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme') - - database_template = os.path.join(acme_share_dir, 'conf', 'database.conf') - database_conf = os.path.join(acme_conf_dir, 'database.conf') - instance.copy( - database_template, - database_conf, - exist_ok=True, - force=force) - - issuer_template = os.path.join(acme_share_dir, 'conf', 'issuer.conf') - issuer_conf = os.path.join(acme_conf_dir, 'issuer.conf') - instance.copy( - issuer_template, - issuer_conf, - exist_ok=True, - force=force) - - realm_template = os.path.join(acme_share_dir, 'conf', 'realm.conf') - realm_conf = os.path.join(acme_conf_dir, 'realm.conf') - instance.copy( - realm_template, - realm_conf, - exist_ok=True, - force=force) + subsystem = pki.server.subsystem.ACMESubsystem(instance) + subsystem.create(force=force) class ACMERemoveCLI(pki.cli.CLI): @@ -661,7 +603,7 @@ def execute(self, argv): database_class = config.get('class') - database_type = DATABASE_TYPES.get(database_class) + database_type = pki.server.subsystem.ACME_DATABASE_TYPES.get(database_class) print(' Database Type: %s' % database_type) if database_type in ['ds', 'ldap', 'openldap']: @@ -724,7 +666,7 @@ def print_help(self): print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' --type Database type: {0}' - .format(', '.join(DATABASE_TYPES.values()))) + .format(', '.join(pki.server.subsystem.ACME_DATABASE_TYPES.values()))) print(' -D= Set property value.') print(' -v, --verbose Run in verbose mode.') print(' --debug Run in debug mode.') @@ -753,7 +695,7 @@ def execute(self, argv): elif o == '--type': database_type = a - if database_type not in DATABASE_TYPES.values(): + if database_type not in pki.server.subsystem.ACME_DATABASE_TYPES.values(): raise Exception('Invalid database type: {0}'.format(database_type)) elif o == '-D': @@ -784,19 +726,8 @@ def execute(self, argv): instance.load() - acme_conf_dir = os.path.join(instance.conf_dir, 'acme') - database_conf = os.path.join(acme_conf_dir, 'database.conf') - config = {} - - if database_type: - # if --type is specified, load the database.conf template - source = '/usr/share/pki/acme/database/{0}/database.conf'.format(database_type) - else: - # otherwise, load the database.conf from the instance - source = database_conf - - logger.info('Loading %s', source) - pki.util.load_properties(source, config) + subsystem = pki.server.subsystem.ACMESubsystem(instance) + config = subsystem.get_database_config(database_type=database_type) # if --type or -D is specified, use silent mode if database_type or props: @@ -806,7 +737,7 @@ def execute(self, argv): logger.info('- %s: %s', name, value) pki.util.set_property(config, name, value) - instance.store_properties(database_conf, config) + subsystem.update_database_config(config) return # otherwise, use interactive mode @@ -821,16 +752,19 @@ def execute(self, argv): print() print( 'Enter the type of the database. ' - 'Available types: %s.' % ', '.join(DATABASE_TYPES.values())) - database_type = DATABASE_TYPES.get(database_class) + 'Available types: %s.' % ', '.join(pki.server.subsystem.ACME_DATABASE_TYPES.values())) + database_type = pki.server.subsystem.ACME_DATABASE_TYPES.get(database_class) orig_database_type = database_type database_type = pki.util.read_text( ' Database Type', - options=DATABASE_TYPES.values(), + options=pki.server.subsystem.ACME_DATABASE_TYPES.values(), default=database_type, required=True) - pki.util.set_property(config, 'class', DATABASE_CLASSES.get(database_type)) + pki.util.set_property( + config, + 'class', + pki.server.subsystem.ACME_DATABASE_CLASSES.get(database_type)) if orig_database_type != database_type: source = '/usr/share/pki/acme/database/{0}/database.conf'.format(database_type) @@ -922,7 +856,7 @@ def execute(self, argv): ' Password', default=password, password=True, required=True) pki.util.set_property(config, 'password', password) - instance.store_properties(database_conf, config) + subsystem.update_database_config(config) class ACMEIssuerCLI(pki.cli.CLI): @@ -997,7 +931,7 @@ def execute(self, argv): issuer_class = config.get('class') - issuer_type = ISSUER_TYPES.get(issuer_class) + issuer_type = pki.server.subsystem.ACME_ISSUER_TYPES.get(issuer_class) print(' Issuer Type: %s' % issuer_type) if issuer_type == 'nss': @@ -1047,7 +981,7 @@ def print_help(self): print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' --type Issuer type: {0}' - .format(', '.join(ISSUER_TYPES.values()))) + .format(', '.join(pki.server.subsystem.ACME_ISSUER_TYPES.values()))) print(' -D= Set property value.') print(' -v, --verbose Run in verbose mode.') print(' --debug Run in debug mode.') @@ -1076,7 +1010,7 @@ def execute(self, argv): elif o == '--type': issuer_type = a - if issuer_type not in ISSUER_TYPES.values(): + if issuer_type not in pki.server.subsystem.ACME_ISSUER_TYPES.values(): raise Exception('Invalid issuer type: {0}'.format(issuer_type)) elif o == '-D': @@ -1107,19 +1041,8 @@ def execute(self, argv): instance.load() - acme_conf_dir = os.path.join(instance.conf_dir, 'acme') - issuer_conf = os.path.join(acme_conf_dir, 'issuer.conf') - config = {} - - if issuer_type: - # if --type is specified, load the issuer.conf template - source = '/usr/share/pki/acme/issuer/{0}/issuer.conf'.format(issuer_type) - else: - # otherwise, load the issuer.conf from the instance - source = issuer_conf - - logger.info('Loading %s', source) - pki.util.load_properties(source, config) + subsystem = pki.server.subsystem.ACMESubsystem(instance) + config = subsystem.get_issuer_config(issuer_type=issuer_type) # if --type or -D is specified, use silent mode if issuer_type or props: @@ -1129,7 +1052,7 @@ def execute(self, argv): logger.info('- %s: %s', name, value) pki.util.set_property(config, name, value) - instance.store_properties(issuer_conf, config) + subsystem.update_issuer_config(config) return # otherwise, use interactive mode @@ -1144,16 +1067,19 @@ def execute(self, argv): print() print( 'Enter the type of the certificate issuer. ' - 'Available types: %s.' % ', '.join(ISSUER_TYPES.values())) - issuer_type = ISSUER_TYPES.get(issuer_class) + 'Available types: %s.' % ', '.join(pki.server.subsystem.ACME_ISSUER_TYPES.values())) + issuer_type = pki.server.subsystem.ACME_ISSUER_TYPES.get(issuer_class) orig_issuer_type = issuer_type issuer_type = pki.util.read_text( ' Issuer Type', - options=ISSUER_TYPES.values(), + options=pki.server.subsystem.ACME_ISSUER_TYPES.values(), default=issuer_type, required=True) - pki.util.set_property(config, 'class', ISSUER_CLASSES.get(issuer_type)) + pki.util.set_property( + config, + 'class', + pki.server.subsystem.ACME_ISSUER_CLASSES.get(issuer_type)) if orig_issuer_type != issuer_type: source = '/usr/share/pki/acme/issuer/{0}/issuer.conf'.format(issuer_type) @@ -1223,7 +1149,7 @@ def execute(self, argv): profile = pki.util.read_text(' Certificate Profile', default=profile, required=True) pki.util.set_property(config, 'profile', profile) - instance.store_properties(issuer_conf, config) + subsystem.update_issuer_config(config) class ACMERealmCLI(pki.cli.CLI): @@ -1298,7 +1224,7 @@ def execute(self, argv): realm_class = config.get('class') - realm_type = REALM_TYPES.get(realm_class) + realm_type = pki.server.subsystem.ACME_REALM_TYPES.get(realm_class) print(' Realm Type: %s' % realm_type) if realm_type == 'in-memory': @@ -1371,7 +1297,7 @@ def print_help(self): print() print(' -i, --instance Instance ID (default: pki-tomcat).') print(' --type Realm type: {0}' - .format(', '.join(REALM_TYPES.values()))) + .format(', '.join(pki.server.subsystem.ACME_REALM_TYPES.values()))) print(' -D= Set property value.') print(' -v, --verbose Run in verbose mode.') print(' --debug Run in debug mode.') @@ -1400,7 +1326,7 @@ def execute(self, argv): elif o == '--type': realm_type = a - if realm_type not in REALM_TYPES.values(): + if realm_type not in pki.server.subsystem.ACME_REALM_TYPES.values(): raise Exception('Invalid realm type: {0}'.format(realm_type)) elif o == '-D': @@ -1431,19 +1357,8 @@ def execute(self, argv): instance.load() - acme_conf_dir = os.path.join(instance.conf_dir, 'acme') - realm_conf = os.path.join(acme_conf_dir, 'realm.conf') - config = {} - - if realm_type: - # if --type is specified, load the realm.conf template - source = '/usr/share/pki/acme/realm/{0}/realm.conf'.format(realm_type) - else: - # otherwise, load the realm.conf from the instance - source = realm_conf - - logger.info('Loading %s', source) - pki.util.load_properties(source, config) + subsystem = pki.server.subsystem.ACMESubsystem(instance) + config = subsystem.get_realm_config(realm_type=realm_type) # if --type or -D is specified, use silent mode if realm_type or props: @@ -1453,7 +1368,7 @@ def execute(self, argv): logger.info('- %s: %s', name, value) pki.util.set_property(config, name, value) - instance.store_properties(realm_conf, config) + subsystem.update_realm_config(config) return # otherwise, use interactive mode @@ -1468,16 +1383,19 @@ def execute(self, argv): print() print( 'Enter the type of the realm. ' - 'Available types: %s.' % ', '.join(REALM_TYPES.values())) - realm_type = REALM_TYPES.get(realm_class) + 'Available types: %s.' % ', '.join(pki.server.subsystem.ACME_REALM_TYPES.values())) + realm_type = pki.server.subsystem.ACME_REALM_TYPES.get(realm_class) orig_realm_type = realm_type realm_type = pki.util.read_text( ' Realm Type', - options=REALM_TYPES.values(), + options=pki.server.subsystem.ACME_REALM_TYPES.values(), default=realm_type, required=True) - pki.util.set_property(config, 'class', REALM_CLASSES.get(realm_type)) + pki.util.set_property( + config, + 'class', + pki.server.subsystem.ACME_REALM_CLASSES.get(realm_type)) if orig_realm_type != realm_type: source = '/usr/share/pki/acme/realm/{0}/realm.conf'.format(realm_type) @@ -1586,4 +1504,4 @@ def execute(self, argv): ' Password', default=password, password=True, required=True) pki.util.set_property(config, 'password', password) - instance.store_properties(realm_conf, config) + subsystem.update_realm_config(config) diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index 615904f5583..15690956b49 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -5183,10 +5183,198 @@ def remove_selinux_contexts(self): trans.finish() + def create_acme_subsystem(self): + ''' + See also pki-server acme-create. + ''' + + logger.info('Creating ACME subsystem') + + subsystem = pki.server.subsystem.ACMESubsystem(self.instance) + subsystem.create() + + return subsystem + + def configure_acme_database(self, subsystem): + ''' + See also pki-server acme-database-mod. + ''' + + logger.info('Configuring ACME database') + + database_type = self.mdict['acme_database_type'] + props = subsystem.get_database_config(database_type=database_type) + + database_class = pki.server.subsystem.ACME_DATABASE_CLASSES.get(database_type) + pki.util.set_property(props, 'class', database_class) + + if database_type in ['ds', 'ldap', 'openldap']: + + url = self.mdict.get('acme_database_url') + pki.util.set_property(props, 'url', url) + + auth_type = props.get('authType') + auth_type = self.mdict.get('acme_database_auth_type', auth_type) + pki.util.set_property(props, 'authType', auth_type) + + if auth_type == 'BasicAuth': + bind_dn = self.mdict.get('acme_database_bind_dn') + pki.util.set_property(props, 'bindDN', bind_dn) + + bind_password = self.mdict.get('acme_database_bind_password') + pki.util.set_property(props, 'bindPassword', bind_password) + + elif auth_type == 'SslClientAuth': + nickname = self.mdict.get('acme_database_nickname') + pki.util.set_property(props, 'nickname', nickname) + + base_dn = self.mdict.get('acme_database_base_dn') + pki.util.set_property(props, 'baseDN', base_dn) + + elif database_type == 'postgresql': + + url = self.mdict.get('acme_database_url') + pki.util.set_property(props, 'url', url) + + user = self.mdict.get('acme_database_user') + pki.util.set_property(props, 'user', user) + + password = self.mdict.get('acme_database_password') + pki.util.set_property(props, 'password', password) + + subsystem.update_database_config(props) + + def configure_acme_issuer(self, subsystem): + ''' + See also pki-server acme-issuer-mod. + ''' + + logger.info('Configuring ACME issuer') + + issuer_type = self.mdict['acme_issuer_type'] + props = subsystem.get_issuer_config(issuer_type=issuer_type) + + issuer_class = pki.server.subsystem.ACME_ISSUER_CLASSES.get(issuer_type) + pki.util.set_property(props, 'class', issuer_class) + + if issuer_type == 'nss': + + nickname = self.mdict.get('acme_issuer_nickname') + pki.util.set_property(props, 'nickname', nickname) + + extensions = self.mdict.get('acme_issuer_extensions') + pki.util.set_property(props, 'extensions', extensions) + + elif issuer_type == 'pki': + + url = self.mdict.get('acme_issuer_url') + pki.util.set_property(props, 'url', url) + + nickname = self.mdict.get('acme_issuer_nickname') + pki.util.set_property(props, 'nickname', nickname) + + username = self.mdict.get('acme_issuer_username') + pki.util.set_property(props, 'username', username) + + password = self.mdict.get('acme_issuer_password') + pki.util.set_property(props, 'password', password) + + password_file = self.mdict.get('acme_issuer_password_file') + pki.util.set_property(props, 'passwordFile', password_file) + + profile = self.mdict.get('acme_issuer_profile') + pki.util.set_property(props, 'profile', profile) + + subsystem.update_issuer_config(props) + + def configure_acme_realm(self, subsystem): + ''' + See also pki-server acme-realm-mod. + ''' + + logger.info('Configuring ACME realm') + + realm_type = self.mdict['acme_realm_type'] + props = subsystem.get_realm_config(realm_type=realm_type) + + realm_class = pki.server.subsystem.ACME_REALM_CLASSES.get(realm_type) + pki.util.set_property(props, 'class', realm_class) + + if realm_type == 'in-memory': + + username = self.mdict.get('acme_realm_username') + pki.util.set_property(props, 'username', username) + + password = self.mdict.get('acme_realm_password') + pki.util.set_property(props, 'password', password) + + elif realm_type == 'ds': + + url = self.mdict.get('acme_realm_url') + pki.util.set_property(props, 'url', url) + + auth_type = props.get('authType') + auth_type = self.mdict.get('acme_realm_auth_type', auth_type) + pki.util.set_property(props, 'authType', auth_type) + + if auth_type == 'BasicAuth': + bind_dn = self.mdict.get('acme_realm_bind_dn') + pki.util.set_property(props, 'bindDN', bind_dn) + + bind_password = self.mdict.get('acme_realm_bind_password') + pki.util.set_property(props, 'bindPassword', bind_password) + + elif auth_type == 'SslClientAuth': + nickname = self.mdict.get('acme_realm_nickname') + pki.util.set_property(props, 'nickname', nickname) + + users_dn = self.mdict.get('acme_realm_users_dn') + pki.util.set_property(props, 'usersDN', users_dn) + + groups_dn = self.mdict.get('acme_realm_groups_dn') + pki.util.set_property(props, 'groupsDN', groups_dn) + + elif realm_type == 'postgresql': + + url = self.mdict.get('acme_realm_url') + pki.util.set_property(props, 'url', url) + + user = self.mdict.get('acme_realm_user') + pki.util.set_property(props, 'user', user) + + password = self.mdict.get('acme_realm_password') + pki.util.set_property(props, 'password', password) + + subsystem.update_realm_config(props) + + def deploy_acme_webapp(self, subsystem): + ''' + See also pki-server acme-deploy. + ''' + + logger.info('Deploying ACME webapp') + + subsystem.enable(wait=True) + + def spawn_acme(self): + + subsystem = self.create_acme_subsystem() + self.instance.add_subsystem(subsystem) + + self.configure_acme_database(subsystem) + self.configure_acme_issuer(subsystem) + self.configure_acme_realm(subsystem) + + self.deploy_acme_webapp(subsystem) + def spawn(self): print('Installing ' + self.subsystem_type + ' into ' + self.instance.base_dir + '.') + if self.subsystem_type == 'ACME': + self.spawn_acme() + return + scriptlet = pki.server.deployment.scriptlets.initialization.PkiScriptlet() scriptlet.deployer = self scriptlet.instance = self.instance diff --git a/base/server/python/pki/server/deployment/pkiconfig.py b/base/server/python/pki/server/deployment/pkiconfig.py index 34f2cb6a339..5793401436b 100644 --- a/base/server/python/pki/server/deployment/pkiconfig.py +++ b/base/server/python/pki/server/deployment/pkiconfig.py @@ -36,7 +36,7 @@ PKI_DEPLOYMENT_DEFAULT_UID = 17 PKI_DEPLOYMENT_DEFAULT_USER = "pkiuser" -PKI_SUBSYSTEMS = ["CA", "KRA", "OCSP", "TKS", "TPS"] +PKI_SUBSYSTEMS = ['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME'] PKI_BASE_RESERVED_NAMES = ["alias", "bin", "ca", "common", "conf", "kra", "lib", "logs", "ocsp", "temp", "tks", "tps", "webapps", "work"] diff --git a/base/server/python/pki/server/deployment/pkimessages.py b/base/server/python/pki/server/deployment/pkimessages.py index dec464b5ef2..b327c845dab 100644 --- a/base/server/python/pki/server/deployment/pkimessages.py +++ b/base/server/python/pki/server/deployment/pkimessages.py @@ -349,7 +349,7 @@ PKI_CHECK_STATUS_MESSAGE = ''' To check the status of the subsystem: systemctl status pki-tomcatd@%s.service''' -PKI_ACCESS_URL = ''' +PKI_ACCESS_URL = '''\ The URL for the subsystem is: https://%s:%s/%s''' PKI_INSTANCE_RESTART_MESSAGE = ''' diff --git a/base/server/python/pki/server/pkispawn.py b/base/server/python/pki/server/pkispawn.py index 44971cbb3b0..43c76c38d52 100644 --- a/base/server/python/pki/server/pkispawn.py +++ b/base/server/python/pki/server/pkispawn.py @@ -190,8 +190,8 @@ def main(argv): parser.indent = 0 deployer.subsystem_type = parser.read_text( - 'Subsystem (CA/KRA/OCSP/TKS/TPS)', - options=['CA', 'KRA', 'OCSP', 'TKS', 'TPS'], + 'Subsystem (CA/KRA/OCSP/TKS/TPS/ACME)', + options=['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME'], default='CA', case_sensitive=False).upper() print() else: @@ -669,6 +669,9 @@ def main(argv): elif deployer.subsystem_type == 'TPS': print_tps_step_one_information(parser.mdict, deployer.instance) + elif deployer.subsystem_type == 'ACME': + print_acme_install_information() + else: print_final_install_information(parser.mdict, deployer.instance) @@ -682,7 +685,15 @@ def validate_user_deployment_cfg(user_deployment_cfg): line = line.strip() if not line.startswith('['): continue - if line not in ['[DEFAULT]', '[Tomcat]', '[CA]', '[KRA]', '[OCSP]', '[TKS]', '[TPS]']: + if line not in [ + '[DEFAULT]', + '[Tomcat]', + '[CA]', + '[KRA]', + '[OCSP]', + '[TKS]', + '[TPS]', + '[ACME]']: raise Exception('Invalid deployment configuration section: %s' % line) @@ -919,6 +930,7 @@ def print_skip_configuration_information(mdict, instance): print(log.PKI_CHECK_STATUS_MESSAGE % instance.name) print(log.PKI_INSTANCE_RESTART_MESSAGE % instance.name) + print() print(log.PKI_ACCESS_URL % (mdict['pki_hostname'], mdict['pki_https_port'], deployer.subsystem_type.lower())) @@ -929,6 +941,17 @@ def print_skip_configuration_information(mdict, instance): print(log.PKI_SPAWN_INFORMATION_FOOTER) +def print_acme_install_information(): + + print(log.PKI_SPAWN_INFORMATION_HEADER) + + print(log.PKI_ACCESS_URL % (deployer.mdict['pki_hostname'], + deployer.mdict['pki_https_port'], + deployer.subsystem_type.lower())) + + print(log.PKI_SPAWN_INFORMATION_FOOTER) + + def print_final_install_information(mdict, instance): print(log.PKI_SPAWN_INFORMATION_HEADER) @@ -970,6 +993,7 @@ def print_final_install_information(mdict, instance): print(log.PKI_CHECK_STATUS_MESSAGE % instance.name) print(log.PKI_INSTANCE_RESTART_MESSAGE % instance.name) + print() print(log.PKI_ACCESS_URL % (mdict['pki_hostname'], mdict['pki_https_port'], deployer.subsystem_type.lower())) diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index 9352264e695..dfa5cc28961 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -46,6 +46,34 @@ SELFTEST_CRITICAL = 'critical' +# TODO: auto-populate this map from /usr/share/pki/acme/database +ACME_DATABASE_CLASSES = { + 'ds': 'org.dogtagpki.acme.database.DSDatabase', + 'in-memory': 'org.dogtagpki.acme.database.InMemoryDatabase', + 'ldap': 'org.dogtagpki.acme.database.LDAPDatabase', + 'openldap': 'org.dogtagpki.acme.database.OpenLDAPDatabase', + 'postgresql': 'org.dogtagpki.acme.database.PostgreSQLDatabase' +} + +ACME_DATABASE_TYPES = {value: key for key, value in ACME_DATABASE_CLASSES.items()} + +# TODO: auto-populate this map from /usr/share/pki/acme/issuer +ACME_ISSUER_CLASSES = { + 'nss': 'org.dogtagpki.acme.issuer.NSSIssuer', + 'pki': 'org.dogtagpki.acme.issuer.PKIIssuer' +} + +ACME_ISSUER_TYPES = {value: key for key, value in ACME_ISSUER_CLASSES.items()} + +# TODO: auto-populate this map from /usr/share/pki/acme/realm +ACME_REALM_CLASSES = { + 'ds': 'org.dogtagpki.acme.realm.DSRealm', + 'in-memory': 'org.dogtagpki.acme.realm.InMemoryRealm', + 'postgresql': 'org.dogtagpki.acme.realm.PostgreSQLRealm' +} + +ACME_REALM_TYPES = {value: key for key, value in ACME_REALM_CLASSES.items()} + logger = logging.getLogger(__name__) @@ -2721,6 +2749,114 @@ def __init__(self, instance): super().__init__(instance, 'tps') +class ACMESubsystem(PKISubsystem): + + def __init__(self, instance): + super().__init__(instance, 'acme') + + @property + def database_conf(self): + return os.path.join(self.conf_dir, 'database.conf') + + @property + def issuer_conf(self): + return os.path.join(self.conf_dir, 'issuer.conf') + + @property + def realm_conf(self): + return os.path.join(self.conf_dir, 'realm.conf') + + def create(self, exist_ok=False, force=False): + + self.instance.makedirs(self.conf_dir, exist_ok=exist_ok) + + default_conf_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'conf') + + self.instance.copy( + os.path.join(default_conf_dir, 'database.conf'), + self.database_conf, + exist_ok=exist_ok, + force=force) + + self.instance.copy( + os.path.join(default_conf_dir, 'issuer.conf'), + self.issuer_conf, + exist_ok=exist_ok, + force=force) + + self.instance.copy( + os.path.join(default_conf_dir, 'realm.conf'), + self.realm_conf, + exist_ok=exist_ok, + force=force) + + def get_database_config(self, database_type=None): + + template_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'database') + + if database_type: + # if database type is specified, load the database.conf template + database_conf = os.path.join(template_dir, database_type, 'database.conf') + else: + # otherwise, load the current database.conf in the instance + database_conf = self.database_conf + + logger.info('Loading %s', database_conf) + config = {} + pki.util.load_properties(database_conf, config) + + return config + + def update_database_config(self, config): + + logger.info('Updating %s', self.database_conf) + self.instance.store_properties(self.database_conf, config) + + def get_issuer_config(self, issuer_type=None): + + template_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'issuer') + + if issuer_type: + # if issuer type is specified, load the issuer.conf template + issuer_conf = os.path.join(template_dir, issuer_type, 'issuer.conf') + else: + # otherwise, load the current issuer.conf in the instance + issuer_conf = self.issuer_conf + + logger.info('Loading %s', issuer_conf) + config = {} + pki.util.load_properties(issuer_conf, config) + + return config + + def update_issuer_config(self, config): + + logger.info('Updating %s', self.issuer_conf) + self.instance.store_properties(self.issuer_conf, config) + + def get_realm_config(self, realm_type=None): + + template_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'acme', 'realm') + + if realm_type: + # if realm type is specified, load the realm.conf template + realm_conf = os.path.join(template_dir, realm_type, 'realm.conf') + else: + # otherwise, load the current realm.conf in the instance + realm_conf = self.realm_conf + + logger.info('Loading %s', realm_conf) + config = {} + pki.util.load_properties(realm_conf, config) + + return config + + def update_realm_config(self, config): + + logger.info('Updating %s', self.realm_conf) + self.instance.store_properties(self.realm_conf, config) + + class PKISubsystemFactory(object): @classmethod