Domain Join - Machine Accounts
There are a number of features we have been considering that will require us to finally give in and support machine accounts also know as domain joining.
Feature Requirements
Limiting Unix Password Auth
Currently unix password authentication is targetted as the method for sudo. Initial access to the machine should come from ssh keys (and in future, ctap2).
In order to maintain compatability with LDAP style authentication, we allow "anonymous hosts" to retrieve ssh public keys, and then perform sudo authentication.
This has the obvious caveat that anyone can stand up a machine that trusts a Kanidm instance. This presents a double edged sword:
- By configuring a machine to authenticate via Kanidm, there is full trust in the authentication decisions Kanidm makes.
- Users of Kanidm may be tricked into accessing a machine that is not managed by their IT or other central authority.
To prevent this, UNIX authentication should be configurable to prevent usage from unregistered machines. This will require the machine to present machine authentication credentials simultaneously with the user's credentials.
A potential change is removing the current unix password auth mechanism as a whole. Instead the user's auth token would contain a TPM bound credential that only the domain joined machine's TPM could access and use.
Requesting Cryptographic Credentials
When a user logs in to a machine, it may be required that they can use that authentication to identify themself to other systems. When a user authenticates with credentials such as ssh-keys, it's not possible to use these to request other forwardable credentials - and ssh agent forwarding only allows forwarding of ssh credentials, not other types of credentials that may be needed.
In this case, when a user authenticates with SSH, since they're using a trusted machine, Kanidm can request short-term and limited credentials on the users behalf.
An example is that we could dynamically request TLS certificates or Kerberos credentials.
Normally with ssh in this manner, everything has to use kerberos. This would force users to kinit on their machine to ssh and forward their credentials to the next machine. This causes friction since configuring kerberos on machines is an exercise in frustration, and with BYOD it gets even worse. In addition when using ssh with an ssh key, the only viable kinit mechanism is password or password + totp once the user has logged in. This is because pkcs11 can't be forwarded over ssh, nor can CTAP2, limiting kinit to weaker authentication mechanisms.
Security Considerations
- Anonymous joins should not be allowed or permitted.
- Join tokens need to be revoked (causing related machines to request re-enrollment) or expired (related machines can continue to function)
- Join tokens must be auditable.
- Private keys SHOULD be stored in a TPM, or at least a software HSM with a secured unlock key.
- The use of the private key must prevent replay attacks
Overview
Since the machine would now be an entity requiring authentication, we need to have a process to establish and maintain this trust relationship.
- A join token is created by a user who is authorised to perform domain joins.
- The machine is audited for a known trust state. This process may vary from site to site. A future improvement could be that the join token can only release on certain TPM PCR values.
- The join token is yielded to the Kanidm UNIX daemon which submits its signing key to the Kanidm server.
- The kanidm server verifies the submission and creates a machine account.
- The Kanidm UNIX daemon now uses its signing key to sign a challenge that is submitted with all requests to the kanidm server.
Extra
- Machines should be able to "re-join" with an alternate join token, moving their machine account join token relationship.
- Machines must be able to self-enroll newer keys which may have stronger cryptographic requirements.
Details
Join Token Creation
Join tokens are persisted in the database allowing tracing back to the usage of the token.
Every machine that is joined by that token will related back to that token. This allows auditing of which token was used to join which machine.
Machines may re-enroll with an alternate token.
The join token should be signed. The JWK pub key should be available at a known HTTPS uri so that the client can use it to validate the join token and its content. This may allow policy to be embedded into the join token for the client to self-adhere to in the join process.
Machine Auditing
The machine should be audited to be in a secure state. It's not yet clear how to proceed here, but we should consider using TPM PCRs with secure boot to measure this and validate the machine state.
One possible way to achieve this could be with full disk encryption that is bound to secure boot and TPM PCRs. Kanidm-unixd could validate the same PCR's to start operating. The challenge here would be updates of the expected PCR values during a system update. Alternately, Kanidm could "assume" that if started, then the FDE must have passed and attestation of health "is out of scope" for us.
Public Key Submission
The private key should be generated and stored in a TPM/HSM. If possible, we should also submit attestation of this.
The submission of the public key should prevent replays, and should sign either a nonce or the current time. The current time must be valid to within a number of seconds. The nonce must be created by the server.
The machine must submit its public key, the time value and the signature. This should accompany the join token.
If the signature is valid, and the join token is correct, then the machine is joined and has a machine account created. The machine account is linked to the join token.
Machine Account
The machine account is a new form of account, similar to a service account. It should identify the machine, its hostname, and other properties. It should also contain the machine's public key id.
When the machine requests certain API's from Kanidm, it should submit signed requests that include the current time. The kid is used to find the machine account that is submitting the request. This then validates the identity of the caller, and then allows the action to proceed.