[ELY-2359] Support HTTP Digest when fronted by load balancer#1909
[ELY-2359] Support HTTP Digest when fronted by load balancer#1909skyllarr wants to merge 3 commits intowildfly-security:2.xfrom
Conversation
4db7996 to
3bdc975
Compare
|
@fjuma This can be reviewed, tests are in the wilfdly PR as they need to setup clustering. Thank you! |
|
|
||
| public static final String CONFIG_VALIDATE_DIGEST_URI = CONFIG_BASE + ".validate-digest-uri"; | ||
| public static final String CONFIG_SKIP_CERTIFICATE_VERIFICATION = CONFIG_BASE + ".skip-certificate-verification"; | ||
| public static final String CONFIG_SESSION_DIGEST = CONFIG_BASE + ".session-digest"; |
There was a problem hiding this comment.
I think it would be good to make it more clear that this is a boolean value. Maybe something like use-digest-session-based-nonce-manager or use-digest-session-nonce-manager or use-session-digest, etc.
There was a problem hiding this comment.
@fjuma Thank you! Fixed to be use-session-based-digest-nonce-manager
| return mechanismRealms.get(realmName); | ||
| } | ||
|
|
||
| public boolean getSessionDigest() { |
There was a problem hiding this comment.
Just a minor comment on naming, since this is returning a boolean value, we could rename this to something like isUseSessionDigest or getUseSessionDigest (or something else depending on the property name you go with).
| return this; | ||
| } | ||
|
|
||
| public Builder setSessionDigest(final Boolean sessionDigest) { |
There was a problem hiding this comment.
Same here, would be good to rename this too.
| Object configSessionDigest = properties.get(CONFIG_SESSION_DIGEST); | ||
| String sessionDigest = configSessionDigest instanceof String ? (String) configSessionDigest : null; | ||
| if (Boolean.parseBoolean(sessionDigest)) { | ||
| nonceManager = new PersistentNonceManager(300000, 900000, true, 20, SHA256, ElytronMessages.httpDigest); |
There was a problem hiding this comment.
Might be good to make these parameter values constants so they can be shared with the default NonceManager too.
There was a problem hiding this comment.
@fjuma Thank you! fixed by placing the constants to the NonceManagerUtils
| } catch (GeneralSecurityException e) { | ||
| throw new IllegalStateException(e); | ||
| } | ||
| return NonceManagerUtils.generateNonce(salt, algorithm, nonceCounter, privateKey ); |
There was a problem hiding this comment.
Totally minor, there's an extra space before the closing bracket.
| * @param algorithm the message digest algorithm to use when creating the digest portion of the nonce. | ||
| */ | ||
|
|
||
| @Deprecated |
There was a problem hiding this comment.
Just to check, why is this deprecated?
There was a problem hiding this comment.
@fjuma This constructor was deprecated because its associated parent constructor is deprecated. I will remove this constructor instead of adding it as deprecated
| /* | ||
| * JBoss, Home of Professional Open Source. | ||
| * Copyright 2018 Red Hat, Inc., and individual contributors | ||
| * Copyright 2023 Red Hat, Inc., and individual contributors |
There was a problem hiding this comment.
Just curious why this is change is needed. I don't seem to see other updates in this file.
| if (request.getScope(Scope.SESSION) == null || !request.getScope(Scope.SESSION).exists()) { | ||
| request.getScope(Scope.SESSION).create(); | ||
| } | ||
| PersistentNonceManager persistentNonceManager = (PersistentNonceManager) request.getScope(Scope.SESSION).getAttachment("persistentNonceManager"); |
There was a problem hiding this comment.
I think it would be good to make the "persistentNonceManager" key a constant.
| * | ||
| * @param persistentNonceManager PersistentNonceManager instance to update the attributes from | ||
| */ | ||
| void refreshInfoFromSessionNonceManager(PersistentNonceManager persistentNonceManager) { |
There was a problem hiding this comment.
Just so I understand better, would you be able to provide some details on why this is needed? Wondering if it's possible to make use of the nonce manager from the session directly rather than updating the attributes of another instance.
There was a problem hiding this comment.
@fjuma The nonceManager in DigestAuthenticationmechanism, where this method is being used, is a final variable. I need to be retrieving the nonce manager with each request, but I cannot assign to a final variable. So I am assigning only its attributes instead. Should I change the nonceManager to be non final so I can assign to it with every request?
There was a problem hiding this comment.
I removed the final from the nonceManager and removed this method. It is now being assigned directly instead of updating specific attributes
|
@darranl Please review, thanks! |
| } else if (properties.get(CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER) != null) { | ||
| Object configSessionDigest = properties.get(CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER); | ||
| String sessionDigest = configSessionDigest instanceof String ? (String) configSessionDigest : null; | ||
| if (Boolean.parseBoolean(sessionDigest)) { |
There was a problem hiding this comment.
I think Boolean.parseBoolean((String) properties.get(CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER)) could be used here.
| * @param log mechanism specific logger. | ||
| */ | ||
| PersistentNonceManager(long validityPeriod, long nonceSessionTime, boolean singleUse, int keySize, String algorithm, ElytronMessages log) { | ||
| this.validityPeriodNano = validityPeriod * 1000000; |
There was a problem hiding this comment.
Just minor but looks like this constructor could just call the one below with customExecutor set to null.
| private long nonceSessionTime; | ||
| private boolean singleUse; | ||
| private String algorithm; | ||
| private volatile ElytronMessages log; |
There was a problem hiding this comment.
@fjuma Not sure why this was volatile, I removed it, thanks!
|
@darranl When you get a chance, please review, thanks! |
4605226 to
12b892a
Compare
|
@darranl Please review, thank you! |
|
@darranl Please review this one when you get a chance, thank you! |
|
@darranl Please review this one when you get a chance, thank you! |
|
I am trying to unravel some of the history regarding the NonceManager, firstly I think I would say in this case we probably don't need to read too much into the variable being final - if at the time these classes were implemented we were setting it once and never changing it we would have set it to final more to allow the compiler to verify we set it to comething and will not change it - not that it is final so we can never change it. It looks like the package is considered private API so I think this has the option to do what we want here rather than constrain ourselves with the past. I see Ashley's changes also added the option to pass in a NonceManager but searching the code I can't find us using this yet. So maybe we could go a few steps further:
|
414d36f to
b260766
Compare
|
Just to post an update here - since this will be persisted within the HTTP session the backwards compatibility will need to be kept in mind |
|
I added some TODOs in the code. The remaining points to address are from Darran above: |
|
FYI I am going to be picking up this one to continue, we can keep this PR here for now but I will likely create a new one to target specific branches. |
6d1b386 to
8b30960
Compare
IIRC we planned to backport this into older versions, so that was another consideration, but yes since it is private API I updated it as per your suggestion.
@darranl I think you mean this Ashley's PR , which added an option to get nonceManager from the mechanism properties here. Since I am using the nonce manager associated with the HTTP session, I am storing it in the HTTP session as an attachment, not in the properties. I am just checking the properties to know if the nonce manager should be persisted.
PR updated with this change.
Updated. |
f838ff8 to
7bf43f3
Compare
|
This PR is superseded by #2300 which targets 2.2.x. Leaving this opened for reference. |
|
A few questions about this proposal:
|
|
Looking at this again, it seems to me that this could benefit from a finer distinction between nonce state and nonce management. Would it not be better to persist nonce state to the session - rather than the mechanism by which nonces are managed? |
@pferraro Yes the current request was associated with the NonceManager, it made some code changes simpler, but it was a bad choice. I removed this association and the NonceManager now does not hold the request.
yes
This uses the HttpScope interface with the SESSION scope, which seemed the right place. I did not check how it is implemented though and if there is any filtering
I will go through the fields again and make sure the unneeded fields are transient/static, thank you
These constructors were present previously and I did not look into this TBH, but yes it doesn't seem the executor should need a thread per instance
How I work with the used nonces map needs a revisit from me, there is a simple smoke test which is passing, but the problems you describe are there so the test is insufficient probably. If the used nonces should not be associated with the session, they need to be shared between nodes some other way Thanks for feedback! |
@pferraro Yes, true. The used nonces map shouldn't be persisted in session because of the points you raised. What would be a good way to share it between nodes in a cluster? |
From what I can tell, this use case has the following consistency requirements:
If my requirement assumptions are correct, I do not think that the HttpSession is suitable for tracking nonces, since it would otherwise allow the same nonce to be used by different sessions. AFAICT, we should be able to model nonce tracking as a simple atomic counter. We can encapsulate this as a java.util.function.ToIntFunction<String>, where, for a given nonce, return 0 if not yet used, or an incremented value if used. e.g. private final Map<String, Integer> map;
@Override
public int applyAsInt(String nonce) {
return map.compute(nonce, (key, count) -> Optional.ofNullable(count).map(Math::incrementExact).orElse(0));
}For the distributed case, we would model nonce tracking via a distributed atomic counter, where the container would provide the ToIntFunction implementation, e.g. one based on Infinispan using server managed configuration. |
|
Closing as this RFE is postponed, backup branch is located at wildfly-security-incubator/wildfly-elytron |
https://issues.redhat.com/browse/ELY-2359