čtvrtek 18. února 2016

Client certificate authentication with oVirt

This post will show you how to configure authentication using client certificates. FreeIPA 4.2.0 introduced support for multiple certificate profiles, including support for user certificates. So we will use integration with FreeIPA 4.2. Please follow this post to create new profile for issuing user certificates in FreeIPA.

Test user creation

We create our user testing user in IPA, please be carefull specifying email address and login, we will need these later on.
$ ipa user-add --first=Ondra --last=Machacek --email=omachace@example.com omachace
---------------------
Added user "omachace"
---------------------
User login: omachace
First name: Ondra
Last name: Machacek
Full name: Ondra Machacek
Display name: Ondra Machacek
Initials: OM
Home directory: /home/omachace
GECOS: Ondra Machacek
Login shell: /bin/sh
Kerberos principal: omachace@EXAMPLE.COM
Email address: omachace@example.com
UID: 725600015
GID: 725600015
Password: False
Member of groups: ipausers
Kerberos keys available: False
view raw ipa_user_add.sh hosted with ❤ by GitHub

pkcs#12 creation

First, we need to create private key for our user:
$ openssl genrsa -out mykey.pem 2048
Second, we have to create certificate signing request(CSR), using config, where we specify subjectAltName extension, which must match our user's email address in FreeIPA and commonName which must match user's login.
$ cat ~/csr.conf
[ req ]
prompt = no
encrypt_key = no
req_extensions = exts
distinguished_name = dn
[ dn ]
commonName = "omachace"
[ exts ]
subjectAltName=email:omachace@example.com
$ openssl req -new -key mykey.pem -out cert.req -text -config csr.conf
view raw csr.sh hosted with ❤ by GitHub
We can now submit our CSR to IPA to issue certificate for our user. Remember to specify our user's login and profile for user certificates.
$ ipa cert-request cert.req --principal omachace --profile-id clientIdentity
Certificate: MIIEcjCCA1qgAwIBAgIBDTANBgkqhkiG9w0BAQsFADBQMS4wLAYDVQQKDCVCUlEtSVBBLTQuUkhFVi5MQUIuRU5HLkJSUS5SRURIQVQuQ09NMR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTYwMjE4MTEwNTQyWhcNMTgwMjE4MTEwNTQyWjBDMS4wLAYDVQQKDCVCUlEtSVBBLTQuUkhFVi5MQUIuRU5HLkJSUS5SRURIQVQuQ09NMREwDwYDVQQDDAhvbWFjaGFjZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPbWoaF2vSdVbguUvZurMcTJA5NWUfteuB/gGMTacwZMCQqqE1DBNDyW1kDcSTMJ0f65K1ae/6xEA1u9exwP6OkSqthivLt6swXID9LGaEsy2kG7jS6udH+5NMMkswTQqee17T66FkofUCr7kjTOluyIPQiidlxCIzBw8WkfO+nQTSSqy9AJh2NT9/zn0Sl8LVNwWirztfhuUB6XOxWKpONWGGnrDE8V6AqdWmNVdZTuXXoRjgdsBQ7FpPteoJiURtYnDNtxIkgL25ISOhn6siQdYnYnBYOmUfYIFi93SfdK4r1UULWGkXuK7nardHz+mkgOjrOU56TzdQW8Zk25wxECAwEAAaOCAWIwggFeMB8GA1UdIwQYMBaAFL8uAcchjBsV8cNhofSahkvtIGvdME0GCCsGAQUFBwEBBEEwPzA9BggrBgEFBQcwAYYxaHR0cDovL2lwYS1jYS5yaGV2LmxhYi5lbmcuYnJxLnJlZGhhdC5jb20vY2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwgYYGA1UdHwR/MH0we6BDoEGGP2h0dHA6Ly9pcGEtY2Eucmhldi5sYWIuZW5nLmJycS5yZWRoYXQuY29tL2lwYS9jcmwvTWFzdGVyQ1JMLmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAdBgNVHQ4EFgQUz1860OiS7+wt1LzbLosFlAjiuXowHwYDVR0RBBgwFoEUb21hY2hhY2VAZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAH9BLwuhLlx3V/3F63rtAmZXpcbdoXxbna3qED454uJ2sWMd0rZqMdlnCQoGJ9kXSKuSsTlw1Zzn/S+ubsYAIJzTeLHQ4sPsqUlxd2eg/ikcUvDTqTKMYdXHP0kDvGTAlncRd8ySKY7HQoD7Bgg97B3yYvE8bGkYC90fhQqaL9lRpuVqueeJvqZ7hWF+Of8dvOugqC0CNb0roNlZ7r5gklgRILldLomkxgjWF4hazN+X+ibnnVx8DTNzYtTO8lMdKzGZtbOdX75FKuO7Ld9ikFKvGHUp5CechJzGQrClmQP9qZVO9ZZMgCwUeXiCgzqlOehGMVkt9qQtaqYDPODJcDY=
Subject: CN=omachace,O=EXAMPLE.COM
Issuer: CN=Certificate Authority,O=EXAMPLE.COM
Not Before: Thu Feb 18 11:05:42 2016 UTC
Not After: Sun Feb 18 11:05:42 2018 UTC
Fingerprint (MD5): 0c:db:6f:ad:ef:52:e4:12:7d:fc:8e:0e:bb:c2:fc:bd
Fingerprint (SHA1): e9:15:78:94:67:25:61:d0:4f:f2:6b:59:8d:2f:2d:09:95:64:27:fe
Serial number: 13
Serial number (hex): 0xD
view raw cert_issue.sh hosted with ❤ by GitHub
Now, we need to extract user's cetificate.
$ ipa user-show omachace --out=cert.pem
----------------------------------------
Certificate(s) stored in file 'cert.pem'
----------------------------------------
User login: omachace
First name: Ondra
Last name: Machacek
Home directory: /home/omachace
Login shell: /bin/sh
Email address: omachace@example.com
UID: 725600015
GID: 725600015
Certificate: MIIEcjCCA1qgAwIBAgIBDTANBgkqhkiG9w0BAQsFADBQMS4wLAYDVQQKDCVCUlEtSVBBLTQuUkhFVi5MQUIuRU5HLkJSUS5SRURIQVQuQ09NMR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTYwMjE4MTEwNTQyWhcNMTgwMjE4MTEwNTQyWjBDMS4wLAYDVQQKDCVCUlEtSVBBLTQuUkhFVi5MQUIuRU5HLkJSUS5SRURIQVQuQ09NMREwDwYDVQQDDAhvbWFjaGFjZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPbWoaF2vSdVbguUvZurMcTJA5NWUfteuB/gGMTacwZMCQqqE1DBNDyW1kDcSTMJ0f65K1ae/6xEA1u9exwP6OkSqthivLt6swXID9LGaEsy2kG7jS6udH+5NMMkswTQqee17T66FkofUCr7kjTOluyIPQiidlxCIzBw8WkfO+nQTSSqy9AJh2NT9/zn0Sl8LVNwWirztfhuUB6XOxWKpONWGGnrDE8V6AqdWmNVdZTuXXoRjgdsBQ7FpPteoJiURtYnDNtxIkgL25ISOhn6siQdYnYnBYOmUfYIFi93SfdK4r1UULWGkXuK7nardHz+mkgOjrOU56TzdQW8Zk25wxECAwEAAaOCAWIwggFeMB8GA1UdIwQYMBaAFL8uAcchjBsV8cNhofSahkvtIGvdME0GCCsGAQUFBwEBBEEwPzA9BggrBgEFBQcwAYYxaHR0cDovL2lwYS1jYS5yaGV2LmxhYi5lbmcuYnJxLnJlZGhhdC5jb20vY2Evb2NzcDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwgYYGA1UdHwR/MH0we6BDoEGGP2h0dHA6Ly9pcGEtY2Eucmhldi5sYWIuZW5nLmJycS5yZWRoYXQuY29tL2lwYS9jcmwvTWFzdGVyQ1JMLmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAdBgNVHQ4EFgQUz1860OiS7+wt1LzbLosFlAjiuXowHwYDVR0RBBgwFoEUb21hY2hhY2VAZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAH9BLwuhLlx3V/3F63rtAmZXpcbdoXxbna3qED454uJ2sWMd0rZqMdlnCQoGJ9kXSKuSsTlw1Zzn/S+ubsYAIJzTeLHQ4sPsqUlxd2eg/ikcUvDTqTKMYdXHP0kDvGTAlncRd8ySKY7HQoD7Bgg97B3yYvE8bGkYC90fhQqaL9lRpuVqueeJvqZ7hWF+Of8dvOugqC0CNb0roNlZ7r5gklgRILldLomkxgjWF4hazN+X+ibnnVx8DTNzYtTO8lMdKzGZtbOdX75FKuO7Ld9ikFKvGHUp5CechJzGQrClmQP9qZVO9ZZMgCwUeXiCgzqlOehGMVkt9qQtaqYDPODJcDY=
Account disabled: False
Password: False
Member of groups: ipausers
Kerberos keys available: False
Now lets finnaly create our pkcs#12. Which you can later use with your browser to login.
$ openssl pkcs12 -export -out my.p12 -inkey mykey.pem -in cert.pem
Enter Export Password:
Verifying - Enter Export Password:
view raw pkcs12.sh hosted with ❤ by GitHub

Apache mod_ssl configuration

Now ssh to your oVirt machine. We need to reconfigure apache mod_ssl module, to require client certificate and since oVirt's AAA works with X-Remote-User header, we need to set it to REMOTE_USER env variable. Please add following lines into /etc/httpd/conf.d/ssl.conf.
SSLVerifyClient require
SSLVerifyDepth 1
SSLUserName SSL_CLIENT_S_DN_CN
RewriteEngine on
RewriteCond %{LA-U:REMOTE_USER} ^(.*)$
RewriteRule ^(.*)$ - [L,P,E=REMOTE_USER:%1,NS]
RequestHeader set X-Remote-User %{REMOTE_USER}s
view raw ssl_conf.sh hosted with ❤ by GitHub
Now add CA certificate of ipa to /etc/pki/ovirt-engine/apache-ca.pem
$ ssh root@ipa.example.com 'cat /etc/ipa/ca.crt' >> /etc/pki/ovirt-engine/apache-ca.pem

oVirt AAA configuration

Authz configuration:
$ cat /etc/ovirt-engine/extensions.d/example-authz.properties
ovirt.engine.extension.name = example-authz
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine-extensions.aaa.ldap
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engineextensions.aaa.ldap.AuthzExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authz
config.profile.file.1 = ../aaa/example.properties
config.globals.bindFormat.simple_bindFormat = realm
Authn configuration:
$ cat /etc/ovirt-engine/extensions.d/profile1-http-authn.properties
ovirt.engine.extension.name = example-http-authn
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine-extensions.aaa.misc
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engineextensions.aaa.misc.http.AuthnExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authn
ovirt.engine.aaa.authn.profile.name = example.com
ovirt.engine.aaa.authn.authz.plugin = example-authz
config.artifact.name = HEADER
config.artifact.arg = X-Remote-User
example.properties - specify your specifc creadentials and values for your IPA server
$ cat /etc/ovirt-engine/aaa/example.properties
include = <ipa.properties>
vars.server = ipa.example.com
vars.user = uid=search_user,cn=users,cn=accounts,dc=example,dc=com
vars.password = securepassword
pool.default.serverset.single.server = ${global:vars.server}
pool.default.auth.simple.bindDN = ${global:vars.user}
pool.default.auth.simple.password = ${global:vars.password}
Restart ovirt-engine and httpd
$ service httpd restart
$ service ovirt-engine restart
view raw restart.sh hosted with ❤ by GitHub

Let's try login

Please assign your user some permissions, in our case we assign user omachace SuperUser permission on system. Then we are able login as follows:
$ wget http://ovirt.example.com/ca.crt
$ curl --cacert ca.crt -E cert.pem --key mykey.pem https://ovirt.example.com/ovirt-engine/api/users/8483b0d6-29b2-44ea-b8a1-53b77af8be69
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user href="/ovirt-engine/api/users/8483b0d6-29b2-44ea-b8a1-53b77af8be69" id="8483b0d6-29b2-44ea-b8a1-53b77af8be69">
<name>Ondra</name>
<link href="/ovirt-engine/api/users/8483b0d6-29b2-44ea-b8a1-53b77af8be69/permissions" rel="permissions"/>
<link href="/ovirt-engine/api/users/8483b0d6-29b2-44ea-b8a1-53b77af8be69/roles" rel="roles"/>
<link href="/ovirt-engine/api/users/8483b0d6-29b2-44ea-b8a1-53b77af8be69/tags" rel="tags"/>
<domain href="/ovirt-engine/api/domains/6578616D706C652D617574687A" id="6578616D706C652D617574687A">
<name>example-authz</name>
</domain>
<domain_entry_id>35386132666338302D643632652D313165352D623962362D303031613461303133663630</domain_entry_id>
<namespace>dc=example,dc=com</namespace>
<last_name>Machacek</last_name>
<user_name>omachace@example-authz</user_name>
<principal>omachace</principal>
<email>omachace@example.com</email>
</user>
view raw login.sh hosted with ❤ by GitHub
In order to try it with our browser, please find relevant documentation how to import your pkcs#12 into browser, then you will be able to connect to oVirt via browser using client certificate.

CN to username mapping

In some setups you can met with situation, that your CN and username don't equal, in that moment you can use mapping extension. Imagine situation that in your CN is 'Ondra.Machacek' instead of 'omachace'. Then you would create a new mapping properties file as follows:
ovirt.engine.extension.name = example-http-mapping
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine-extensions.aaa.misc
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engineextensions.aaa.misc.mapping.MappingExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Mapping
config.mapAuthRecord.type = regex
config.mapAuthRecord.regex.mustMatch = true
config.mapAuthRecord.regex.pattern = ^(?<firstchar>[A-Z])[A-Z]*\\.(?<lastname>[A-Z]{1,7}).*?$
config.mapAuthRecord.regex.replacement = ${firstchar}${lastname}
And then don't forget to link this mapping to authn extension.
ovirt.engine.extension.name = example-http-authn
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine-extensions.aaa.misc
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engineextensions.aaa.misc.http.AuthnExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authn
ovirt.engine.aaa.authn.mapping.plugin = example-http-mapping
ovirt.engine.aaa.authn.profile.name = example.com
ovirt.engine.aaa.authn.authz.plugin = example-authz
config.artifact.name = HEADER
config.artifact.arg = X-Remote-User