URL: https://github.com/freeipa/freeipa/pull/769 Author: Rezney Title: #769: test_caless: add pkinit option and test it Action: opened
PR body: """ What was done? ~~~ 1.) caless-create-pki The script was kind of merged with https://github.com/freeipa/freeipa-tools/blob/master/makepki.sh. Standa took care of PKINIT certificates generation so that write_chain() function was introduced which handles cert chain in the pkcs12 files and also reverse chanin order for openssl command. Then gen_pkinit_extensions() and gen_pkinit_cert() are handling the PKINIT certificate generation. See https://web.mit.edu/kerberos/krb5-1.13/doc/admin/pkinit.html for details. 2.) test_caless.py As the tests are currently failing due to the pkinit option not provided "pkinit_pin, pkinit_pkcs12_exists and pkinit_pkcs12" parameters were added to both install_server() and prepare_replica methods and particular options are added to installator. Then copy_pkinit() is handling pkinit certs transfer. TestPKINIT class contains test_server_replica_install_pkinit() test which checks both server and replica install with pkinit for a starter. Eventually added "raiseonerr=False" to ipa_certs_cleanup() cause tests were failing there but that whole workaround for ticket 4639 will be removed in different commit. ~~~ What can be improved? (at least what I am aware of) ~~~ Currently pkinit certificates are not inside nss db so we copy it separately (we could also move it to certdir and copy as whole). Tried to put it there with pk12util but the certs were getting nicknames from openssl friendly names (I guess). Added -name parameter to "openssl pkcs12 -export" command and the nicknames were fine (e.g. "ca1/pkinit-server" after certuril -L) however after the "caless-create-pki" script was done all pkinit cert nicknames were just prefixed with "ca1/" (instead of ca1/ ca2/ etc.). ~~~ Issues found: ~~~ Replica install with pkinit is not failing anymore with "Certificate issuance failed (CA_UNREACHABLE)", however the ERROR message is still presented: [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] [1/1]: installing X509 Certificate for PKINIT [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] ipa : ERROR PKINIT certificate request failed: Certificate issuance failed (CA_UNREACHABLE) [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] ipa : ERROR Failed to configure PKINIT [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] Done configuring Kerberos KDC (krb5kdc). [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] Applying LDAP updates [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] Upgrading IPA:. Estimated time: 1 minute 30 seconds [ipa.ipatests.pytest_plugins.integration.host.Host.vm-021.cmd26] [1/9]: stopping directory server ~~~ """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/769/head:pr769 git checkout pr769
From e8fbb3de2436936370b3be3df5c5dfbd59670333 Mon Sep 17 00:00:00 2001 From: Michal Reznik <mrez...@redhat.com> Date: Tue, 9 May 2017 16:39:45 +0200 Subject: [PATCH] test_caless: add pkinit option and test it change "caless-create-pki" so pkinit certificates can be generated. See https://web.mit.edu/kerberos/krb5-1.13/doc/admin/pkinit.html for details. add pkinit option to the ipa installer and test both master and replica install with pkinit. Signed-off-by: Michal Reznik <mrez...@redhat.com> --- .../test_integration/scripts/caless-create-pki | 112 ++++++++++++++++----- ipatests/test_integration/test_caless.py | 77 +++++++++++--- 2 files changed, 150 insertions(+), 39 deletions(-) diff --git a/ipatests/test_integration/scripts/caless-create-pki b/ipatests/test_integration/scripts/caless-create-pki index 8928e95..816e7dc 100644 --- a/ipatests/test_integration/scripts/caless-create-pki +++ b/ipatests/test_integration/scripts/caless-create-pki @@ -1,14 +1,29 @@ #!/bin/bash -e -profile_ca=(-t CT,C,C -v 120) -profile_server=(-t ,, -v 12) - -crl_path=${crl_path-$(readlink -f $dbdir)} - -serial_number=0 +profile_ca_request_options=(-1 -2 -4) +profile_ca_request_input="\$'0\n1\n5\n6\n9\ny\ny\n\ny\n1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'" +profile_ca_create_options=(-v 120) +profile_ca_add_options=(-t CT,C,C) +profile_server_request_options=(-4) +profile_server_request_input="\$'1\n7\nfile://'\$(readlink -f \$dbdir)/\$ca.crl\$'\n-1\n-1\n-1\nn\nn\n'" +profile_server_create_options=(-v 12) +profile_server_add_options=(-t ,,) + +write_chain() { + local nick="$1" + + chain=`certutil -O -d $dbdir -n "$nick" | + sed -e '/^\s*$/d' -e "s/\s*\"\(.*\)\" \[.*/\1/g"` + + while read -r name; do + # OpenSSL requires a reverse order to what we get from NSS + echo -e "`certutil -L -d "$dbdir" -n "$name" -a`\n`cat $dbdir/$nick.pem` + " > "$dbdir/$nick.pem" + done <<< "$chain" +} gen_cert() { - local profile="$1" nick="$2" subject="$3" ca options pwfile noise csr crt + local profile="$1" nick="$2" subject="$3" ca request_options request_input create_options serial add_options pwfile noise csr crt shift 3 echo "gen_cert(profile=$profile nick=$nick subject=$subject)" @@ -18,13 +33,20 @@ gen_cert() { ca="$nick" fi - eval "options=(\"\${profile_$profile[@]}\")" + eval "request_options=(\"\${profile_${profile}_request_options[@]}\")" + eval "eval request_input=\"\${profile_${profile}_request_input}\"" + + eval "create_options=(\"\${profile_${profile}_create_options[@]}\")" if [ "$ca" = "$nick" ]; then - options=("${options[@]}" -x -m 1) + create_options=("${create_options[@]}" -x -m 1) else - options=("${options[@]}" -c "$ca") + eval "serial_${ca//\//_}=\$((\${serial_${ca//\//_}:-1}+1))" + eval "serial=\$serial_${ca//\//_}" + create_options=("${create_options[@]}" -c "$ca" -m "$serial") fi + eval "add_options=(\"\${profile_${profile}_add_options[@]}\")" + pwfile="$(mktemp)" echo "$dbpassword" >"$pwfile" @@ -38,22 +60,14 @@ gen_cert() { csr="$(mktemp)" crt="$(mktemp)" - certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" -4 -2 >/dev/null <<EOF -y -0 -N -1 -7 -file://$crl_path/$ca.crl --1 --1 --1 -n -n -EOF - serial_number=$(($serial_number+1)) - certutil -C -d "$dbdir" -f "$pwfile" -m "$serial_number" -i "$csr" -o "$crt" "${options[@]}" "$@" - certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${options[@]}" + + certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" "${request_options[@]}" >/dev/null <<<"$request_input" + certutil -C -d "$dbdir" -f "$pwfile" -i "$csr" -o "$crt" "${create_options[@]}" "$@" + certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${add_options[@]}" + + mkdir -p "$(dirname $dbdir/$nick.pem)" + write_chain "$nick" + pk12util -o "$dbdir/$nick.p12" -n "$nick" -d "$dbdir" -k "$pwfile" -w "$pwfile" rm -f "$pwfile" "$noise" "$csr" "$crt" } @@ -102,6 +116,49 @@ gen_server_certs() { revoke_cert "$nick-revoked" } +gen_pkinit_extensions() { + echo "[kdc_cert] +basicConstraints=CA:FALSE +keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement +extendedKeyUsage=TLS Web Server Authentication, 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:${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:${realm}" > "$dbdir/ext.kdc" +} + +gen_pkinit_cert() { + local nick="$1" subj="$2" outname="$3" + shift 3 + + openssl genrsa -out "$dbdir/$nick/kdc.key" 2048 > /dev/null + openssl req -new -out "$dbdir/$nick/kdc.req" -key "$dbdir/$nick/kdc.key" \ + -subj "$subj" + + openssl pkcs12 -in "$dbdir/$nick.p12" -passin "pass:$dbpassword" \ + -nodes -nocerts -out "$dbdir/$nick.key" > /dev/null + + openssl x509 -req -in "$dbdir/$nick/kdc.req" \ + -CAkey "$dbdir/$nick.key" -CA "$dbdir/$nick.pem" \ + -out "$dbdir/$nick/kdc.crt" -days 365 \ + -extfile "$dbdir/ext.kdc" -extensions kdc_cert -CAcreateserial > /dev/null + + rm "$dbdir/$nick/kdc.req" + + openssl pkcs12 -export -in "$dbdir/$nick/kdc.crt" \ + -inkey "$dbdir/$nick/kdc.key" -password "pass:$dbpassword" \ + -out "$dbdir/$nick/$outname.p12" -chain -CAfile "$dbdir/$nick.pem" +} + gen_subtree() { local nick="$1" org="$2" shift 2 @@ -110,6 +167,8 @@ gen_subtree() { gen_cert ca "$nick" "CN=CA,O=$org" "$@" gen_cert server "$nick/wildcard" "CN=*.$domain,O=$org" + gen_pkinit_cert "$nick" "/O=$realm/CN=$server1" "pkinit-server" + gen_pkinit_cert "$nick" "/O=$realm/CN=$server2" "pkinit-replica" gen_server_certs "$nick/server" "$server1" "$org" gen_server_certs "$nick/replica" "$server2" "$org" gen_server_certs "$nick/client" "$client" "$org" @@ -117,6 +176,7 @@ gen_subtree() { gen_cert server server-selfsign "CN=$server1,O=Self-signed" gen_cert server replica-selfsign "CN=$server2,O=Self-signed" +gen_pkinit_extensions gen_cert server noca "CN=$server1,O=No-CA" gen_subtree ca1 'Example Organization' gen_subtree ca1/subca 'Subsidiary Example Organization' diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py index d7692ec..0438472 100644 --- a/ipatests/test_integration/test_caless.py +++ b/ipatests/test_integration/test_caless.py @@ -61,11 +61,11 @@ def ipa_certs_cleanup(host): raiseonerr=False) # A workaround for https://fedorahosted.org/freeipa/ticket/4639 result = host.run_command(['certutil', '-L', '-d', - paths.HTTPD_ALIAS_DIR]) + paths.HTTPD_ALIAS_DIR], raiseonerr=False) for rawcert in result.stdout_text.split('\n')[4: -1]: cert = rawcert.split(' ')[0] host.run_command(['certutil', '-D', '-d', paths.HTTPD_ALIAS_DIR, - '-n', cert]) + '-n', cert], raiseonerr=False) def server_install_teardown(func): @@ -123,10 +123,12 @@ def install(cls, mh): client_hostname = 'unused-client.test' cls.env = { 'domain': cls.master.domain.name, + 'realm': cls.master.domain.name.upper(), 'server1': cls.master.hostname, 'server2': replica_hostname, 'client': client_hostname, 'dbdir': 'nssdb', + 'dbpassword': cls.cert_password, 'crl_path': cls.crl_path, 'dirman_password': cls.master.config.dirman_password, } @@ -154,15 +156,21 @@ def uninstall(cls, mh): def install_server(cls, host=None, http_pkcs12='server.p12', dirsrv_pkcs12='server.p12', http_pkcs12_exists=True, dirsrv_pkcs12_exists=True, - http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, - root_ca_file='root.pem', unattended=True, - stdin_text=None): + http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None, + root_ca_file='root.pem', pkinit_pkcs12_exists=False, + pkinit_pkcs12=None, unattended=True, stdin_text=None): """Install a CA-less server Return value is the remote ipa-server-install command """ if host is None: host = cls.master + + extra_args = ['--http-cert-file', http_pkcs12, + '--dirsrv-cert-file', dirsrv_pkcs12, + '--ca-cert-file', root_ca_file, + '--ip-address', host.ip] + if http_pin is _DEFAULT: http_pin = cls.cert_password if dirsrv_pin is _DEFAULT: @@ -173,6 +181,14 @@ def install_server(cls, host=None, files_to_copy.append(http_pkcs12) if dirsrv_pkcs12_exists: files_to_copy.append(dirsrv_pkcs12) + + if pkinit_pkcs12_exists and not pkinit_pin: + pkinit_pin = cls.cert_password + if pkinit_pkcs12: + extra_args.extend(['--pkinit-cert-file', pkinit_pkcs12, + '--pkinit-pin', pkinit_pin ]) + else: + extra_args.append('--no-pkinit') for filename in set(files_to_copy): cls.copy_cert(host, filename) @@ -181,11 +197,6 @@ def install_server(cls, host=None, host.run_command(args + ["ca1"], raiseonerr=False) host.run_command(args + ["ca1/server"], raiseonerr=False) - extra_args = ['--http-cert-file', http_pkcs12, - '--dirsrv-cert-file', dirsrv_pkcs12, - '--ca-cert-file', root_ca_file, - '--ip-address', host.ip] - if http_pin is not None: extra_args.extend(['--http-pin', http_pin]) if dirsrv_pin is not None: @@ -199,13 +210,20 @@ def install_server(cls, host=None, def copy_cert(cls, host, filename): host.transport.put_file(os.path.join(cls.cert_dir, filename), os.path.join(host.config.test_dir, filename)) + @classmethod + def copy_pkinit(cls, host, pkinit_nick): + filename = pkinit_nick.split('/')[-1] + host.transport.put_file(os.path.join(cls.cert_dir, 'nssdb', pkinit_nick), + os.path.join(host.config.test_dir, filename)) + def prepare_replica(self, _replica_number=0, replica=None, master=None, http_pkcs12='replica.p12', dirsrv_pkcs12='replica.p12', http_pkcs12_exists=True, dirsrv_pkcs12_exists=True, - http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, - root_ca_file='root.pem', unattended=True, - stdin_text=None, domain_level=None): + http_pin=_DEFAULT, dirsrv_pin=_DEFAULT, pkinit_pin=None, + root_ca_file='root.pem', pkinit_pkcs12_exists=False, + pkinit_pkcs12=None, unattended=True, stdin_text=None, + domain_level=None): """Prepare a CA-less replica Puts the bundle file into test_dir on the replica if successful, @@ -249,6 +267,15 @@ def prepare_replica(self, _replica_number=0, replica=None, master=None, if dirsrv_pkcs12_exists: extra_args.extend(['--dirsrv-cert-file', dirsrv_pkcs12]) + if pkinit_pkcs12_exists and not pkinit_pin: + pkinit_pin = self.cert_password + if pkinit_pkcs12 and domain_level != DOMAIN_LEVEL_0: + extra_args.extend(['--pkinit-cert-file', pkinit_pkcs12, + '--pkinit-pin', pkinit_pin ]) + else: + extra_args.append('--no-pkinit') + + if http_pin is not None: extra_args.extend(['--http-pin', http_pin]) if dirsrv_pin is not None: @@ -1472,3 +1499,27 @@ def test_ds_old_options(self): result = self.certinstall('d', 'ca1/server', args=args, stdin_text=stdin_text) assert_error(result, "no such option: --dirsrv-pin") + + +class TestPKINIT(CALessBase): + num_replicas = 1 + + @classmethod + def install(cls, mh): + super(TestPKINIT, cls).install(mh) + cls.export_pkcs12('ca1/server') + cls.copy_pkinit(cls.master, 'ca1/pkinit-server.p12') + with open(cls.pem_filename, 'w') as f: + f.write(cls.get_pem('ca1')) + result = cls.install_server(pkinit_pkcs12='pkinit-server.p12', + pkinit_pkcs12_exists=True) + assert result.returncode == 0 + + @replica_install_teardown + def test_server_replica_install_pkinit(self): + self.export_pkcs12('ca1/replica', filename='replica.p12') + self.copy_pkinit(self.replicas[0], 'ca1/pkinit-replica.p12') + result = self.prepare_replica(pkinit_pkcs12='pkinit-replica.p12', + pkinit_pkcs12_exists=True) + assert result.returncode == 0 + self.verify_installation()
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code