diff --git a/roles/ipareplica/library/ipareplica_ds_apply_updates.py b/roles/ipareplica/library/ipareplica_ds_apply_updates.py index 66aecd2680e688a639e4492a6680177fe27f58c0..3796874bc3f5dd7e85ffccd1b33940582e17450d 100644 --- a/roles/ipareplica/library/ipareplica_ds_apply_updates.py +++ b/roles/ipareplica/library/ipareplica_ds_apply_updates.py @@ -123,8 +123,8 @@ def main(): ccache=dict(required=True), _ca_enabled=dict(required=False, type='bool'), _ca_file=dict(required=False), - _dirsrv_pkcs12_info=dict(required=False), - _pkinit_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), dirman_password=dict(required=True, no_log=True), ds_ca_subject=dict(required=True), diff --git a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py index 88c5f0b3e7a6cd53ad0bf1aebe954293be208ecd..a1b638efccecee96d68e62a7cd529db3d02dd545 100644 --- a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py +++ b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py @@ -119,8 +119,8 @@ def main(): ccache=dict(required=True), _ca_enabled=dict(required=False, type='bool'), _ca_file=dict(required=False), - _dirsrv_pkcs12_info=dict(required=False), - _pkinit_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), dirman_password=dict(required=True, no_log=True), ds_ca_subject=dict(required=True), diff --git a/roles/ipareplica/library/ipareplica_krb_enable_ssl.py b/roles/ipareplica/library/ipareplica_krb_enable_ssl.py index 1a73414d8add47c49aef29ea18e88886dc552f1d..a302b0faf4949746470eb34b354097d47af32334 100644 --- a/roles/ipareplica/library/ipareplica_krb_enable_ssl.py +++ b/roles/ipareplica/library/ipareplica_krb_enable_ssl.py @@ -106,7 +106,7 @@ def main(): ccache=dict(required=True), _ca_enabled=dict(required=False, type='bool'), _ca_file=dict(required=False), - _pkinit_pkcs12_info=dict(required=False), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), dirman_password=dict(required=True, no_log=True), ), diff --git a/roles/ipareplica/library/ipareplica_prepare.py b/roles/ipareplica/library/ipareplica_prepare.py index 0478d7de9c6ae95286bb179f7089f3eca13ada59..ed89b692c59bf22d0e618f8125bf663ee6265817 100644 --- a/roles/ipareplica/library/ipareplica_prepare.py +++ b/roles/ipareplica/library/ipareplica_prepare.py @@ -195,6 +195,7 @@ import os import tempfile import traceback import six +from shutil import copyfile from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_ipa_replica import ( @@ -485,6 +486,21 @@ def main(): "certificate are not signed by the same CA " "certificate") + # Copy pkcs12_files to make them persistent till deployment is done + # and encode certificates for ansible compatibility + if http_pkcs12_info is not None: + copyfile(http_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_http") + http_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_http", http_pin) + http_ca_cert = "" + if dirsrv_pkcs12_info is not None: + copyfile(dirsrv_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_dirsrv") + dirsrv_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_dirsrv", dirsrv_pin) + dirsrv_ca_cert = "" + if pkinit_pkcs12_info is not None: + copyfile(pkinit_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_pkinit") + pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin) + pkinit_ca_cert = "" + ansible_log.debug("-- FQDN --") installutils.verify_fqdn(config.host_name, options.no_host_dns) diff --git a/roles/ipareplica/library/ipareplica_setup_ca.py b/roles/ipareplica/library/ipareplica_setup_ca.py index 850ceb964b82e14e4a7fde5342fa3cf26c160c91..d71299b6754fce6cf7aae7f907526c4c86a1c30d 100644 --- a/roles/ipareplica/library/ipareplica_setup_ca.py +++ b/roles/ipareplica/library/ipareplica_setup_ca.py @@ -138,8 +138,8 @@ def main(): _ca_file=dict(required=False), _kra_enabled=dict(required=False, type='bool'), _kra_host_name=dict(required=False), - _dirsrv_pkcs12_info=dict(required=False), - _pkinit_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), _ca_subject=dict(required=True), _subject_base=dict(required=True), diff --git a/roles/ipareplica/library/ipareplica_setup_custodia.py b/roles/ipareplica/library/ipareplica_setup_custodia.py index e9e4047a4cc81476b267efcc83eca6c6bd3f258a..5a74e876782cb31660f0656b47c9cfd3270c4e4c 100644 --- a/roles/ipareplica/library/ipareplica_setup_custodia.py +++ b/roles/ipareplica/library/ipareplica_setup_custodia.py @@ -118,7 +118,7 @@ def main(): _ca_file=dict(required=False), _kra_enabled=dict(required=False, type='bool'), _kra_host_name=dict(required=False), - _pkinit_pkcs12_info=dict(required=False), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), dirman_password=dict(required=True, no_log=True), ), diff --git a/roles/ipareplica/library/ipareplica_setup_ds.py b/roles/ipareplica/library/ipareplica_setup_ds.py index 39e3348a9e6e85957f8c0263f518c895e84d58ac..8a44120b4634cc5dc4f39dbc85898a7b694f9e8d 100644 --- a/roles/ipareplica/library/ipareplica_setup_ds.py +++ b/roles/ipareplica/library/ipareplica_setup_ds.py @@ -190,7 +190,7 @@ def main(): ccache=dict(required=True), installer_ccache=dict(required=True), _ca_enabled=dict(required=False, type='bool'), - _dirsrv_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), _add_to_ipaservers=dict(required=True, type='bool'), _ca_subject=dict(required=True), diff --git a/roles/ipareplica/library/ipareplica_setup_http.py b/roles/ipareplica/library/ipareplica_setup_http.py index a33587c766fcc6b7680d9e6ae555d52d0c40b934..987ea9598c44cc9ea08fa25d68ec5e42be3b62f0 100644 --- a/roles/ipareplica/library/ipareplica_setup_http.py +++ b/roles/ipareplica/library/ipareplica_setup_http.py @@ -115,7 +115,7 @@ def main(): ccache=dict(required=True), _ca_enabled=dict(required=False, type='bool'), _ca_file=dict(required=False), - _http_pkcs12_info=dict(required=False), + _http_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), dirman_password=dict(required=True, no_log=True), ), diff --git a/roles/ipareplica/library/ipareplica_setup_krb.py b/roles/ipareplica/library/ipareplica_setup_krb.py index 7763f76ff0708b1e57d43f409869f62f104e8b4f..c8d09f732da2578f499bfeb6dff8d8a58175ee3d 100644 --- a/roles/ipareplica/library/ipareplica_setup_krb.py +++ b/roles/ipareplica/library/ipareplica_setup_krb.py @@ -96,7 +96,7 @@ def main(): # additional config_master_host_name=dict(required=True), ccache=dict(required=True), - _pkinit_pkcs12_info=dict(required=False), + _pkinit_pkcs12_info=dict(required=False, type='list'), _top_dir=dict(required=True), ), supports_check_mode=True, diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml index fe81a4d1d80b2fe00eb81f7acca41caffeada8b5..fc7f83e433dedd9172dc187ca86c2a089c8cc8fe 100644 --- a/roles/ipareplica/tasks/install.yml +++ b/roles/ipareplica/tasks/install.yml @@ -407,6 +407,7 @@ ccache: "{{ result_ipareplica_prepare.ccache }}" _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}" _ca_file: "{{ result_ipareplica_prepare._ca_file }}" + _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}" _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}" _top_dir: "{{ result_ipareplica_prepare._top_dir }}" dirman_password: "{{ ipareplica_dirman_password }}" @@ -750,6 +751,16 @@ state: absent when: result_ipareplica_enable_ipa.changed + always: + - name: Cleanup temporary files + file: + path: "{{ item }}" + state: absent + with_items: + - "/etc/ipa/.tmp_pkcs12_dirsrv" + - "/etc/ipa/.tmp_pkcs12_http" + - "/etc/ipa/.tmp_pkcs12_pkinit" + when: not ansible_check_mode and not (result_ipareplica_test.client_already_configured is defined or result_ipareplica_test.server_already_configured is defined) diff --git a/roles/ipaserver/library/ipaserver_set_ds_password.py b/roles/ipaserver/library/ipaserver_set_ds_password.py index 007730241ed22b3d63bf728b588e89880cde7e4e..4b5b3b35ee3994aca8d69dc609bd82af16e4e309 100644 --- a/roles/ipaserver/library/ipaserver_set_ds_password.py +++ b/roles/ipaserver/library/ipaserver_set_ds_password.py @@ -127,7 +127,7 @@ def main(): no_hbac_allow=dict(required=False, type='bool', default=False), no_pkinit=dict(required=False, type='bool', default=False), dirsrv_config_file=dict(required=False), - _dirsrv_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), # ssl certificate dirsrv_cert_files=dict(required=False, type='list', default=[]), subject_base=dict(required=False), diff --git a/roles/ipaserver/library/ipaserver_setup_ca.py b/roles/ipaserver/library/ipaserver_setup_ca.py index faaf66018a7cbffcde3ead96d53fac88af258473..6a5cfcce45d2f9772603c6aaae9b9c153dbd1fe8 100644 --- a/roles/ipaserver/library/ipaserver_setup_ca.py +++ b/roles/ipaserver/library/ipaserver_setup_ca.py @@ -163,7 +163,7 @@ from ansible.module_utils.ansible_ipa_server import ( AnsibleModuleLog, setup_logging, options, sysrestore, paths, ansible_module_get_parsed_ip_addresses, api_Backend_ldap2, redirect_stdout, ca, installutils, ds_init_info, - custodiainstance, write_cache, x509 + custodiainstance, write_cache, x509, decode_certificate ) @@ -191,7 +191,7 @@ def main(): no_pkinit=dict(required=False, type='bool', default=False), dirsrv_config_file=dict(required=False), dirsrv_cert_files=dict(required=False, type='list'), - _dirsrv_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), # certificate system external_ca=dict(required=False, type='bool', default=False), external_ca_type=dict(required=False), @@ -265,8 +265,8 @@ def main(): # additional options.domainlevel = ansible_module.params.get('domainlevel') options._http_ca_cert = ansible_module.params.get('_http_ca_cert') - # tions._update_hosts_file = ansible_module.params.get( - # 'update_hosts_file') + if options._http_ca_cert is not None: + options._http_ca_cert = decode_certificate(options._http_ca_cert) # init ################################################################# diff --git a/roles/ipaserver/library/ipaserver_setup_ds.py b/roles/ipaserver/library/ipaserver_setup_ds.py index fc49d62a246bcd23b12e7bdade8a3c0243244f00..3fc9d50ad2ceb3a6ba02d4f232b3021f97395346 100644 --- a/roles/ipaserver/library/ipaserver_setup_ds.py +++ b/roles/ipaserver/library/ipaserver_setup_ds.py @@ -126,7 +126,7 @@ def main(): dirsrv_config_file=dict(required=False), # ssl certificate dirsrv_cert_files=dict(required=False, type='list', default=[]), - _dirsrv_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), # certificate system external_cert_files=dict(required=False, type='list', default=[]), subject_base=dict(required=False), diff --git a/roles/ipaserver/library/ipaserver_setup_http.py b/roles/ipaserver/library/ipaserver_setup_http.py index f5479f5d152250a565fef71841aa21c02092ff54..4d9a54d6585f634c6850b5af321e1b85434da6fa 100644 --- a/roles/ipaserver/library/ipaserver_setup_http.py +++ b/roles/ipaserver/library/ipaserver_setup_http.py @@ -199,8 +199,8 @@ def main(): # _update_hosts_file=dict(required=False, type='bool', # default=False), - _dirsrv_pkcs12_info=dict(required=False), - _http_pkcs12_info=dict(required=False), + _dirsrv_pkcs12_info=dict(required=False, type='list'), + _http_pkcs12_info=dict(required=False, type='list'), ), ) diff --git a/roles/ipaserver/library/ipaserver_setup_krb.py b/roles/ipaserver/library/ipaserver_setup_krb.py index d86714672cff3864943aacaacb02a27ebcb212e2..1101d8d0cdf178583857b5deadac5179abccd351 100644 --- a/roles/ipaserver/library/ipaserver_setup_krb.py +++ b/roles/ipaserver/library/ipaserver_setup_krb.py @@ -160,7 +160,7 @@ def main(): no_reverse=dict(required=False, type='bool', default=False), auto_forwarders=dict(required=False, type='bool', default=False), - _pkinit_pkcs12_info=dict(required=False), + _pkinit_pkcs12_info=dict(required=False, type='list'), ), ) diff --git a/roles/ipaserver/library/ipaserver_test.py b/roles/ipaserver/library/ipaserver_test.py index 71afabcf6e7ee4b18ef0eb631e77af26da7ff9b7..4ac100c91d053f3ce188d38fa1fced725102c072 100644 --- a/roles/ipaserver/library/ipaserver_test.py +++ b/roles/ipaserver/library/ipaserver_test.py @@ -209,6 +209,7 @@ import sys import six import inspect import random +from shutil import copyfile from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native @@ -219,7 +220,8 @@ from ansible.module_utils.ansible_ipa_server import ( NUM_VERSION, is_ipa_configured, sysrestore, paths, bindinstance, read_cache, ca, tasks, check_ldap_conf, timeconf, httpinstance, check_dirsrv, ScriptError, get_fqdn, verify_fqdn, BadHostError, - validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION + validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION, + encode_certificate ) if six.PY3: @@ -252,7 +254,7 @@ def main(): dirsrv_config_file=dict(required=False), # ssl certificate dirsrv_cert_files=dict(required=False, type='list', default=None), - http_cert_files=dict(required=False, type='list', defaullt=None), + http_cert_files=dict(required=False, type='list', default=None), pkinit_cert_files=dict(required=False, type='list', default=None), dirsrv_pin=dict(required=False), http_pin=dict(required=False), @@ -1013,6 +1015,21 @@ def main(): # done ################################################################## + # Copy pkcs12_files to make them persistent till deployment is done + # and encode certificates for ansible compatibility + if http_pkcs12_info is not None: + copyfile(http_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_http") + http_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_http", http_pin) + http_ca_cert = encode_certificate(http_ca_cert) + if dirsrv_pkcs12_info is not None: + copyfile(dirsrv_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_dirsrv") + dirsrv_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_dirsrv", dirsrv_pin) + dirsrv_ca_cert = encode_certificate(dirsrv_ca_cert) + if pkinit_pkcs12_info is not None: + copyfile(pkinit_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_pkinit") + pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin) + pkinit_ca_cert = encode_certificate(pkinit_ca_cert) + ansible_module.exit_json(changed=False, ipa_python_version=IPA_PYTHON_VERSION, # basic diff --git a/roles/ipaserver/module_utils/ansible_ipa_server.py b/roles/ipaserver/module_utils/ansible_ipa_server.py index 244bc75d2ad99b524b18e17f31d68e337a939c8c..d934751389a33dfc8290e7e4133a36655bef0fd7 100644 --- a/roles/ipaserver/module_utils/ansible_ipa_server.py +++ b/roles/ipaserver/module_utils/ansible_ipa_server.py @@ -37,11 +37,13 @@ __all__ = ["IPAChangeConf", "certmonger", "sysrestore", "root_logger", "validate_dm_password", "read_cache", "write_cache", "adtrustinstance", "IPAAPI_USER", "sync_time", "PKIIniLoader", "default_subject_base", "default_ca_subject_dn", - "check_ldap_conf"] + "check_ldap_conf", "encode_certificate", "decode_certificate"] import sys import logging from contextlib import contextmanager as contextlib_contextmanager +import six +import base64 from ipapython.version import NUM_VERSION, VERSION @@ -137,6 +139,17 @@ if NUM_VERSION >= 40500: except ImportError: check_ldap_conf = None + try: + from ipalib.x509 import Encoding + except ImportError: + from cryptography.hazmat.primitives.serialization import Encoding + + try: + from ipalib.x509 import load_pem_x509_certificate + except ImportError: + from ipalib.x509 import load_certificate + load_pem_x509_certificate = None + else: # IPA version < 4.5 @@ -322,3 +335,41 @@ def ansible_module_get_parsed_ip_addresses(ansible_module, ansible_module.fail_json(msg="Invalid IP Address %s: %s" % (ip, e)) ip_addrs.append(ip_parsed) return ip_addrs + + +def encode_certificate(cert): + """ + Encode a certificate using base64. + + It also takes FreeIPA and Python versions into account. + """ + if isinstance(cert, (str, bytes)): + encoded = base64.b64encode(cert) + else: + encoded = base64.b64encode(cert.public_bytes(Encoding.DER)) + if not six.PY2: + encoded = encoded.decode('ascii') + return encoded + + +def decode_certificate(cert): + """ + Decode a certificate using base64. + + It also takes FreeIPA versions into account and returns a IPACertificate + for newer IPA versions. + """ + if hasattr(x509, "IPACertificate"): + cert = cert.strip() + if not cert.startswith("-----BEGIN CERTIFICATE-----"): + cert = "-----BEGIN CERTIFICATE-----\n" + cert + if not cert.endswith("-----END CERTIFICATE-----"): + cert += "\n-----END CERTIFICATE-----" + + if load_pem_x509_certificate is not None: + cert = load_pem_x509_certificate(cert.encode('utf-8')) + else: + cert = load_certificate(cert.encode('utf-8')) + else: + cert = base64.b64decode(cert) + return cert diff --git a/roles/ipaserver/tasks/install.yml b/roles/ipaserver/tasks/install.yml index 85df9a7dd92610a133be9dffc92aef4629837cfe..30f9da2102e074b9317397d9ad7fabf77a3fa380 100644 --- a/roles/ipaserver/tasks/install.yml +++ b/roles/ipaserver/tasks/install.yml @@ -286,6 +286,7 @@ reverse_zones: "{{ result_ipaserver_prepare.reverse_zones }}" no_reverse: "{{ ipaserver_no_reverse }}" auto_forwarders: "{{ ipaserver_auto_forwarders }}" + _http_ca_cert: "{{ result_ipaserver_test._http_ca_cert }}" register: result_ipaserver_setup_ca - name: Copy /root/ipa.csr to "{{ inventory_hostname }}-ipa.csr" @@ -448,6 +449,16 @@ when: not result_ipaserver_setup_ca.csr_generated | bool + always: + - name: Cleanup temporary files + file: + path: "{{ item }}" + state: absent + with_items: + - "/etc/ipa/.tmp_pkcs12_dirsrv" + - "/etc/ipa/.tmp_pkcs12_http" + - "/etc/ipa/.tmp_pkcs12_pkinit" + when: not ansible_check_mode and not (not result_ipaserver_test.changed and (result_ipaserver_test.client_already_configured is defined or diff --git a/tests/ca-less/certificates/pkinit/extensions.conf b/tests/ca-less/certificates/pkinit/extensions.conf new file mode 100644 index 0000000000000000000000000000000000000000..cbff73bef1ed6cf35caf01ec8347627155983b27 --- /dev/null +++ b/tests/ca-less/certificates/pkinit/extensions.conf @@ -0,0 +1,20 @@ +[kdc_cert] +basicConstraints=CA:FALSE +keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement +extendedKeyUsage=1.3.6.1.5.2.3.5 +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +issuerAltName=issuer:copy +subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name + +[kdc_princ_name] +realm=EXP:0,GeneralString:${ENV::REALM} +principal_name=EXP:1,SEQUENCE:kdc_principal_seq + +[kdc_principal_seq] +name_type=EXP:0,INTEGER:1 +name_string=EXP:1,SEQUENCE:kdc_principals + +[kdc_principals] +princ1=GeneralString:krbtgt +princ2=GeneralString:${ENV::REALM} diff --git a/tests/ca-less/clean_up_certificates.yml b/tests/ca-less/clean_up_certificates.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea8a4e8ee714efbe9dd95f792eb92d11964c6eda --- /dev/null +++ b/tests/ca-less/clean_up_certificates.yml @@ -0,0 +1,15 @@ +--- +- name: Clean up certificates + hosts: localhost + gather_facts: false + + tasks: + - name: Run generate-certificates.sh + command: > + /bin/bash + generate-certificates.sh delete "{{ item }}" + args: + chdir: "{{ playbook_dir }}" + with_items: + - "{{ groups.ipaserver[0] }}" + - "{{ groups.ipareplicas[0] }}" \ No newline at end of file diff --git a/tests/ca-less/generate-certificates.sh b/tests/ca-less/generate-certificates.sh new file mode 100755 index 0000000000000000000000000000000000000000..e96d323ae4a932883d2bc574d1e1cea3305d9b1e --- /dev/null +++ b/tests/ca-less/generate-certificates.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +ROOT_CA_DIR="certificates/root-ca" +DIRSRV_CERTS_DIR="certificates/dirsrv" +HTTPD_CERTS_DIR="certificates/httpd" +PKINIT_CERTS_DIR="certificates/pkinit" +PKCS12_PASSWORD="SomePKCS12password" + +# generate_ipa_pkcs12_certificate \ +# $cert_name $ipa_fqdn $certs_dir $root_ca_cert $root_ca_private_key extensions_file extensions_name +function generate_ipa_pkcs12_certificate { + + cert_name=$1 + ipa_fqdn=$2 + certs_dir=$3 + root_ca_cert=$4 + root_ca_private_key=$5 + extensions_file=$6 + extensions_name=$7 + + # Generate CSR and private key + openssl req -new -newkey rsa:4096 -nodes \ + -subj "/C=US/ST=Test/L=Testing/O=Default/CN=${ipa_fqdn}" \ + -keyout ${certs_dir}/private.key \ + -out ${certs_dir}/request.csr + + # Sign CSR to generate PEM certificate + if [ -z "${extensions_file}" ]; then + openssl x509 -req -days 365 -sha256 \ + -CAcreateserial \ + -CA ${root_ca_cert} \ + -CAkey ${root_ca_private_key} \ + -in ${certs_dir}/request.csr \ + -out ${certs_dir}/cert.pem + else + openssl x509 -req -days 365 -sha256 \ + -CAcreateserial \ + -CA ${ROOT_CA_DIR}/cert.pem \ + -CAkey ${ROOT_CA_DIR}/private.key \ + -extfile ${extensions_file} \ + -extensions ${extensions_name} \ + -in ${certs_dir}/request.csr \ + -out ${certs_dir}/cert.pem + fi + + # Convert certificate to PKCS12 format + openssl pkcs12 -export \ + -name ${cert_name} \ + -certfile ${root_ca_cert} \ + -in ${certs_dir}/cert.pem \ + -inkey ${certs_dir}/private.key \ + -passout "pass:${PKCS12_PASSWORD}" \ + -out ${certs_dir}/cert.p12 +} + +# generate_ipa_pkcs12_certificates $ipa_fqdn $ipa_domain +function generate_ipa_pkcs12_certificates { + + host=$1 + if [ -z "$host" ]; then + echo "ERROR: ipa-host-fqdn is not set" + echo + echo "usage: $0 create ipa-host-fqdn domain" + exit 0; + fi + + domain=$2 + if [ -z "$domain" ]; then + echo "ERROR: domain is not set" + echo + echo "usage: $0 create ipa-host-fqdn domain" + exit 0; + fi + + # Generate certificates folder structure + mkdir -p ${ROOT_CA_DIR} + mkdir -p ${DIRSRV_CERTS_DIR}/$host + mkdir -p ${HTTPD_CERTS_DIR}/$host + mkdir -p ${PKINIT_CERTS_DIR}/$host + + # Generate root CA + if [ ! -f "${ROOT_CA_DIR}/private.key" ]; then + openssl genrsa \ + -out ${ROOT_CA_DIR}/private.key 4096 + + openssl req -new -x509 -sha256 -nodes -days 3650 \ + -subj "/C=US/ST=Test/L=Testing/O=Default" \ + -key ${ROOT_CA_DIR}/private.key \ + -out ${ROOT_CA_DIR}/cert.pem + fi + + # Generate a certificate for the Directory Server + if [ ! -f "${DIRSRV_CERTS_DIR}/$host/cert.pem" ]; then + generate_ipa_pkcs12_certificate \ + "dirsrv-cert" \ + $host \ + "${DIRSRV_CERTS_DIR}/$host" \ + "${ROOT_CA_DIR}/cert.pem" \ + "${ROOT_CA_DIR}/private.key" + fi + + # Generate a certificate for the Apache server + if [ ! -f "${HTTPD_CERTS_DIR}/$host/cert.pem" ]; then + generate_ipa_pkcs12_certificate \ + "httpd-cert" \ + $host \ + "${HTTPD_CERTS_DIR}/$host" \ + "${ROOT_CA_DIR}/cert.pem" \ + "${ROOT_CA_DIR}/private.key" + fi + + # Generate a certificate for the KDC PKINIT + if [ ! -f "${PKINIT_CERTS_DIR}/$host/cert.pem" ]; then + export REALM=${domain^^} + + generate_ipa_pkcs12_certificate \ + "pkinit-cert" \ + $host \ + "${PKINIT_CERTS_DIR}/$host" \ + "${ROOT_CA_DIR}/cert.pem" \ + "${ROOT_CA_DIR}/private.key" \ + "${PKINIT_CERTS_DIR}/extensions.conf" \ + "kdc_cert" + fi +} + +# delete_ipa_pkcs12_certificates $ipa_fqdn +function delete_ipa_pkcs12_certificates { + + host=$1 + if [ -z "$host" ]; then + echo "ERROR: ipa-host-fqdn is not set" + echo + echo "usage: $0 delete ipa-host-fqdn" + exit 0; + fi + + rm -f certificates/*/$host/* + rm -f ${ROOT_CA_DIR}/* +} + +# Entrypoint +case "$1" in + create) + generate_ipa_pkcs12_certificates $2 $3 + ;; + delete) + delete_ipa_pkcs12_certificates $2 + ;; + *) + echo $"Usage: $0 {create|delete}" + ;; +esac diff --git a/tests/ca-less/install_replica_without_ca.yml b/tests/ca-less/install_replica_without_ca.yml new file mode 100644 index 0000000000000000000000000000000000000000..83398b24596cc9be52b156d9d27ec1ff8fc4f18e --- /dev/null +++ b/tests/ca-less/install_replica_without_ca.yml @@ -0,0 +1,82 @@ +--- +- name: Generate certificates + hosts: localhost + gather_facts: false + + tasks: + - name: Run generate-certificates.sh + command: > + /bin/bash + generate-certificates.sh create + "{{ groups.ipareplicas[0] }}" + "{{ ipareplica_domain | default(groups.ipareplicas[0].split('.')[1:] | join ('.')) }}" + args: + chdir: "{{ playbook_dir }}" + +- name: Test ipareplicas installation without CA + hosts: ipareplicas + become: true + + vars: + # Root CA certificate + ipareplica_ca_cert_files: + - /root/ca-less-test/ca.crt + # Directory server certificates + ipareplica_dirsrv_cert_name: dirsrv-cert + ipareplica_dirsrv_cert_files: + - /root/ca-less-test/dirsrv.p12 + ipareplica_dirsrv_pin: SomePKCS12password + # Apache certificates + ipareplica_http_cert_name: httpd-cert + ipareplica_http_cert_files: + - /root/ca-less-test/httpd.p12 + ipareplica_http_pin: SomePKCS12password + # PKINIT configuration + ipareplica_no_pkinit: no + ipareplica_pkinit_cert_name: pkinit-cert + ipareplica_pkinit_cert_files: + - /root/ca-less-test/pkinit.p12 + ipareplica_pkinit_pin: SomePKCS12password + + pre_tasks: + - name: Remove "/root/ca-less-test" + file: + path: "/root/ca-less-test" + state: absent + + - name: Generate "/root/ca-less-test" + file: + path: "/root/ca-less-test" + state: directory + + - name: Copy CA certificate + copy: + src: "{{ playbook_dir }}/certificates/root-ca/cert.pem" + dest: "/root/ca-less-test/ca.crt" + owner: root + group: root + mode: "0644" + + - name: Copy p12 certificates + copy: + src: "{{ playbook_dir }}/certificates/{{ item }}/{{ groups.ipareplicas[0] }}/cert.p12" + dest: "/root/ca-less-test/{{ item }}.p12" + owner: root + group: root + mode: "0644" + with_items: + - dirsrv + - httpd + - pkinit + + roles: + - role: ipareplica + state: present + + post_tasks: + - name: Fix KDC certificate permissions + file: + path: /var/kerberos/krb5kdc/kdc.crt + owner: root + group: root + mode: '0644' diff --git a/tests/ca-less/install_server_without_ca.yml b/tests/ca-less/install_server_without_ca.yml new file mode 100644 index 0000000000000000000000000000000000000000..ecb609c4fd17593e0352a5b2c37c28465266e394 --- /dev/null +++ b/tests/ca-less/install_server_without_ca.yml @@ -0,0 +1,74 @@ +--- +- name: Generate certificates + hosts: localhost + gather_facts: false + + tasks: + - name: Run generate-certificates.sh + command: > + /bin/bash + generate-certificates.sh create + "{{ groups.ipaserver[0] }}" + "{{ ipaserver_domain | default(groups.ipaserver[0].split('.')[1:] | join ('.')) }}" + args: + chdir: "{{ playbook_dir }}" + +- name: Test ipaserver installation without CA + hosts: ipaserver + become: true + + vars: + # Root CA certificate + ipaserver_ca_cert_files: + - /root/ca-less-test/ca.crt + # Directory server certificates + ipaserver_dirsrv_cert_name: dirsrv-cert + ipaserver_dirsrv_cert_files: + - /root/ca-less-test/dirsrv.p12 + ipaserver_dirsrv_pin: SomePKCS12password + # Apache certificates + ipaserver_http_cert_name: httpd-cert + ipaserver_http_cert_files: + - /root/ca-less-test/httpd.p12 + ipaserver_http_pin: SomePKCS12password + # PKINIT configuration + ipaserver_no_pkinit: no + ipaserver_pkinit_cert_name: pkinit-cert + ipaserver_pkinit_cert_files: + - /root/ca-less-test/pkinit.p12 + ipaserver_pkinit_pin: SomePKCS12password + + pre_tasks: + - name: Remove "/root/ca-less-test" + file: + path: "/root/ca-less-test" + state: absent + + - name: Generate "/root/ca-less-test" + file: + path: "/root/ca-less-test" + state: directory + + - name: Copy CA certificate + copy: + src: "{{ playbook_dir }}/certificates/root-ca/cert.pem" + dest: "/root/ca-less-test/ca.crt" + owner: root + group: root + mode: "0644" + + - name: Copy p12 certificates + copy: + src: "{{ playbook_dir }}/certificates/{{ item }}/{{ groups.ipaserver[0] }}/cert.p12" + dest: "/root/ca-less-test/{{ item }}.p12" + owner: root + group: root + mode: "0644" + with_items: + - dirsrv + - httpd + - pkinit + + roles: + - role: ipaserver + state: present diff --git a/tests/ca-less/inventory b/tests/ca-less/inventory new file mode 100644 index 0000000000000000000000000000000000000000..ec5da4ef66fe5601863f805f0fbf32bea9c6b999 --- /dev/null +++ b/tests/ca-less/inventory @@ -0,0 +1,17 @@ +[ipaserver] +ipaserver.test.local + +[ipaserver:vars] +ipaserver_domain=test.local +ipaserver_realm=TEST.LOCAL +ipaadmin_password=SomeADMINpassword +ipadm_password=SomeDMpassword + +[ipareplicas] +ipareplica.test.local + +[ipareplicas:vars] +ipareplica_domain=test.local +ipareplica_realm=TEST.LOCAL +ipaadmin_password=SomeADMINpassword +ipadm_password=SomeDMpassword \ No newline at end of file