-
Notifications
You must be signed in to change notification settings - Fork 126
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
Authn-JWT Refactor #2734
base: master
Are you sure you want to change the base?
Authn-JWT Refactor #2734
Conversation
69a6f88
to
34f63f7
Compare
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.
Thanks for this design @jvanderhoof.
It's really visible in your implementation, of how much the different steps of the authenticator logic is organized and easier to understand. I added a few comments and questions, would love for you to take a look.
app/domain/authentication/Readme.md
Outdated
end | ||
``` | ||
|
||
which defines the required and optional data as well as the type. As Conjur Variables store values as strings, they type will always be `String`. |
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.
Question: while all variables are strings, some of them hold a JSON structure. For example, in the JWT authenticator, the public-keys
variable expects a structure like this:
{
"type": "jwks",
"value": <JWKS VALUE AS DEFINED BY THE IDENTITY PROVIDER>
}
Do you think that the schema should somehow define that as well?
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 really like this idea because the public-keys
implementation was one of the hardest bits to reverse engineer.
Dry Validations use Dry RB's Schema library, which supports custom types: https://dry-rb.org/gems/dry-schema/1.10/advanced/custom-types/. Schema also supports nested validation: https://dry-rb.org/gems/dry-schema/1.10/nested-data/. I'll need to dig in some more, but it looks likely that we could perform some validation on the provided JSON (if defined). This would give us the ability to guide the end user on the format of their public keys data.
app/domain/authentication/Readme.md
Outdated
# | ||
# @param [Hash/String] identifier - the role identifier established by the Strategy | ||
# @param [String] account - request account | ||
# @param [Array] allowed_roles - an array of roles with permission to authenticate using this authenticator |
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 assume that the allowed_roles
are given so that the Identity resolver will not need to access the DB directly?
I have a concern that if this authenticator has 30K roles that are allowed to it, such array will be quite a memory consumer and also the copying process of such amount of data will probably not be efficient for the Ruby runtime.
WDYT?
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 went back and forth a LOT on this during the design of the code redirect OIDC authentication flow. Ultimately I decided the simplicity of the approach (and critically, removing the DB awareness from the scope of the Strategy) was more important in the short term.
This question hints at a ResolveIdentity
improvement I'm trying to figure out how best to implement (and it would solve the above concern). Ultimately, I'd like the ResolveIdentity.call
method to be called for each role allowed to authenticate. The call
method would return either True
or False
depending on if the match is successful or not.
This solves two problems. The first, as noted above, is that it allows us to stream a set of roles (resolving the memory "bloat" potential. The second, is it allows us to use annotations for re-mapping email address to users (and enable OIDC users to be kept outside the root
policy. Andy's security concern is that multiple matches need to be identified and flagged (we can't exit on the first identified match). "Streaming" the role match allows us to guarantee only one match exists.
I need to find a good way to allow us to perform setup activity (example) before transforming call
into a method which takes a block.
# @param [String] request_body - authentication request body | ||
# @param [Hash] parameters - authentication request parameters | ||
# | ||
# @return something suitable for identifying a Conjur Role (usually a String or Hash) |
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.
If the Strategy is the code that has the authentication logic itself, maybe it needs to respond a decision? Like true if the authentication was successful, false if not. And additionally an optional string, with a reason to why an authentication failed?
I am trying to think if it makes sense for the strategy to be more strict in what it returns, so that the correct logic (authentication decision making) will be implemented into it. I see that in your implementation, it returns the JWT, right? Can you please explain why is that needed?
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 the case of the JWT Authenticator, we return the relevant part of the JWT token. Initially I was thinking is is because we perform some extraction in the Identity Resolution phase. In the OIDC Authenticator, we extract the desired lookup attribute from the claim (and return that).
Until you asked the question, I didn't think about the fact that the "pre-filter" logic probably should live in the Strategy (and Strategies need to return a single identity to be mapped to a Conjur Role). This allows us to keep the implementation details (JWT/OIDC/AWS IAM, etc.) in the Strategy.
This change also enables the above refactor (streaming role match) to be significantly simpler.
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.
Scratch part of this above statement. There is key information in the JWT that needs to be available in the Identity Resolution phase (ex annotation matching). Returning the relevant bits of the validated JWT ensures the secondary check that at least one mapping annotation exists on a host can occur.
5d0826f
to
972a3c5
Compare
2413c6e
to
cae1e05
Compare
@logger = logger | ||
end | ||
|
||
def call(identifier:, allowed_roles:, id: 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.
Method call
has 67 lines of code (exceeds 25 allowed). Consider refactoring.
end | ||
|
||
# Verify that `ca_cert` has a secret value set if the variable is present | ||
rule(:ca_cert, :account, :service_id) do |
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.
Similar blocks of code found in 9 locations. Consider refactoring.
end | ||
|
||
# Verify that `enforced_claims` has a secret value set if variable is present | ||
rule(:enforced_claims, :service_id, :account) do |
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.
Similar blocks of code found in 9 locations. Consider refactoring.
end | ||
|
||
# Verify that `identity_path` has a secret value set if variable is present | ||
rule(:identity_path, :service_id, :account) do |
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.
Similar blocks of code found in 9 locations. Consider refactoring.
end | ||
|
||
# Verify that `audience` has a secret value set if variable is present | ||
rule(:audience, :service_id, :account) do |
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.
Similar blocks of code found in 9 locations. Consider refactoring.
b8aa76a
to
7652fef
Compare
975e82d
to
b563eb9
Compare
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 PR diff size of 12507 lines exceeds the maximum allowed for the inline comments feature.
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 PR diff size of 12513 lines exceeds the maximum allowed for the inline comments feature.
Code Climate has analyzed commit 29dcd27 and detected 204 issues on this pull request. Here's the issue category breakdown:
The test coverage on the diff in this pull request is 93.4% (50% is the threshold). This pull request will bring the total coverage in the repository to 89.2% (-2.5% change). View more on Code Climate. |
29dcd27
to
31a8f22
Compare
This commit migrates the existing authn-jwt functionality to the new Strategy/ResolveIdentity architecture.
This commit updates the previously implemented authn-oidc workflow to adhere to the small changes in interface defined in the authn-jwt refactor
This commit removes the previous authn-jwt implementation and unit tests.
5c83479
to
e2584f9
Compare
This commit: - Adds support for regional STS endpoints. - Improves log messaging for failed requests. - Adds unit tests for all authenticator functionality.
Conjur is currently the only project which uses the conjur-rack gem. This PR brings it into Conjur to simplify the authorization process.
This commit: - Replaces a call to `git` with the equivalent Ruby call. This prevents a Git permission error. - Addresses all Rubocop warnings.
@jvanderhoof Can this PR be closed? |
This work is an effort to port the Authn-JWT functionality to the new Authenticator architecture.
Desired Outcome
Refactor Authn-JWT to use the new authenticator architecture defined in the new OIDC authenticator.
Implemented Changes
The authenticator has been completely ported, with all previous integration tests passing. I've added a Readme which overviews the architecture and interfaces.
Connected Issue/Story
N/A
Definition of Done
Changelog
CHANGELOG update
Test coverage
changes, or
Documentation
README
s) were updated in this PRBehavior
Security