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

generate kuadrant authconfig #37

Merged
merged 8 commits into from
Jan 31, 2022
Merged

generate kuadrant authconfig #37

merged 8 commits into from
Jan 31, 2022

Conversation

eguzki
Copy link
Collaborator

@eguzki eguzki commented Jan 26, 2022

New command to generate AuthConfig objects from your OpenAPI spec.

kuadrantctl generate kuadrant authconfig

Running example:

Using the following OpenAPI spec that contains three operations:

  • one not secured
  • one with apiKey sec requirement
  • one with OIDC sec requirement
---
openapi: "3.1.0"
info:
  title: "Pet Store API"
  version: "1.0.0"
servers:
  - url: https://toplevel.example.io/v1
paths:
  /cat:
    get:  # No sec requirements
      operationId: "getCat"
      responses:
        405:
          description: "invalid input"
    post:  # API key
      operationId: "postCat"
      security:
        - petstore_api_key: []
      responses:
        405:
          description: "invalid input"
  /dog:
    get:  # OIDC
      operationId: "getDog"
      security:
        - petstore_oidc:
          - read:dogs
      responses:
        405:
          description: "invalid input"
components:
  securitySchemes:
    petstore_api_key:
      type: apiKey
      name: api_key
      in: header
    petstore_oidc:
      type: openIdConnect
      openIdConnectUrl: http://example.org/auth/realms/myrealm

The generated AuthConfig would be:

kind: AuthConfig
apiVersion: authorino.kuadrant.io/v1beta1
metadata:
  name: petstoreapi
  creationTimestamp: null
spec:
  hosts:
    - example.io
  identity:
    - name: postCat
      when:
        - selector: context.request.http.path.@extract:{"sep":"/"}
          operator: eq
          value: /cat
        - selector: context.request.http.method.@case:lower
          operator: eq
          value: post
      credentials:
        in: header
        keySelector: api_key
      apiKey:
        labelSelectors:
          app: petstoreapi
          authorino.kuadrant.io/managed-by: authorino
    - name: getDog
      when:
        - selector: context.request.http.path.@extract:{"sep":"/"}
          operator: eq
          value: /dog
        - selector: context.request.http.method.@case:lower
          operator: eq
          value: get
      credentials:
        keySelector: ""
      oidc:
        endpoint: http://example.org/auth/realms/myrealm
status:
  ready: false
  numIdentitySources: 0
  numMetadataSources: 0
  numAuthorizationPolicies: 0
  numResponseItems: 0
  festivalWristbandEnabled: false

@eguzki eguzki requested a review from a team January 26, 2022 13:42
@eguzki eguzki force-pushed the generate-authconfig branch from bfe9238 to e93c618 Compare January 26, 2022 14:41
return authConfig, nil
}

func generateKuadrantAuthConfigMetadata(doc *openapi3.T) ([]*authorinov1beta1.Metadata, error) {

Choose a reason for hiding this comment

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

what are the empty methods for wondering what the metadata concept is?

Copy link
Collaborator Author

@eguzki eguzki Jan 26, 2022

Choose a reason for hiding this comment

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

placeholders if we ever need to generate them. We can remove them and assign the value nil in the struct

// - toystore_oidc: []
//

// scopes not being used now

Choose a reason for hiding this comment

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

do you see a use case for scopes in the future? Are you thinking it might make up part of authorization? Or would we leave that entirely up to the user to implement?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is actually part of the spec, so the command should generate required config to validate the scopes. The authorino implementation would be in the authorization phase validating that the user doing the request has granted those scopes.

I think we can leave this for another PR. Definitely is a missing feature from the command

switch secScheme.Type {
case "apiKey":
AuthConfigIdentityFromApiKeyScheme(identity, secScheme)
case "openIdConnect":

Choose a reason for hiding this comment

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

I may have missed it on the call earlier. Are these the only two schemas in open API?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, there are more:
For OpenAPI 3.0.X it is "apiKey", "http", , "oauth2", "openIdConnect".
OpenAPI 3.1 added "mutualTLS"

For the first implementation, apiKey and oidc. Others can be implemented either as authorizationpolicies or with authorino (authorino does not implement, yet, mtls)

Copy link
Contributor

@guicassolato guicassolato Jan 26, 2022

Choose a reason for hiding this comment

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

I think oauth2 could be supported combining Envoy's OAuth2 filter and either Authorino's OIDC or OAuth2 Token Introspection.

http can be modelled on top of Authorino API key authn. See this user guide for ref.

And, yes, mutualTLS soon to be supported in Authorino, but not yet.

Copy link

@maleck13 maleck13 left a comment

Choose a reason for hiding this comment

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

This looks really good and simple to me!

return []authorinov1beta1.JSONPattern{
{
JSONPatternExpression: authorinov1beta1.JSONPatternExpression{
Selector: "context.request.http.path",
Copy link
Contributor

@guicassolato guicassolato Jan 26, 2022

Choose a reason for hiding this comment

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

A trick might be needed here when the OAS securitySchemes.{name}.in == "query".

As query string params are part of path (as supplied by Envoy), we may need to strip them out or the condition won't match. To do so, you can use a string modifier:

context.request.http.path.@extract{"sep":"/"}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok, that is unexpected. Good catch.

I will fix that

@maleck13
Copy link

@eguzki tried out the example and looks good. One thing is it created a status block in the authconfig is that expected?

// Fixed label selector for now
apikey := authorinov1beta1.Identity_APIKey{
LabelSelectors: map[string]string{
"authorino.kuadrant.io/managed-by": "authorino",
Copy link
Contributor

Choose a reason for hiding this comment

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

How can users be more restrictive here?

The way it is right now, virtually any API key secret stored in a cluster can be used to authenticate to any API protected with Authorino deployed by Kuadrant in this cluster.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will add some more to be more restrictive

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a new label app = {title of the openapi satinized}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Anyway, the CLI can have some default values.. as a user I would expect to override them if I needed.

For another PR (good exercise to somebody wanting to learn) add CLI optional parameters to override default labels.

Choose a reason for hiding this comment

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

Probably a question for Authorino. but should it not be limited to looking in the same namespace as the authconfig? Seems odd I would protect my API with secret in a different namespace?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That depends on the deployment mode of authorino. As kuadrant deployment, it will be cluster scoped controller, so secrets on any namespace are valid as long as they have the required labels.

Copy link

@maleck13 maleck13 Jan 27, 2022

Choose a reason for hiding this comment

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

It seems odd to me though, that I can create an authconfig in namespace A with a secret label selector "app":"myapp" and someone working in a different namespace can create a secret in their namespace with the same selector and then get access to my API? They don't need to know the API Key or contents of the secret they just need to be able to guess the label selector. This is not related to the PR though so lets discuss separately

Copy link
Contributor

Choose a reason for hiding this comment

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

@codecov-commenter
Copy link

codecov-commenter commented Jan 26, 2022

Codecov Report

Merging #37 (780d7e9) into generate-istiointegration (94ec0aa) will decrease coverage by 0.78%.
The diff coverage is 21.42%.

Impacted file tree graph

@@                      Coverage Diff                      @@
##           generate-istiointegration      #37      +/-   ##
=============================================================
- Coverage                      30.39%   29.60%   -0.79%     
=============================================================
  Files                             19       21       +2     
  Lines                           1020     1118      +98     
=============================================================
+ Hits                             310      331      +21     
- Misses                           659      733      +74     
- Partials                          51       54       +3     
Impacted Files Coverage Δ
cmd/generate_kuadrant_authconfig.go 11.49% <11.49%> (ø)
cmd/generate.go 100.00% <100.00%> (ø)
cmd/generate_kuadrant.go 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 94ec0aa...780d7e9. Read the comment docs.

@eguzki
Copy link
Collaborator Author

eguzki commented Jan 26, 2022

@eguzki tried out the example and looks good. One thing is it created a status block in the authconfig is that expected?

Because the .status is an embbeded struct which 0 value is still serialized even though it has the json tag omitempty.

It should no do any harm to create the object with the .status field, as the controller should update it always based on the current state.

There is a way to have the CLI remove the status field. After serialization, unserialize with unstructured types (maps and lists), then, remove the status with a delete(authConfigMap, "status") and then serialize again. I do not think it is really needed. Let me know if it is convenient for you.

@eguzki
Copy link
Collaborator Author

eguzki commented Jan 27, 2022

Thanks for the reviews.

This PR can be merged after #36

Base automatically changed from generate-istiointegration to main January 31, 2022 13:00
@eguzki eguzki force-pushed the generate-authconfig branch from 81f7664 to 7d6edc4 Compare January 31, 2022 13:01
@eguzki eguzki merged commit 369b4c3 into main Jan 31, 2022
@eguzki eguzki deleted the generate-authconfig branch January 31, 2022 15:14
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.

4 participants