-
Notifications
You must be signed in to change notification settings - Fork 365
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
feat: implement API Key Auth security policy #4986
base: main
Are you sure you want to change the base?
Conversation
7da88c9
to
ab9eb56
Compare
// are allowed. This provides very limited but simple authorization. | ||
// | ||
// +optional | ||
AllowedClients []string `json:"allowedClients,omitempty"` |
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.
Are there any use cases where an API key is authenticated but not allowed to access the targeted Route/Gateway?
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.
A use case I could come up is:
- the gateway has API Key Auth configured globally. e.g.,
Credentials
hasadmin: key1
anduser: key2
. - the route also has per-route API Key Auth, and you can define this route is only for
admin
withAllowedClients
.
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.
In such case, this route should only has admin: key1 credential attached. The HTTPRoute level SP can't use a "global" Credential at the Gateway. This is a design decision of EG that the more specific policy should override the higher level of policy, rather than merging with it.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
name: admin-route-sp
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: admin-route
apiKeyAuth:
credentials:
name: secret-with-admin-key
keySources:
...
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 see, I missed that point.
But, I'm not sure if it's the best interface for users that have to prepare secret per access group. Also, the same key for each client would be spread across different secrets.
So, I still see the benefit of AllowedClients there; users can have only one secret as a source of keys and they can select which key(s) is enabled on certain routes with AllowedClients.
What do you think?
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.
users can have only one secret as a source of keys and they can select which key(s) is enabled on certain routes with AllowedClients.
I think your argument kind of makes sense 🙂, but I’d prefer not to include the AllowedClients field in the API for the following reasons:
- Increased Complexity: Adding another field for users to configure complicates the API, making it less intuitive.
- Potential Security Risk: The shared secret may include API keys not authorized to access an HTTPRoute. This poses a security risk, as the owner of an HTTPRoute might inadvertently or intentionally add such keys to AllowedClients.
@envoyproxy/gateway-maintainers please weigh in.
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.
First of all, if a bad actor has an access to HTTPRoute or associated SecurityPolicy, they can just disable APIKeyAuth to allow access for unintended clients
-
according to EG's design, if the app dev service account can create a SecurityPolicy in the dev namespace where the HTTPRoute also lives, they should be able to override the auth, we are relying on users to set up kube RBAC based on their use case
-
my vote would be to make it look more like
basicAuth
https://gateway.envoyproxy.io/docs/api/extension_types/#basicauth
with one fieldcredentials
/keyRefs
that can hold a list of secrets -
we need to specify what the secret
key
andvalue
should look like
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.
Sorry, so what's your suggestion then, whether removing AllowedClients
vs not?
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
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.
yeah my vote is to rm it, I'll bring this API up in the community meeting tomorrow, to hear more thoughts from the community
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.
@zhaohuabing and I discussed this in the community meeting today and we prefer if AllowedClients
was removed from the initial API. In the future if we want to do API key based Authz, we could enhance then authorization API
type Principal struct { |
999022c
to
46d4f6a
Compare
E2e test passed on my local and opened the PR for the review. |
Signed-off-by: Kensei Nakada <[email protected]>
46d4f6a
to
47fed4a
Compare
@@ -57,9 +57,28 @@ func validateSecurityPolicySpec(spec *egv1a1.SecurityPolicySpec) error { | |||
errs = append(errs, err) | |||
} | |||
|
|||
if err := ValidateAPIKeyAuth(spec.APIKeyAuth); err != nil { |
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.
There's a bug that ValidateSecurityPolicy
isn't called at all currently.
I sent another PR to patch that, #4987.
Signed-off-by: Kensei Nakada <[email protected]>
d7139e3
to
eabdd76
Compare
Signed-off-by: Kensei Nakada <[email protected]>
api/v1alpha1/api_key_auth_types.go
Outdated
|
||
// KeySources is where to fetch the key from the coming request. | ||
// The value from the first source that has a key will be used. | ||
KeySources []*KeySource `json:"keySources"` |
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 like OpenAPI's definiton which uses In
https://swagger.io/docs/specification/v3_0/authentication/api-keys/
in:
type: Header
name: "x-api-id"
another existing design pattern
the jwt
field uses extractFrom
https://gateway.envoyproxy.io/docs/api/extension_types/#jwtprovider
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.
Well, generally speaking, I do prefer the current form in this kind of case at API design, especially for better future extensibility; it allows us to add a different structure later, e.g., let's say envoy supports a new key source fetching key from body (which Kong already supports), it might be like:
type KeySource struct {
Header *string
Query *string
Cookie *string
Body *BodyKeySource
}
type BodyKeySource struct {
BodyType string // yaml or json.
Field string // where we get API Key from.
}
Also, extractFrom
actually looks like very similar to KeySource
here, are you suggesting renaming it from KeySources to ExtractFrom?
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.
yeah suggesting we pick a name thats already used in the API / or industry and also try and use a union discriminator i.e.
extractFrom:
type: Header
header: <val>
e.g. https://gateway.envoyproxy.io/docs/api/extension_types/#ratelimitspec
https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/1027-api-unions/README.md
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.
Renamed from keySources
to extractFrom
.
Signed-off-by: Kensei Nakada <[email protected]>
// This is an Opaque secret. | ||
// Each API key is stored in the key representing the client id, | ||
// which can be used in AllowedClients to authorize the client in a simple way. | ||
Credentials gwapiv1.SecretObjectReference `json:"credentials"` |
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.
prefer CredentialRefs []gwapiv1.SecretObjectReference
we can start and only support a single entry in the list, but it helps us leave room for expansion
// +kubebuilder:validation:XValidation:rule="(has(self.header) || has(self.query) || has(self.cookie))",message="one of header, query or cookie must be specified" | ||
type ExtractFrom struct { | ||
// Header is the name of the header to fetch the key from. | ||
// This field is optional, but only one of header, query or cookie is supposed to be specified. |
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.
needs a Type
field - Header
, QueryParams
, Cookie
// This field is optional, but only one of header, query or cookie is supposed to be specified. | ||
// | ||
// +optional | ||
Query *string `json:"query,omitempty"` |
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.
prefer QueryParams
here
- envoy calls it
parameters
in some places andqueryParameters
in some places, - openAPI calls it query
https://swagger.io/docs/specification/v3_0/describing-parameters/#query-parameters - gateway api uses
queryParams
https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2fv1.HTTPRouteMatch
// APIKeyAuth defines the configuration for the API Key Authentication. | ||
type APIKeyAuth struct { | ||
// Credentials is the Kubernetes secret which contains the API keys. | ||
// This is an Opaque secret. |
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.
we should highlight the format of the Secret
here , its a map of key - value pairs, is the map key the API Key
and the map value the Client ID
or vice versa
What type of PR is this?
What this PR does / why we need it:
implement API Key Auth security policy.
Which issue(s) this PR fixes:
Fixes #2630
Release Notes: Yes