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

Flexible jwt authenticator #1295

Open
AndrewCopeland opened this issue Feb 5, 2020 · 6 comments
Open

Flexible jwt authenticator #1295

AndrewCopeland opened this issue Feb 5, 2020 · 6 comments

Comments

@AndrewCopeland
Copy link

AndrewCopeland commented Feb 5, 2020

As a conjur admin, I want a more flexible jwt authenticator, so that I can use any metadata within a JWT as a host identity.

SUMMARY:
The proposed azure authenticator flow does the following: Client sends JWT to conjur and conjur will then fetch and parse specific attributes from the JWT and then validate the parsed information with the hosts annotations.
This process seems like it could be made more generic to support not just azure JWT but any JWT.
Here is a sample policy I made. I am creating an authn-jwt authenticator that allows the ability to authenticate using subscription-id, resource-groupand resource-name. jsonPath is used to parse the attributes and then we can use regex to granularly obtain information within a json attribute.

GIVEN the policy

- !policy
  id: conjur/authn-jwt/azure-resource-name
  body:
  - !webservice
    annotations:
      # provider uri will be defined in annotations
      provider-uri: https://sts.azure.com/tenantID

      # the jwt is json,
      # so lets first parse with jsonPath
      # and then if we need to be more granular use
      # methods such as split or regex
      subscription-id/parse: $.sub

      # xms_mirid is a more complex attribute
      # and we must use the split method to
      # receive the needed resource group.
      # And a regex method for more 
      # complex attributes.
      resource-group/parse: $.xms_mirid
      # splitting on parsed attribute
      resource-group/parse/split: /
      # fetching this index from the array of the split
      resource-group/parse/index: 4

      # lets say I want to use regex
      resource-name/parse: $.xms_mirid
      # regex should only supports 1 match group 
      resource-name/parse/regex: .*\/(.*)$
      # only 1 match should be returned in this use case
      resource-name/parse/index: 0
  - !group apps
  - !permit
    role: !group apps
    resource: !webservice
    privilege: [ read, authenticate ]

- !host
  id: cyberark/azure/example_vm
  annotations:
    subscription-id: 24d6c3d7-d3eb-4973-b899-974bf79f48a
    resource-group: Conjur-ResourceGroup
    resource-name: ExampleVM

- !grant
  role: !group conjur/authn-oidc/azure-resource-group/apps
  member: !host cyberark/azure/example_vm

# now when `!host app1` authenticates the `subscription-id`, `resource-group` and `resource-name` will be validated and a conjur session token will be returned.

WHEN
The authn-jwt/azure-resource-name is configured and enabled

THEN
The following JWT should successfully authenticate to conjur

{
  "aud": "https://management.azure.com/",
  "iss": "https://sts.windows.net/df242c82.../",
  "iat": 157,
  "nbf": 157,
  "exp": 157,
  "aio": "42Vg...",
  "appid": "7230cc60...",
  "appidacr": "2",
  "idp": "https://sts.windows.net/df242c82...",
  "oid": "14751f4a...",
  "sub": "24d6c3d7-d3eb-4973-b899-974bf79f48a",
  "tid": "df242c82...",
  "uti": "g1mKQ0DE...",
  "ver": "1.0",
  "xms_mirid": "/subscriptions/24d6c3d7-d3eb-4973-b899-974bf79f48a/resourceGroups/Conjur-ResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ExampleVM"
}

I still think having default authenticators for each cloud provider such as iam, azure and gcp with jwt pre configured to the highest security standard is best. This solution would give us the ability to authenticate JWTs from any vendor.

On the conjur core side I think creating a jwt_validator class would be beneficial. An example of implementation below:

subscription_id_attr = {
  "annotation": "subscription-id",
  "json_path": "$.sub"
}
resource_group_attr = {
  "annotation": "resource-group",
  "json_path": "$.xms_mirid",
  "regex": "/",
  "index": 4
}
resource_name_attr = {
  "annotation": "resource-name",
  "json_path": "$.xms_mirid",
  "split": ".*\/(.*)$",
  "index": 0
}
expected_attributes = [
  subscription_id_attr,
  resource_group_attr,
  resource_name_attr
]
validated = jwt_validator(jwt, host_annotations, expected_attributes)
raise Err::Jwt_Validation_Error if !validated
@InbalZilberman
Copy link
Contributor

@AndrewCopeland we have discussed this today in the design review of Azure authenticator: #1288
The scope you are describing here is Azure specific I would suggest an even more generic approach for an JWT authenticator. a mechanism where you can specify a field in the JWT by which you would like to authenticate and its value. in Azure this field can be subscription id but in another identity provider it could be "myIdentity". WDYT?

@AndrewCopeland
Copy link
Author

This example is azure specific but it can be applied to any JWT.

What I am proposing is to parse the JWT using jsonPath and further parsing should be done with actions like regex or split all defined within the authenticators !webservice annotations.

@AndrewCopeland
Copy link
Author

Having a JWT authenticator would allow me to remove secret zero for many DevOps products that already have a certificate and private key. We could integrate with these products, generate a JWT (from the present private key) which can then be used to authenticate to conjur to then fetch secrets. This removes the need to pass around API keys to these devops tools.

@saravanant23
Copy link

👍👍

@InbalZilberman
Copy link
Contributor

@AndrewCopeland I do think JWT authn is the right way to go but I would dream of a way to "inherit" a basic jwt authn in a way that support authenticators that can be built on top of it that speak a native language of the target.
I'll give a HL example
Given a simple jwt autenticator I as a developer can contribute easily an Azure authenticator so that the hosts will have reasonable host annotations.

for example
authn-azure inherits authn-jwt according to these configurations
[{
"annotation": "resource-group",
"json_path": "$.xms_mirid",
"regex": "/",
"index": 4
},
{
"annotation": "resource-name",
"json_path": "$.xms_mirid",
"split": "./(.)$",
"index": 0
}]
WDYT?

@AndrewCopeland
Copy link
Author

Where would

[{
"annotation": "resource-group",
"json_path": "$.xms_mirid",
"regex": "/",
"index": 4
},
{
"annotation": "resource-name",
"json_path": "$.xms_mirid",
"split": "./(.)$",
"index": 0
}]

Be applied? All of this information should be configured on the policy itself and not implement within ruby code.
Conjur admins typically are not well versed in ruby meaning that configuration should be applied on the policy level.

Thanks,
Andrew

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

No branches or pull requests

3 participants