Hi,
I'm setting up an openvpn server and I'd like to use our already existing FreeIPA
CA to issue user keys/certs for openvpn's use. Since our OpenVPN box is a freeipa
client, I thought it'd be nice to use certmonger to issue and keep up to date these
certs.
Ergo, I've created a certificate profile:
pat@apex-freeipa ~$ ipa certprofile-show --all OpenVPNUserCert
dn: cn=OpenVPNUserCert,cn=certprofiles,cn=ca,dc=int,dc=apexmw,dc=com
Profile ID: OpenVPNUserCert
Profile description: OpenVPN User Certificates
Store issued certificates: FALSE
objectclass: ipacertprofile, top
And also a CA acl. For experimentation (and working vs our test freeipa) I've left
this as wide open as I can:
[pat@apex-freeipa ~]$ ipa caacl-show --all OpenVPN_User_Certificate_ACL
dn:
ipaUniqueID=6dde33a6-7849-11e9-aa05-525400b52c7b,cn=caacls,cn=ca,dc=int,dc=apexmw,dc=com
ACL name: OpenVPN_User_Certificate_ACL
Enabled: TRUE
CA category: all
Profile category: all
User category: all
Host category: all
Service category: all
ipauniqueid: 6dde33a6-7849-11e9-aa05-525400b52c7b
objectclass: ipaassociation, ipacaacl
Then, on my openvpn server, I ask for a cert for use for one of my
users (myself, in this case):
root@apex-openvpn:~# ipa-getcert request -f /etc/openvpn/client/pat.crt -k
/etc/openvpn/client/pat.key -r -N 'CN=pat,O=INT.APEXMW.COM' -K pat -g 4096
--profile OpenVPNUserCert
New signing request "20190603014016" added.
But, it fails due to an access err vs the 'userCertificate' attribute
of my account:
root@apex-openvpn:~# ipa-getcert list
(...snippy snip excess...)
Request ID '20190603014016':
status: CA_REJECTED
ca-error: Server at
https://apex-freeipa.int.apexmw.com/ipa/xml denied our request,
giving up: 2100 (RPC failed at server. Insufficient access: Insufficient 'write'
privilege to the 'userCertificate' attribute of entry
'uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com'.).
stuck: yes
key pair storage: type=FILE,location='/etc/openvpn/client/pat.key'
certificate: type=FILE,location='/etc/openvpn/client/pat.crt'
CA: IPA
issuer:
subject:
expires: unknown
pre-save command:
post-save command:
track: yes
auto-renew: yes
If I look at the dirsrv log, here's the accesses I see for this request
(trimmed off the date/time to make the lines a _little_ shorter):
root@apex-freeipa slapd-INT-APEXMW-COM# grep conn=178 access | cut -d' ' -f3-
conn=178 fd=114 slot=114 connection from 10.10.200.1 to 10.10.200.1
conn=178 op=0 BIND dn="" method=sasl version=3 mech=GSS-SPNEGO
conn=178 op=0 RESULT err=0 tag=97 nentries=0 etime=0.0025554208
dn="fqdn=apex-openvpn.int.apexmw.com,cn=computers,cn=accounts,dc=int,dc=apexmw,dc=com"
conn=178 op=1 SRCH base="cn=ipaconfig,cn=etc,dc=int,dc=apexmw,dc=com" scope=0
filter="(objectClass=*)" attrs=ALL
conn=178 op=1 RESULT err=0 tag=101 nentries=1 etime=0.0001319554
conn=178 op=2 SRCH base="cn=masters,cn=ipa,cn=etc,dc=int,dc=apexmw,dc=com"
scope=2 filter="(&(objectClass=ipaConfigObject)(cn=CA))" attrs=ALL
conn=178 op=2 RESULT err=0 tag=101 nentries=1 etime=0.0000979573
conn=178 op=3 SRCH base="cn=masters,cn=ipa,cn=etc,dc=int,dc=apexmw,dc=com"
scope=2 filter="(&(objectClass=ipaConfigObject)(cn=CA))" attrs=ALL
conn=178 op=3 RESULT err=0 tag=101 nentries=1 etime=0.0000736730
conn=178 op=4 SRCH base="cn=cas,cn=ca,dc=int,dc=apexmw,dc=com" scope=2
filter="(&(objectClass=ipaca)(cn=ipa))" attrs=""
conn=178 op=4 RESULT err=0 tag=101 nentries=1 etime=0.0000499142
conn=178 op=5 SRCH base="cn=ipa,cn=cas,cn=ca,dc=int,dc=apexmw,dc=com" scope=0
filter="(objectClass=*)" attrs="ipaCaId ipaCaSubjectDN cn ipaCaIssuerDN
description"
conn=178 op=5 RESULT err=0 tag=101 nentries=1 etime=0.0000482726
conn=178 op=6 SRCH
base="cn=apex-freeipa.int.apexmw.com,cn=masters,cn=ipa,cn=etc,dc=int,dc=apexmw,dc=com"
scope=2
filter="(&(objectClass=ipaConfigObject)(ipaConfigString=enabledService)(cn=CA))"
attrs=ALL
conn=178 op=6 RESULT err=0 tag=101 nentries=1 etime=0.0000950646 notes=U
conn=178 op=7 SRCH base="cn=accounts,dc=int,dc=apexmw,dc=com" scope=2
filter="(&(objectClass=krbprincipalaux)(krbPrincipalName=pat(a)INT.APEXMW.COM))"
attrs=ALL
conn=178 op=7 RESULT err=0 tag=101 nentries=1 etime=0.0002747849
conn=178 op=8 EXT oid="1.3.6.1.4.1.4203.1.11.3" name="whoami-plugin"
conn=178 op=8 RESULT err=0 tag=120 nentries=0 etime=0.0000135034
conn=178 op=9 SRCH base="cn=request certificate ignore caacl,cn=virtual
operations,cn=etc,dc=int,dc=apexmw,dc=com" scope=0 filter="(objectClass=*)"
attrs="objectClass"
conn=178 op=9 RESULT err=0 tag=101 nentries=1 etime=0.0000932668 - entryLevelRights: none
conn=178 op=10 SRCH base="uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com"
scope=0 filter="(objectClass=*)" attrs="distinguishedName"
conn=178 op=10 RESULT err=0 tag=101 nentries=1 etime=0.0000640289
conn=178 op=11 SRCH base="uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com"
scope=0 filter="(objectClass=*)" attrs="telephoneNumber ipaSshPubKey uid
krbCanonicalName ipatokenRadiusUserName ipaUserAuthType krbPrincipalExpiration
homeDirectory nsAccountLock usercertificate;binary title loginShell uidNumber mail
ipaCertMapData memberOf memberofindirect krbPrincipalName givenName gidNumber sn ou
userClass ipatokenRadiusConfigLink"
conn=178 op=11 RESULT err=0 tag=101 nentries=1 etime=0.0001401737
conn=178 op=12 SRCH base="dc=int,dc=apexmw,dc=com" scope=2
filter="(|(member=uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com)(memberUser=uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com)(memberHost=uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com))"
attrs=""
conn=178 op=12 RESULT err=0 tag=101 nentries=7 etime=0.0001492344 notes=P pr_idx=0
pr_cookie=-1
conn=178 op=13 SRCH base="uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com"
scope=0 filter="(userPassword=*)" attrs="userPassword"
conn=178 op=13 RESULT err=0 tag=101 nentries=1 etime=0.0000524838
conn=178 op=14 SRCH base="uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com"
scope=0 filter="(krbPrincipalKey=*)" attrs="krbPrincipalKey"
conn=178 op=14 RESULT err=0 tag=101 nentries=1 etime=0.0000597589
conn=178 op=15 SRCH
base="ipaUniqueID=80b23b30-6a0c-11e9-baa3-525400b52c7b,cn=sudorules,cn=sudo,dc=int,dc=apexmw,dc=com"
scope=0 filter="(objectClass=*)" attrs="cn"
conn=178 op=15 RESULT err=0 tag=101 nentries=1 etime=0.0000379744
conn=178 op=16 SRCH
base="ipaUniqueID=5fb3a640-705a-11e9-aa05-525400b52c7b,cn=hbac,dc=int,dc=apexmw,dc=com"
scope=0 filter="(objectClass=*)" attrs="cn"
conn=178 op=16 RESULT err=0 tag=101 nentries=1 etime=0.0000337904
conn=178 op=17 SRCH base="cn=caacls,cn=ca,dc=int,dc=apexmw,dc=com" scope=1
filter="(&(objectClass=ipaassociation)(objectClass=ipacaacl))"
attrs="serviceCategory cn ipaMemberCertProfile ipaMemberCa ipaCertProfileCategory
memberUser userCategory hostCategory memberHost ipaEnabledFlag ipaCaCategory memberService
description"
conn=178 op=17 RESULT err=0 tag=101 nentries=2 etime=0.0001647058
conn=178 op=18 EXT oid="1.3.6.1.4.1.4203.1.11.3" name="whoami-plugin"
conn=178 op=18 RESULT err=0 tag=120 nentries=0 etime=0.0000138321
conn=178 op=19 SRCH base="uid=pat,cn=users,cn=accounts,dc=int,dc=apexmw,dc=com"
scope=0 filter="(objectClass=*)" attrs="userCertificate"
conn=178 op=19 RESULT err=0 tag=101 nentries=1 etime=0.0001475052 - entryLevelRights:
none
conn=178 op=20 UNBIND
conn=178 op=20 fd=114 closed - U1
To begin with, I note that this session does a BIND with 'dn=""', right
at the beginning, it's essentially an anonymous bind, yah?
No, it is not. BIND operation may be multi-round and actual result is
not known until all rounds passed through. In your cases, SASL
GSS-SPNEGO is in use, so on the first round you get the bound identity:
conn=178 op=0 BIND dn="" method=sasl version=3 mech=GSS-SPNEGO
conn=178 op=0 RESULT err=0 tag=97 nentries=0 etime=0.0025554208
dn="fqdn=apex-openvpn.int.apexmw.com,cn=computers,cn=accounts,dc=int,dc=apexmw,dc=com"
E.g. all connection was done using host keytab of that machine
(apex-openvpn.int....). This is how certmonger works -- it uses a host
keytab of the machine it runs on.
That operation near the end, here:
op=17 SRCH base="cn=caacls,cn=ca,dc=int,dc=apexmw,dc=com" scope=1
filter="(&(objectClass=ipaassociation)(objectClass=ipacaacl))"
seems like it might be kinda key. and indeed, if I attempt to run this
by hand as an anonymous bind, I get no results:
root@apex-freeipa slapd-INT-APEXMW-COM# ldapsearch -x -h localhost -b
dc=int,dc=apexmw,dc=com -s sub
"(|(objectClass=ipaassociation)(objectClass=ipacaacl))"
# extended LDIF
#
# LDAPv3
# base <dc=int,dc=apexmw,dc=com> with scope subtree
# filter: (|(objectClass=ipaassociation)(objectClass=ipacaacl))
# requesting: ALL
#
# search result
search: 2
result: 0 Success
# numResponses: 1
It's only if I run this as an _authenticated_ bind, that I can find my ACL:
root@apex-freeipa slapd-INT-APEXMW-COM# ldapsearch -x -D "cn=Directory Manager"
-W -h localhost -b dc=int,dc=apexmw,dc=com -s sub
"(&(objectClass=ipaassociation)(objectClass=ipacaacl))" cn
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=int,dc=apexmw,dc=com> with scope subtree
# filter: (&(objectClass=ipaassociation)(objectClass=ipacaacl))
# requesting: cn
#
# c98b740c-6903-11e9-ad1b-525400b52c7b, caacls, ca,
int.apexmw.com
dn: ipaUniqueID=c98b740c-6903-11e9-ad1b-525400b52c7b,cn=caacls,cn=ca,dc=int,dc
=apexmw,dc=com
cn: hosts_services_caIPAserviceCert
# 6dde33a6-7849-11e9-aa05-525400b52c7b, caacls, ca,
int.apexmw.com
dn: ipaUniqueID=6dde33a6-7849-11e9-aa05-525400b52c7b,cn=caacls,cn=ca,dc=int,dc
=apexmw,dc=com
cn: OpenVPN_User_Certificate_ACL
# search result
search: 2
result: 0 Success
# numResponses: 3
# numEntries: 2
Is this (using certmonger to auto-issue signed certs/keys for my
openvpn users) going to be essentially impossible to do, here? Do I
need to go a more traditional route of creating a seperate
keystore/certdb, issuing a CSR, and feeding that to FreeIPA to sign?
You'd need
to create an ACL that would allow a host identity that
certmonger uses to have write rights to the userCertificate attribute of
the target user. You are already successfully passed CA ACL check
because the framework tried to see if you have rights to actually write
the resulting certificate (public cert) to the userCertficiate attribute
of the target entry, so it was not a question whether you can issue
(yes, you can) but whether you can store the cert (you cannot).
A way to create that would be by utilizing permissions/roles system of FreeIPA.
Something like this:
ipa permission-add write-user-certificate-permission \
--right=write --attrs=userCertificate --type=user
ipa privilege-add write-user-certificate-privilege
ipa privilege-add-permission write-user-certificate-privilege \
--permissions=write-user-certificate-permission
ipa role-add user-certificate-issuer
ipa role-add-privilege user-certificate-issuer \
--privileges=write-user-certificate-privilege
ipa role-add-member user-certificate-issuer \
--hosts=apex-openvpn
--
/ Alexander Bokovoy
Sr. Principal Software Engineer
Security / Identity Management Engineering
Red Hat Limited, Finland