mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-22 02:02:14 +00:00
refactor: allow assume role with optional externalId
This commit is contained in:
parent
b3312b7e90
commit
4ada2885aa
@ -30,7 +30,7 @@ import vinyldns.core.domain.{Fqdn, record}
|
||||
import vinyldns.core.domain.record.{RecordSet, RecordType}
|
||||
|
||||
class Route53IntegrationSpec
|
||||
extends AnyWordSpec
|
||||
extends AnyWordSpec
|
||||
with BeforeAndAfterAll
|
||||
with BeforeAndAfterEach
|
||||
with Matchers {
|
||||
@ -52,6 +52,8 @@ class Route53IntegrationSpec
|
||||
"test",
|
||||
Option("access"),
|
||||
Option("secret"),
|
||||
None,
|
||||
None,
|
||||
sys.env.getOrElse("R53_SERVICE_ENDPOINT", "http://localhost:19003"),
|
||||
"us-east-1"
|
||||
)
|
||||
|
@ -16,13 +16,9 @@
|
||||
|
||||
package vinyldns.route53.backend
|
||||
|
||||
import java.util.UUID
|
||||
import cats.data.OptionT
|
||||
import cats.effect.IO
|
||||
import com.amazonaws.auth.{
|
||||
AWSStaticCredentialsProvider,
|
||||
BasicAWSCredentials,
|
||||
DefaultAWSCredentialsProviderChain
|
||||
}
|
||||
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration
|
||||
import com.amazonaws.handlers.AsyncHandler
|
||||
import com.amazonaws.services.route53.{AmazonRoute53Async, AmazonRoute53AsyncClientBuilder}
|
||||
@ -278,21 +274,23 @@ object Route53Backend {
|
||||
r53ClientBuilder.withEndpointConfiguration(
|
||||
new EndpointConfiguration(config.serviceEndpoint, config.signingRegion)
|
||||
)
|
||||
// If either of accessKey or secretKey are empty in conf file; then use AWSCredentialsProviderChain to figure out
|
||||
// credentials.
|
||||
// https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html
|
||||
val credProvider = config.accessKey
|
||||
.zip(config.secretKey)
|
||||
.map {
|
||||
case (key, secret) =>
|
||||
new AWSStaticCredentialsProvider(
|
||||
new BasicAWSCredentials(key, secret)
|
||||
)
|
||||
}
|
||||
.headOption
|
||||
.getOrElse {
|
||||
new DefaultAWSCredentialsProviderChain()
|
||||
|
||||
val r53CredBuilder = Route53Credentials.builder
|
||||
for {
|
||||
accessKey <- config.accessKey
|
||||
secretKey <- config.secretKey
|
||||
} r53CredBuilder.basicCredentials(accessKey, secretKey)
|
||||
|
||||
for (role <- config.roleArn) {
|
||||
config.externalId match {
|
||||
case Some(externalId) =>
|
||||
r53CredBuilder.withRole(role, UUID.randomUUID().toString, externalId)
|
||||
case None => r53CredBuilder.withRole(role, UUID.randomUUID().toString)
|
||||
}
|
||||
}
|
||||
|
||||
val credProvider = r53CredBuilder.build().provider
|
||||
|
||||
r53ClientBuilder.withCredentials(credProvider).build()
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.route53.backend
|
||||
|
||||
import com.amazonaws.auth._
|
||||
import org.slf4j.LoggerFactory
|
||||
import com.amazonaws.services.securitytoken.{
|
||||
AWSSecurityTokenService,
|
||||
AWSSecurityTokenServiceClientBuilder
|
||||
}
|
||||
|
||||
private[backend] sealed trait Route53Credentials extends Serializable {
|
||||
def provider: AWSCredentialsProvider
|
||||
}
|
||||
|
||||
private[backend] final case object DefaultCredentials extends Route53Credentials {
|
||||
def provider: AWSCredentialsProvider = new DefaultAWSCredentialsProviderChain
|
||||
}
|
||||
|
||||
private[backend] final case class BasicCredentials(accessKeyId: String, secretKey: String)
|
||||
extends Route53Credentials {
|
||||
|
||||
private final val logger = LoggerFactory.getLogger(classOf[Route53Backend])
|
||||
|
||||
def provider: AWSCredentialsProvider =
|
||||
try {
|
||||
new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, secretKey))
|
||||
} catch {
|
||||
case e: IllegalArgumentException =>
|
||||
logger.error(
|
||||
"Error when using accessKey/secret: {}. Using DefaultProviderChain.",
|
||||
e.getMessage()
|
||||
)
|
||||
new DefaultAWSCredentialsProviderChain
|
||||
}
|
||||
}
|
||||
|
||||
private[backend] final case class STSCredentials(
|
||||
roleArn: String,
|
||||
sessionName: String,
|
||||
externalId: Option[String] = None,
|
||||
longLivedCreds: Route53Credentials = DefaultCredentials
|
||||
) extends Route53Credentials {
|
||||
|
||||
def provider: AWSCredentialsProvider = {
|
||||
lazy val stsClient: AWSSecurityTokenService =
|
||||
AWSSecurityTokenServiceClientBuilder
|
||||
.standard()
|
||||
.withCredentials(longLivedCreds.provider)
|
||||
.build()
|
||||
val builder = new STSAssumeRoleSessionCredentialsProvider.Builder(roleArn, sessionName)
|
||||
.withStsClient(stsClient)
|
||||
externalId match {
|
||||
case Some(externalId) =>
|
||||
builder
|
||||
.withExternalId(externalId)
|
||||
.build()
|
||||
case None =>
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Route53Credentials {
|
||||
class Builder {
|
||||
private var basicCreds: Option[BasicCredentials] = None
|
||||
private var stsCreds: Option[STSCredentials] = None
|
||||
|
||||
def basicCredentials(accessKeyId: String, secretKey: String): Builder = {
|
||||
basicCreds = Option(BasicCredentials(accessKeyId, secretKey))
|
||||
this
|
||||
}
|
||||
|
||||
def withRole(roleArn: String, sessionName: String): Builder = {
|
||||
stsCreds = Option(STSCredentials(roleArn, sessionName))
|
||||
this
|
||||
}
|
||||
|
||||
def withRole(roleArn: String, sessionName: String, externalId: String): Builder = {
|
||||
stsCreds = Option(
|
||||
STSCredentials(
|
||||
roleArn,
|
||||
sessionName,
|
||||
Option(externalId)
|
||||
)
|
||||
)
|
||||
this
|
||||
}
|
||||
|
||||
def build(): Route53Credentials =
|
||||
stsCreds.map(_.copy(longLivedCreds = longLivedCreds)).getOrElse(longLivedCreds)
|
||||
|
||||
private def longLivedCreds: Route53Credentials = basicCreds.getOrElse(DefaultCredentials)
|
||||
}
|
||||
|
||||
def builder: Builder = new Builder
|
||||
}
|
@ -27,6 +27,8 @@ final case class Route53BackendConfig(
|
||||
id: String,
|
||||
accessKey: Option[String],
|
||||
secretKey: Option[String],
|
||||
roleArn: Option[String],
|
||||
externalId: Option[String],
|
||||
serviceEndpoint: String,
|
||||
signingRegion: String
|
||||
)
|
||||
|
@ -94,6 +94,7 @@ object Dependencies {
|
||||
|
||||
lazy val r53Dependencies = Seq(
|
||||
"com.amazonaws" % "aws-java-sdk-core" % awsV withSources(),
|
||||
"com.amazonaws" % "aws-java-sdk-sts" % awsV withSources(),
|
||||
"com.amazonaws" % "aws-java-sdk-route53" % awsV withSources()
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user