Closed izgeri closed 4 years ago
Possible flow (not 100% sure this is correct):
+-----+ +-----+ +---------+
| App | | K8s | | Conjur |
+-----+ +-----+ +---------+
| | |
| Inject client certificate request | |
|---------------------------------------------------------------------------------------------------------------->|
| | |
| | Validate pod exists in namespace |
| |<----------------------------------------------------------|
| | |
| | Validate application identity resource exists in K8s |
| |<----------------------------------------------------------|
| | |
| | Error (if one occurred) |
|<----------------------------------------------------------------------------------------------------------------|
| | |
| | Run "kubectl exec" to install the signed certificate |
| |<----------------------------------------------------------|
| | |
| Install the signed Conjur certificate in the pod | |
|<----------------------------------------------------| |
| | |
| | Error (if one occurred) |
| |---------------------------------------------------------->|
| | |
| Authenticate request | |
|---------------------------------------------------------------------------------------------------------------->|
| | |
| | Validate pod exists in namespace |
| |<----------------------------------------------------------|
| | |
| | Validate application identity resource exists in K8s |
| |<----------------------------------------------------------|
| | |
| | Return Conjur access token |
|<----------------------------------------------------------------------------------------------------------------|
| | |
this was generated at https://textart.io/sequence using the source:
object App K8s Conjur
App->Conjur: Inject client certificate request
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resource exists in K8s
Conjur->App: Error (if one occurred)
Conjur->K8s: Run "kubectl exec" to install the signed certificate
K8s->App: Install the signed Conjur certificate in the pod
K8s->Conjur: Error (if one occurred)
App->Conjur: Authenticate request
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resource exists in K8s
Conjur->App: Return Conjur access token
@izgeri this looks good! I would add a little bit more details that I think are relevant for someone that learns about this. Let me know what you think.
object App K8s Conjur
App->App: Generate CSR
App->Conjur: Inject client certificate request (with CSR)
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resource exists in K8s
Conjur->App: Error (if one occurred)
Conjur->Conjur: Sign certificate
Conjur->K8s: Run kube client "exec" to install the signed certificate
K8s->App: Install the signed Conjur certificate in the pod
K8s->Conjur: Error (if one occurred)
Conjur->App: Certificate injection result (either success or failure)
App->Conjur: Authenticate request (mutual TLS using signed certificate)
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resources exist in K8s
Conjur->App: Return Conjur access token
The changes I made:
kubectl
CLI but a client SDKauthenticate
call creates an mTLS communicationOutcome:
+-----+ +-----+ +---------+
| App | | K8s | | Conjur |
+-----+ +-----+ +---------+
| | |
| Generate CSR | |
|------------- | |
| | | |
|<------------ | |
| | |
| Inject client certificate request (with CSR) | |
|-------------------------------------------------------------------------------------------------------------------->|
| | |
| | Validate pod exists in namespace |
| |<--------------------------------------------------------------|
| | |
| | Validate application identity resource exists in K8s |
| |<--------------------------------------------------------------|
| | |
| | Error (if one occurred) |
|<--------------------------------------------------------------------------------------------------------------------|
| | |
| | | Sign certificate
| | |-----------------
| | | |
| | |<----------------
| | |
| | Run kube client "exec" to install the signed certificate |
| |<--------------------------------------------------------------|
| | |
| Install the signed Conjur certificate in the pod | |
|<----------------------------------------------------| |
| | |
| | Error (if one occurred) |
| |-------------------------------------------------------------->|
| | |
| | Certificate injection result (either success or failure) |
|<--------------------------------------------------------------------------------------------------------------------|
| | |
| Authenticate request (mutual TLS using signed certificate) |
|-------------------------------------------------------------------------------------------------------------------->|
| | |
| | Validate pod exists in namespace |
| |<--------------------------------------------------------------|
| | |
| | Validate application identity resources exist in K8s |
| |<--------------------------------------------------------------|
| | |
| | Return Conjur access token |
|<--------------------------------------------------------------------------------------------------------------------|
| | |
Thanks @izgeri for getting this. I actually didn't know about this tool and it's a cool one.
I won't merge my suggestions with Rafi's so it is easier to understand the diff from the original.
I have this diagram:
+-----+ +-----+ +---------+
| App | | K8s | | Conjur |
+-----+ +-----+ +---------+
| | |
| Inject client certificate request (including Conjur host ID) |
|---------------------------------------------------------------------------------------------------------------------------------->|
| | |
| | | Validate K8s Authenticator webservice exists, Conjur host exists is permitted on the webservice
| | |------------------------------------------------------------------------------------------------
| | | |
| | |<-----------------------------------------------------------------------------------------------
| | |
| | Validate pod exists in namespace |
| |<----------------------------------------------------------------------------|
| | |
| | Validate Conjur host can authenticate from the requesting K8s resource |
| |<----------------------------------------------------------------------------|
| | |
| | Error (if one occurred) |
|<----------------------------------------------------------------------------------------------------------------------------------|
| | |
| | Run "kubectl exec" to install the signed certificate |
| |<----------------------------------------------------------------------------|
| | |
| Install the signed Conjur certificate in the pod | |
|<----------------------------------------------------| |
| | |
| | Error (if one occurred) |
| |---------------------------------------------------------------------------->|
| | |
| Authenticate request (including Conjur host ID and the Conjur certificate) |
|---------------------------------------------------------------------------------------------------------------------------------->|
| | |
| | | Validate K8s Authenticator webservice exists, Conjur host exists is permitted on the webservice
| | |------------------------------------------------------------------------------------------------
| | | |
| | |<-----------------------------------------------------------------------------------------------
| | |
| | Validate pod exists in namespace |
| |<----------------------------------------------------------------------------|
| | |
| | Validate Conjur host can authenticate from the requesting K8s resource |
| |<----------------------------------------------------------------------------|
| | |
| | Return Conjur access token |
|<----------------------------------------------------------------------------------------------------------------------------------|
| | |
I used these steps:
object App K8s Conjur
App->Conjur: Inject client certificate request (including Conjur host ID)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
Conjur->App: Error (if one occurred)
Conjur->K8s: Run "kubectl exec" to install the signed certificate
K8s->App: Install the signed Conjur certificate in the pod
K8s->Conjur: Error (if one occurred)
App->Conjur: Authenticate request (including Conjur host ID and the Conjur certificate)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
Conjur->App: Return Conjur access token
What i added:
btw, i would add this diagram also to the K8s authenticator client. what do you think?
That's a great idea @orenbm but I suggest keeping one source of truth, so I'd recommend that the authn client would have a link pointing to this diagram located under Conjur.
@rafis3 @orenbm - if I look at your two proposals and try to justify them, here is what I come up with. let me know what you think. if it looks good I'll put up a PR to add it (and a link in the authn-k8s client, too)
sequence definition:
object App K8s Conjur
App->App: Generate certificate signing request (CSR)
App->Conjur: Send inject client certificate request (including Conjur host ID)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists, Conjur host is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resource exists in K8s
Conjur->App: Error (if one occurred)
Conjur->Conjur: Sign certificate
Conjur->K8s: Run kube client "exec" to install the signed certificate
K8s->App: Install the signed Conjur certificate in the pod
K8s->Conjur: Error (if one occurred)
App->Conjur: Authenticate request (including Conjur host ID, mutual TLS using signed certificate)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists, Conjur host is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate application identity resource exists in K8s
Conjur->App: Return Conjur access token
@rafis3 you had replaced
Conjur->K8s: Validate application identity resource exists in K8s
with
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
But I think the former is clearer, and more in line with what it's actually doing based on my review of the code. But maybe you understand it differently - can you clarify why you made this change?
@orenbm you had added
Conjur->App: Certificate injection result (either success or failure)
after the certificate injection, but I don't think this is accurate. I think this is captured by the line a few steps above which says:
Conjur->App: Error (if one occurred)
Conjur sends the error back to the app if it's unable to validate the request, then proceeds asynchronously with the certificate injection. It logs an error if the cert injection fails, but I don't think it sends any further info to the app. The app can inspect and determine that the certificate has not arrived, and kick off another login attempt if desired. Does this make sense?
@izgeri you switched between our suggestions, @rafis3 please note.
I made this change (application identity) because we don't verify that an application identity resource exists in K8s
. I don;t blame you from thinking that, and I am in the middle of a refactor to the application identity area because it is not clear (i learned that recently).
I changed the class name from ApplicationIdentity
to ResourceRestrictions
and wrote the following comment above the class definition:
# This class represents the restrictions that are set on a Conjur host regarding
# the K8s resources that it can authenticate with Conjur from.
# It consists a list of K8sResource objects which represent the resource
# restriction that need to be met in an authentication request.
#
# For example, if `resources` includes the K8sResource:
# - type: "namespace"
# - value: "some-namespace"
#
# then this Conjur host can authenticate with Conjur only from a pod that is
# part of the namespace "some-namespace"
The restrictions are set either in the host id or in the host annotations. Is it clearer now what we do in this step? Or maybe you understood it also before?
How would you phrase the line in the diagram knowing the above?
@izgeri regarding my comments:
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
I meant to say that the caller pod is checked based on the app restrictions, as @orenbm described. We can change the wording. But I was also wrong, should have looked again in the code before writing it. This part is done in the authenticate
step, not in the client cert injection.
Conjur->App: Certificate injection result (either success or failure)
The authn client communicates with the Conjur REST API, while the certificate should be found asynchronously on the filesystem, the REST API returns a result so that the code can continue.
This part is done in the authenticate step, not in the client cert injection.
it happens in both steps.
@orenbm yeah now I see it, thanks for pointing that out, I missed it in validate
. I think it's redundant, we don't need to check the correlation between the host expected attributes and the actual in both APIs. The reason for injecting a certificate is to provide an identity to the calling pod, but validating the identity should be done in authenticate
. Makes sense?
So why even send a host id in the inject cert request? As long as we send a host id i think we need to verify it.
@orenbm I want to wrap this up, and I think my last iteration is almost good enough, except for your one comment about "application identity".
I think we are both saying the same thing, and neither line is quite clear enough.
my line:
Conjur->K8s: Validate application identity resource exists in K8s
your line:
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
proposed alternative:
Conjur->K8s: Validate requesting K8s resource actually exists in K8s
The crux of it is that the request comes with an application identity that lists a certain k8s resource type / name. We are checking that the given k8s resource type / name actually exists in the cluster. Does this proposed alternative make it clearer? Do you have a counter proposal?
the thing is that your line Validate requesting K8s resource actually exists in K8s
is not what happens. we don't just verify that the requesting K8s resource (extracted from the request) exists but that the requesting Conjur host can authenticate from the requesting k8s resource.
For example, we have a Conjur host that is defined as follows:
- !host
id: some-app
annotations:
authn-k8s/namespace: some-namespace
and we get an authentication request with the spiffe-id spiffe://cluster.local/namespace/some-other-namespace/podname/some-pod
and the Conjur host-id some-app
.
In this case the requesting K8s resource is the pod some-pod
that is in the namespace some-other-namespace
. It actually exists in k8s but the application identity validation will fail. It will fail because the requesting Conjur host (some-app
) can authenticate with Conjur only from the namespace some-namespace
. So although the requesting k8s resource actually exist in k8s, the conjur host is not allowed to authenticate from it (similar to CIDR restrictions with the restricted_to
field).
That's why i think that Validate Conjur host can authenticate from the requesting K8s resource
is more accurate.
btw, i'm in progress of changing ApplicationIdentity
to ResourceRestrictions
which i think will be more understandable.
ok - so if I write the flow as:
object App K8s Conjur
App->App: Generate certificate signing request (CSR)
App->Conjur: Send inject client certificate request (including Conjur host ID)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists, Conjur host is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
Conjur->App: Error (if one occurred)
Conjur->Conjur: Sign certificate
Conjur->K8s: Run kube client "exec" to install the signed certificate
K8s->App: Install the signed Conjur certificate in the pod
K8s->Conjur: Error (if one occurred)
App->Conjur: Authenticate request (including Conjur host ID, mutual TLS using signed certificate)
Conjur->Conjur: Validate K8s Authenticator webservice exists, Conjur host exists, Conjur host is permitted on the webservice
Conjur->K8s: Validate pod exists in namespace
Conjur->K8s: Validate Conjur host can authenticate from the requesting K8s resource
Conjur->App: Return Conjur access token
does this look good? can I put up some PRs to add this to our code? @rafis3 @orenbm
this is actually my bad but i think that having this text Validate Conjur host can authenticate from the requesting K8s resource
with a line from Conjur to K8s is a bit weird. Maybe let's change it to Validate the requesting K8s resource applies to the Conjur host's authentication restrictions
.
I am ok with whichever decision you take if you think it's ok.
other than that, i am ok for a PR. thanks!
does it help to know that I meant this line to refer to this check? https://github.com/cyberark/conjur/blob/807d5d912694d848e734ea1b4e732f85dcfae9be/app/domain/authentication/authn_k8s/validate_application_identity.rb#L106
which uses the k8s API to verify that a resource of the given type and name exists in the given namespace?
Regarding the amendment @orenbm suggested. I think both options sound good and clear.
Regarding this sentence:
Validate K8s Authenticator webservice exists, Conjur host exists, Conjur host is permitted on the webservice
I think it should be permitted to
instead of on
.
Other than these small comments, all looks good to me.
@orenbm don't we have a flow diagram for authn-k8s in this repo now? can you please add a link to the file here so we can close the issue?
it might be nice to make sure flow diagrams are linked from CONTRIBUTING too - they're super handy for new devs to understand the project.
thanks @izgeri , the diagram can be found here. care to add the links in the places you think they should go to?
I think it should go in CONTRIBUTING.md, so that contributors can find that reference if they're going to work on authn-k8s.
I think you could make a case for updating this project's style guide, too, now that we have the style guide for Ruby in the community repo - I will leave both for the people actually doing the development work on this project :)
It can be hard for newcomers to understand the authn-k8s flow, so it might be useful to add an ascii flow diagram (like we've done with Secretless here, for example) to the top of the main authn-k8s file (eg this one).