-
Notifications
You must be signed in to change notification settings - Fork 436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement a KubernetesSecretsRepository #4462
Conversation
2a7c57f
to
d057d11
Compare
This looks good. Only question (not urgent or blocking, more for docs) is clarifcation on the two notes:
I imagine most people likely aren't or won't be accessing runtime APIs here so imagine for vast majority this is more an FYI? I'm not even sure we document the key APIs for the runtime in Azure but wanted to make sure
Do you have an example of what allowing the pod identity to read and modify a secret is? Is this a Kubernetes RBAC identity thing? |
Yes, I'd imagine most kubernetes users would be generating their keys and creating that secret on deployment or something like that, then just use volumeMount. Though all of the runtime APIs assume that key management will go through the runtime APIs itself. So there is a bit of a disconnect there. We sorta document the keys APIs but not too much I guess. It's hard to judge their usage though because the UX uses them heavily.
Yes, exactly. # Create an identity to use for your pod
# Alternativly you can just set the RoleBinding below for
# the `default` identity for the namespace, though all pods
# in this namespace will have access to the secret.
apiVersion: v1
kind: ServiceAccount
metadata:
name: function-identity
---
# Create a secret-manager role
# Change the resource to `configmaps` if you wanna use
# ConfigMap instead of a Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secrets-manager
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
# Create a RoleBinding between secrets-manager <=> function-identity
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: function-identity-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: secrets-manager
subjects:
- kind: ServiceAccount
name: function-identity
--- Then you can give that identity to your pod using this in your deployment object apiVersion: apps/v1
kind: Deployment
metadata:
...
spec:
...
spec:
+ serviceAccountName: function-identity
containers:
.... /cc @anirudhgarg for FYI as well. |
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
} | ||
} | ||
|
||
return hostSecrets?.MasterKey == null ? null : hostSecrets; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might be a bug in KeyVaultSecretsRepository
here as I was looking at it as an example to make this one.
SecretsManager assumes that if ReadAsync
returned a non-null object for HostSecrets
, then a _master
key must exist. If this is the first time to use this repository on a brand new Secret (and I would assume KeyVault too, though I didn't test KeyVault) and you return a non-null HostSecrets
without a _master
key in it, all Admin operations will fail with a 500
/cc @alrod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this does look like a bug.
src/WebJobs.Script.WebHost/Security/KeyManagement/KubernetesSecretsRepository.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a quick look and this looks good! Mostly nit comments and a couple of questions at this point.
src/WebJobs.Script.WebHost/Security/KeyManagement/IKubernetesClient.cs
Outdated
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Outdated
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Outdated
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Outdated
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Outdated
Show resolved
Hide resolved
src/WebJobs.Script.WebHost/Security/KeyManagement/SimpleKubernetesClient.cs
Outdated
Show resolved
Hide resolved
|
||
private static async Task<IDictionary<string, string>> GetFromFiles(string path) | ||
{ | ||
var files = await FileUtility.GetFilesAsync(path, "*"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: var
-> string
(for files)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the rule you use for var
vs typeName?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key thing (for me) is whether or not the type is evident.
If the variable is being assigned from a method call, (e.g. var foo = SomeMethod()
), I opt to use the type instead of var
as that significantly improves readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't spend a lot of time this time around, but didn't have any major concerns from the first review.
4cc9991
to
53f7390
Compare
53f7390
to
30fa09f
Compare
Was this included in the |
Yes |
Hey @ahmelsayed, should these kubernetes keys also work for authorizing a specific function call? I'm getting a 401 no matter how I try to configure this. Here is what I have: I've configured a secret basically based on the examplke above
I know that my my pod is able to access this secret because my host api calls work and the repository is able to generate a new code for my specific function. Using the api to get the key for the function works correctly with the host.master key I have defined in the secret. If I send a get request to
I looked in the secret in kubernetes and the value above does match. Next I try to make a call to my function using I apologize for adding this comment here instead of an issue but I thought this would be the best place since I was testing this specific functionality. Thanks for adding this functionality!! I was looking for something like this for a few weeks |
I think for eventgrid you need a You'll need to create it first though. You can either add it to your Kubernetes host.systemkey.eventgrid_extension: # base64 encoded value or if you wanna use the Host APIs, then try: # First create the key using:
# POST https://my-api-url/admin/host/systemkeys/eventgrid_extension?code={masterkey}
curl -d '' https://my-api-url/admin/host/systemkeys/eventgrid_extension?code={masterkey}
# The create action above will return the key. But you can do a GET on it Then try using that key. If it still doesn't work, I'll need to cc someone more familiar with eventgrid than me. |
actually I don't think you have to name it |
Also thank you for giving it a try. I still need to update the core-tools to allow for creating and managing keys for kubernetes deployment, so it's a bit easier to use. But it's great to see someone put the pieces together and use it already :) |
I was able to create the system key and get a value returned but unfortunately that did not work either. If I try to make a GET to It's also interesting because if I make a GET request to
|
Got it to work! There seems to be a small bug after the systemKey is created where even though it is added to the kubernetes secret, the collection of systemKeys in memory is not updated. Killing the pod and restarting it fixes the issue. |
Just to double check, are you creating a service account and using a read/write secret or mounting the secret as a volume? |
I'm using a service account with a read/write secret. |
Is there anyway to tell the function app to look at another path other than /run/secrets/function-keys? |
This adds a
KubernetesSecretsRepository
implementation ofISecretsRepository
. I'm still working on adding some tests, but the functionality is done. I have few questions that I'll add as comments.With this PR the user can set
AzureWebJobsSecretStorageType=kubernetes
Then optionally you can set
AzureWebJobsKubernetesSecretName
to a kubernetes secret name in the same namespace as the deployment. If you don't set that, then we expect a secret to be volumeMounted into/run/secrets/functions-keys
Note: when using
volumeMount
, the secrets are readonly and can't be updated by the runtime APIs. When using aAzureWebJobsKubernetesSecretName
with a kubernetes secret (or configMap), it's read/write.Note: To use
AzureWebJobsKubernetesSecretName
, you need to allow the functions pod identity to be able to read and modify that secret.the secret in kubernetes
if you set
AzureWebJobsKubernetesSecretName
toconfigmaps/{name}
it'll use configmaps instead which doesn't require base64 enodingIf you don't want to create an identity for the function pod, you can mount the sam secret or configmap using this
and don't set
AzureWebJobsKubernetesSecretName
./cc @jeffhollan @paulbatum @mattchenderson