On pe, 13 touko 2022, Sam Morris via FreeIPA-users wrote:
I'm looking into using https://github.com/guilhem/freeipa-issuer to request certificates from FreeIPA on behalf of a (FreeIPA) service.
The project authenticates to the FreeIPA API with a specified username and password: https://github.com/guilhem/freeipa-issuer/blob/174d145616a672b09d3fdb56b2dd7c93612e483e/provisionners/freeipa.go#L38
I presume this means that it's only possible for it to authenticate to the FreeIPA API as a user, as opposed to a host or service.
Not correct. You can authenticate with any Kerberos principal. Your rights would be limited to what that object is allowed to do and this can be adjusted with permissions/privileges/roles: https://vda.li/en/posts/2016/08/30/Creating-permissions-in-FreeIPA/
We support: - adding users and groups to roles (since ... FreeIPA 2?) - allow services as group members (https://pagure.io/freeipa/issue/7513, since 4.7.0) - adding user ID overrides from the Default Trust View as group and role members (https://pagure.io/freeipa/issue/7255, since 4.9.0)
Host principal by default has rights to manage own services. We use this a lot in different scenarios, an example can be seen in my recent update to FreeIPA workshop where in chapter 12 we set up Keycloak on IPA client and issue certificates for it, managed by the certmonger with host principal: https://freeipa.readthedocs.io/en/latest/workshop/12-external-idp-support.ht...
That being the case, I am trying to lock things down as much as possible, so that the user is only able to request certificates for a single service.
I've had a read through Fraiser's excellent blog post https://frasertweedale.github.io/blog-redhat/posts/2015-09-02-freeipa-cert-issuance-delegation.html which points me towards creating a CA ACL, which I've done.
The CA ACL links together the user, the service and for good measure I specified the CA and the profile too. But it's not sufficient to allow a certificate request to work, as when the issuer tries to ask for the certificate:
Fail to request certificate: ACIError (2100): Insufficient access: not allowed to perform operations: request certificate
Returning to the blog post, I gather I additionally need to grant the following two permissions to the user:
- 'Request Certificate'
- 'System: Modify Services'
What I'd like to understand is the scope of these permissions.
Does 'Request certificate' merely unlock the ability to make requests that are themselves constrained by CA ACLs? That being the case, this permission alone doesn't let the user request certificates for any other hosts or services, right?
As for 'System: Modify Services': I guess granting this permission will allow the user to add certificates to *any* service? In which case, I suppose I need to create a new privilege that allows the usercertificate of a particular entry only to be modified. Are there any examples of this?
I would start on a different side. What is CA ACL? You can think of the CA ACL as an HBAC rule: - CA id is used as a target host - CA profile id is used as a target service name - for user, its groups used to populate list of groups in the rule - for host, its hostgroups used to populate list of groups in the rule - for services, its group membership is also used to populate list of groups in the rule
There is an incomplete implementation, though, that does not take actual service principal into account when evaluating these rules. I need to file a bug about it and fix it. This is not a problem for certmonger because it acts as a host principal which is supported.
The sequence of checks when considering whether a bound principal can request certificates is the following:
- check whether the bound principal is not a asking for itself and it is not a host
- if that's true, then check that the bound principal can write to objectclass attribute of the virtual operation 'Request Certificate':
# Request Certificate virtual op dn: cn=Request Certificate,cn=permissions,cn=pbac,$SUFFIX changetype: add objectClass: top objectClass: groupofnames objectClass: ipapermission cn: Request Certificate member: cn=Certificate Administrators,cn=privileges,cn=pbac,$SUFFIX
dn: $SUFFIX changetype: modify add: aci aci: (targetattr = "objectclass") (target = "ldap:///cn=request certificate,cn=virtual operations,cn=etc,$SUFFIX" ) (version 3.0 ; acl "permission:Request Certificate" ; allow (write) groupdn = "ldap:///cn=Request Certificate,cn=permissions,cn=pbac,$SUFFIX";)
- otherwise, check that it can write to the attribute of the virtual operation 'Request certificate Ignore CAACL', similar to the above, just different pair of permission and ACI, to identify if the bound principal can bypass CA ACLs
- if CA ACLs cannot be bypassed, check CA ACLs
- do validation of the requested properties depending on whether this certificate is asked for user, host, service, krbtgt entry and so on
- check that the bound principal can write to 'userCertificate' attribute of the target LDAP object
- finally, do validation of SAN fields
- (ask CA to issue certificate)
If the bound principal is host, then its rights would be defined by two parts: - passing CA ACL
- ability to write to userCertificate attribute in the target service/host entry
The latter is controlled by the default ACLs in FreeIPA:
# Define which hosts can edit services # The managedby attribute stores the DN of hosts that are allowed to manage # a service. Use service-add-host to add hosts to a service. dn: cn=services,cn=accounts,$SUFFIX changetype: modify add: aci aci: (targetattr="userCertificate || krbPrincipalKey") (version 3.0; acl "Hosts can manage service Certificates and kerberos keys"; allow(write) userattr = "parent[0,1].managedby#USERDN";)
# Allow hosts to update their own certificate in host/ # krbLastPwdChange lets a host unenroll itself dn: cn=computers,cn=accounts,$SUFFIX changetype: modify add: aci aci: (targetattr="usercertificate || krblastpwdchange || description || l || nshostlocation || nshardwareplatform || nsosversion") (version 3.0; acl "Hosts can modify their own certs and keytabs"; allow(write) userdn = "ldap:///self";) aci: (targetattr="ipasshpubkey") (version 3.0; acl "Hosts can modify their own SSH public keys"; allow(write) userdn = "ldap:///self";)
# Define which hosts can edit other hosts # The managedby attribute stores the DN of hosts that are allowed to manage # another host. dn: cn=computers,cn=accounts,$SUFFIX changetype: modify add: aci aci: (targetattr="userCertificate || krbPrincipalKey") (version 3.0; acl "Hosts can manage other host Certificates and kerberos keys"; allow(write) userattr = "parent[0,1].managedby#USERDN";) aci: (targetattr="ipasshpubkey") (version 3.0; acl "Hosts can manage other host SSH public keys"; allow(write) userattr = "parent[0,1].managedby#USERDN";)
So, if you want a host to control which services it can issue certificates for, just make sure they manage those services, through 'ipa service-add-host' command. Services on the same host can do that already because they are allowed to create services for themselves and during that step we add 'managedby' to point to the host-creator.
If you want to limit the host to not issue certificates for some services on itself, add CA ACLs that allow only use of a specific CA/profile/host or service rules. The default rule is like HBAC's allow_all for the default certificate profile for services. Obviously, you need to design rules globally because lack of a rule means denial.