Skip to content

Commit

Permalink
NE-1323: Add AWS RoleARN for Shared VPC support
Browse files Browse the repository at this point in the history
Adds the ability to specify a RoleARN to create Route 53 DNS
records in a different AWS Account. Supports Shared VPC scenario.
  • Loading branch information
gcs278 committed Jul 7, 2023
1 parent 0cd8bee commit d474491
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 1 deletion.
8 changes: 8 additions & 0 deletions api/v1beta1/externaldns_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,14 @@ type ExternalDNSAWSProviderOptions struct {
// +kubebuilder:validation:Required
// +required
Credentials SecretReference `json:"credentials"`

// RoleARN contains the ARN of a IAM role that will be assumed when using the AWS API.
// It provides the ability to use a hosted zone in another AWS account.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Pattern:=`^arn:(aws|aws-cn|aws-us-gov):iam::[0-9]{12}:role\/.*$`
// +optional
RoleARN *string `json:"roleARN,omitempty"`
// TODO: Additionally support access for:
// - kiam/kube2iam enabled clusters ("iam.amazonaws.com/role" POD's annotation to assume IAM role)
// - EKS clusters ("eks.amazonaws.com/role-arn" ServiceAccount's annotation to assume IAM role)
Expand Down
25 changes: 25 additions & 0 deletions api/v1beta1/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,31 @@ var _ = Describe("ExternalDNS admission webhook", func() {
Expect(err).ShouldNot(Succeed())
Expect(err.Error()).Should(ContainSubstring("credentials secret must be specified when provider type is AWS"))
})
It("valid RoleARN", func() {
resource := makeExternalDNS("test-valid-rolearn", nil)
resource.Spec.Provider = ExternalDNSProvider{
Type: ProviderTypeAWS,
AWS: &ExternalDNSAWSProviderOptions{
RoleARN: pointer.String("arn:aws:iam::123456789012:role/foo"),
Credentials: SecretReference{Name: "credentials"},
},
}
err := k8sClient.Create(context.Background(), resource)
Expect(err).Should(Succeed())
})
It("invalid RoleARN rejected", func() {
resource := makeExternalDNS("test-invalid-rolearn", nil)
resource.Spec.Provider = ExternalDNSProvider{
Type: ProviderTypeAWS,
AWS: &ExternalDNSAWSProviderOptions{
RoleARN: pointer.String("arn:aws:iam:bad:123456789012:role/foo"),
Credentials: SecretReference{Name: "credentials"},
},
}
err := k8sClient.Create(context.Background(), resource)
Expect(err).ShouldNot(Succeed())
Expect(err.Error()).Should(ContainSubstring(`ExternalDNS.externaldns.olm.openshift.io "test-invalid-rolearn" is invalid: spec.provider.aws.roleARN: Invalid value: "arn:aws:iam:bad:123456789012:role/foo": spec.provider.aws.roleARN in body should match '^arn:(aws|aws-cn|aws-us-gov):iam::[0-9]{12}:role\/.*$'`))
})
})

Context("resource with Azure provider", func() {
Expand Down
7 changes: 6 additions & 1 deletion api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,12 @@ spec:
required:
- name
type: object
roleARN:
description: RoleARN contains the ARN of a IAM role that will
be assumed when using the AWS API. It provides the ability
to use a hosted zone in another AWS account.
pattern: ^arn:(aws|aws-cn|aws-us-gov):iam::[0-9]{12}:role\/.*$
type: string
required:
- credentials
type: object
Expand Down
57 changes: 57 additions & 0 deletions pkg/operator/controller/externaldns/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,55 @@ func TestDesiredExternalDNSDeployment(t *testing.T) {
},
},
},
{
name: "RoleARN set AWS Route",
inputExternalDNS: testAWSExternalDNSRoleARN(operatorv1beta1.SourceTypeRoute, "arn:aws:iam:123456789012:role/foo"),
expectedTemplatePodSpec: corev1.PodSpec{
ServiceAccountName: test.OperandName,
NodeSelector: map[string]string{
osLabel: linuxOS,
masterNodeRoleLabel: "",
},
Tolerations: []corev1.Toleration{
{
Key: masterNodeRoleLabel,
Operator: corev1.TolerationOpExists,
Effect: corev1.TaintEffectNoSchedule,
},
},
Containers: []corev1.Container{
{
Name: ExternalDNSContainerName,
Image: test.OperandImage,
Args: []string{
"--aws-assume-role=arn:aws:iam:123456789012:role/foo",
"--metrics-address=127.0.0.1:7979",
"--txt-owner-id=external-dns-test",
"--zone-id-filter=my-dns-public-zone",
"--provider=aws",
"--source=openshift-route",
"--policy=sync",
"--registry=txt",
"--log-level=debug",
"--ignore-hostname-annotation",
`--fqdn-template={{""}}`,
"--txt-prefix=external-dns-",
},
SecurityContext: &corev1.SecurityContext{
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{allCapabilities},
},
Privileged: pointer.Bool(false),
RunAsNonRoot: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
},
},
},
},
},
{
name: "Nominal Azure Route",
inputSecretName: azureSecret,
Expand Down Expand Up @@ -5317,6 +5366,14 @@ func testAWSExternalDNSDomainFilter(zones []string, source operatorv1beta1.Exter
return extdns
}

func testAWSExternalDNSRoleARN(source operatorv1beta1.ExternalDNSSourceType, roleARN string) *operatorv1beta1.ExternalDNS {
extdns := testCreateDNSFromSourceWRTCloudProvider(source, operatorv1beta1.ProviderTypeAWS, nil, "")
extdns.Spec.Provider.AWS = &operatorv1beta1.ExternalDNSAWSProviderOptions{
RoleARN: pointer.String(roleARN),
}
return extdns
}

func testPlatformStatusGCP(projectID string) *configv1.PlatformStatus {
return &configv1.PlatformStatus{
Type: configv1.GCPPlatformType,
Expand Down
4 changes: 4 additions & 0 deletions pkg/operator/controller/externaldns/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ func (b *externalDNSContainerBuilder) fillProviderAgnosticFields(seq int, zone s
args = append(args, "--ignore-hostname-annotation")
}

if b.externalDNS.Spec.Provider.AWS != nil && b.externalDNS.Spec.Provider.AWS.RoleARN != nil {
args = append(args, fmt.Sprintf("--aws-assume-role=%s", *b.externalDNS.Spec.Provider.AWS.RoleARN))
}

if len(b.externalDNS.Spec.Source.FQDNTemplate) > 0 {
args = append(args, fmt.Sprintf("--fqdn-template=%s", strings.Join(b.externalDNS.Spec.Source.FQDNTemplate, ",")))
} else {
Expand Down

0 comments on commit d474491

Please sign in to comment.