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

Upgrade AWS STS client to v2 #504

Merged
merged 9 commits into from
Feb 14, 2025
Merged

Upgrade AWS STS client to v2 #504

merged 9 commits into from
Feb 14, 2025

Conversation

kelvin-chappell
Copy link
Member

@kelvin-chappell kelvin-chappell commented Feb 11, 2025

What is the purpose of this change?

To remove the deprecated AWS v1 STS client and replace it with v2.
And to remove the awscala STS library which has a dependence on the v1 client.

What is the value of this change and how do we measure success?

Code is up to date and easier to patch in future.
Should still be able to:

  • get temporary credentials to sign in to the AWS console
  • get temporary credentials to access AWS locally
  • have temporary credentials that expire at the expected time
  • revoke access to an account for credentials created before a threshold datetime

Any additional notes?

This is part of a series of PRs to upgrade AWS clients to v2.
See also:

Including removal of awscala STS library.
@kelvin-chappell kelvin-chappell changed the title WIP Upgrade AWS STS client to v2 Feb 11, 2025
@kelvin-chappell kelvin-chappell requested review from a team February 11, 2025 10:11
@kelvin-chappell kelvin-chappell marked this pull request as ready for review February 11, 2025 10:11
Copy link
Contributor

@adamnfish adamnfish left a comment

Choose a reason for hiding this comment

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

I think the revocation code will be broken as-is, because the authentication isn't providing the session token. This is a super important, but irregularly used, feature of Janus! Perhaps we can test it locally towards the end of a day on our own account after checking no-one is depending on their session?

Otherwise looks great, thank you!


class ViewHelpersTest extends AnyFreeSpec with Matchers {

private def mkCredentials(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason for this name to be shortened? Would it be clearer to call it makeCredentials? I'm also not 100% convinced that it wouldn't be clearer to just use the builder in each place, but if you tried that and thought this way was clearer then 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes I'll switch to using the builder inline.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 05ea600

def formatDateTime(date: DateTime): String =
dateTimeFormatter.print(date)

def formatTime(date: DateTime): String =
timeFormatter.print(date)
def formatTime(instant: java.time.Instant): String =
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 probably switch to java.time everywhere, at some point 😬

Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely!

Credentials(creds.accessKeyId, creds.secretAccessKey, creds.sessionToken)
val provider = new AWSStaticCredentialsProvider(sessionCredentials)
val iamClient = IAM(provider)
val iamClient = IAM(creds.accessKeyId(), creds.secretAccessKey())
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this will need the session token as well, since these are temporary credentials just like any others that Janus produces.

This bit still depends on AWScala, because we're making an instance of IAM here. While that's true I think we might as well continue to use the AWSCala credentials representation. awscala's core module includes SessionCredentials, which takes the three arguments we need here, and can be passed to the IAM constructor.

Copy link
Member Author

@kelvin-chappell kelvin-chappell Feb 12, 2025

Choose a reason for hiding this comment

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

I'll have another look at this code. It looked like it was actually just passing the same values around from one structure to another but I've probably missed something important here. It proves the value of code review.

Copy link
Member Author

Choose a reason for hiding this comment

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

I should have read the code more carefully! Fixed in f2a07b1

Copy link
Member Author

Choose a reason for hiding this comment

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

I've left the awscala IAM code as-is and I'll deal with that separately.

autoLogoutUrl(url, autoLogout)
}

private def signinToken(credentials: Credentials): String = {
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't necessarily the way we'd implement all this from scratch, but great for now given that this is explicitly a reimplementation of the existing code in AWScala 👍

Copy link
Contributor

@adamnfish adamnfish Feb 11, 2025

Choose a reason for hiding this comment

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

I've tried to read this all super carefully, and it looks like it should be the same as the previous behaviour. Ultimately it comes down to verifying this by using it, which I'm sure you have done locally (and it seems to work fine for me)!

Copy link
Member Author

Choose a reason for hiding this comment

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

It works locally. Like the dynamo db change, I see this as the smallest change I could make and it can be built on and improved.


val awsMinimumSessionLength = 900.seconds
private val signInUrl = "https://signin.aws.amazon.com/federation"
private val consoleUrl =
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be clearer to apply the URLEncoding where it is used, to call this something like encodedConsoleUrl, or to inline this val?

This is only used in one place, but I can see the benefit to having it be defined up top as a "constant". However, including the encoding feels a little like mixing the constant definition with the logic of its use. I don't feel strongly, whatever you think!

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right that would be more consistent and readable. It's a slight optimisation that's of very little value here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in b598b03

@kelvin-chappell
Copy link
Member Author

I think the revocation code will be broken as-is, because the authentication isn't providing the session token. This is a super important, but irregularly used, feature of Janus! Perhaps we can test it locally towards the end of a day on our own account after checking no-one is depending on their session?

Good point. I haven't tested that!

@kelvin-chappell
Copy link
Member Author

Tested revocation endpoint by:

  • verifying that account that was signed in is signed out following revocation and can be signed in again
  • janus-role-revocation-policy has a conditional date that corresponds with revocation action

# Conflicts:
#	app/aws/Federation.scala
#	app/controllers/Janus.scala
@adamnfish adamnfish dismissed their stale review February 14, 2025 10:28

Changes have been addressed, I'll re-review

Copy link
Contributor

@adamnfish adamnfish left a comment

Choose a reason for hiding this comment

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

LGTM, thank you.

Let's make sure we test it carefully, especially today while people are probably using Janus a bit more out of excitement for the multi-session feature!

@@ -129,8 +141,11 @@ object Federation {
stsClient,
Federation.awsMinimumSessionLength
)
val sessionCredentials =
Credentials(creds.accessKeyId, creds.secretAccessKey, creds.sessionToken)
val sessionCredentials = awscala.Credentials(
Copy link
Contributor

Choose a reason for hiding this comment

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

Great stuff, nice and easy for now and we defer this to the IAM change 👍

@kelvin-chappell kelvin-chappell merged commit 53c4489 into main Feb 14, 2025
6 checks passed
@kelvin-chappell kelvin-chappell deleted the kc/sts-upgrade branch February 14, 2025 13:51
@kelvin-chappell
Copy link
Member Author

Tested in Prod:

  • Can sign into console in multiple accounts
  • Can sign out from console and back in again from Janus
  • Can revoke all credentials for an account and see policy condition has correct threshold time and console and credentials expired
  • Default expiry time of temporary credentials is unchanged
  • Explicit expiry time for temporary credentials is still honoured (will find out in a few minutes)
  • Elk log has no new errors

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.

2 participants