Skip to content
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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

sanposhiho
Copy link
Contributor

@sanposhiho sanposhiho commented Dec 31, 2024

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

// are allowed. This provides very limited but simple authorization.
//
// +optional
AllowedClients []string `json:"allowedClients,omitempty"`
Copy link
Member

@zhaohuabing zhaohuabing Dec 31, 2024

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?

Copy link
Contributor Author

@sanposhiho sanposhiho Dec 31, 2024

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:

  1. the gateway has API Key Auth configured globally. e.g., Credentials has admin: key1 and user: key2.
  2. the route also has per-route API Key Auth, and you can define this route is only for admin with AllowedClients.

Copy link
Member

@zhaohuabing zhaohuabing Dec 31, 2024

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: 
    ...

Copy link
Contributor Author

@sanposhiho sanposhiho Dec 31, 2024

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?

Copy link
Member

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.

Copy link
Contributor

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

  1. 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

  2. my vote would be to make it look more like basicAuth https://gateway.envoyproxy.io/docs/api/extension_types/#basicauth
    with one field credentials / keyRefs that can hold a list of secrets

  3. we need to specify what the secret key and value should look like

Copy link
Contributor Author

@sanposhiho sanposhiho Jan 5, 2025

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.

Copy link
Contributor

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

Copy link
Contributor

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 {

@sanposhiho sanposhiho force-pushed the api-key-auth branch 2 times, most recently from 999022c to 46d4f6a Compare January 1, 2025 06:59
@sanposhiho sanposhiho marked this pull request as ready for review January 1, 2025 06:59
@sanposhiho sanposhiho requested a review from a team as a code owner January 1, 2025 06:59
@sanposhiho
Copy link
Contributor Author

E2e test passed on my local and opened the PR for the review.

@@ -57,9 +57,28 @@ func validateSecurityPolicySpec(spec *egv1a1.SecurityPolicySpec) error {
errs = append(errs, err)
}

if err := ValidateAPIKeyAuth(spec.APIKeyAuth); err != nil {
Copy link
Contributor Author

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]>
Signed-off-by: Kensei Nakada <[email protected]>

// 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"`
Copy link
Contributor

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

Copy link
Contributor Author

@sanposhiho sanposhiho Jan 3, 2025

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?

Copy link
Contributor

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

Copy link
Contributor Author

@sanposhiho sanposhiho Jan 5, 2025

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.

// 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"`
Copy link
Contributor

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.
Copy link
Contributor

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"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer QueryParams here

// 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.
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support API Key Auth
3 participants