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

feat(aws-sd): tag services #4745

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion docs/tutorials/aws-sd.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Learn more about the API in the [AWS Cloud Map API Reference](https://docs.aws.a

## IAM Permissions

To use the AWS Cloud Map API, a user must have permissions to create the DNS namespace. Additionally you need to make sure that your nodes (on which External DNS runs) have an IAM instance profile with the `AWSCloudMapFullAccess` managed policy attached, that provides following permissions:
To use the AWS Cloud Map API, a user must have permissions to create the DNS namespace. You need to make sure that your nodes (on which External DNS runs) have an IAM instance profile with the `AWSCloudMapFullAccess` managed policy attached, that provides following permissions:

```
{
Expand Down Expand Up @@ -42,6 +42,86 @@ To use the AWS Cloud Map API, a user must have permissions to create the DNS nam
}
```

### IAM Permissions with ABAC
You can use Attribute-based access control(ABAC) for advanced deployments.

You can define AWS tags that are applied to services created by the controller. By doing so, you can have precise control over your IAM policy to limit the scope of the permissions to services managed by the controller, rather than having to grant full permissions on your entire AWS account.
To pass tags to service creation, use either CLI flags or environment variables:

*cli:*
```
--aws-sd-create-tag=key1=value1 --aws-sd-create-tag=key2=value2
```

*environment:*
```
EXTERNAL_DNS_AWS_SD_CREATE_TAG=key1=value1\nkey2=value2
```
Using tags, your `servicediscovery` policy can become:
```
github-vincent-miszczak marked this conversation as resolved.
Show resolved Hide resolved
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"servicediscovery:ListNamespaces",
"servicediscovery:ListServices"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"servicediscovery:CreateService",
"servicediscovery:TagResource"
],
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/YOUR_TAG_KEY": "YOUR_TAG_VALUE"
}
}
},
{
"Effect": "Allow",
"Action": [
"servicediscovery:DiscoverInstances"
],
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"servicediscovery:NamespaceName": "YOUR_NAMESPACE_NAME"
}
}
},
{
"Effect": "Allow",
"Action": [
"servicediscovery:RegisterInstance",
"servicediscovery:DeregisterInstance",
"servicediscovery:DeleteService",
"servicediscovery:UpdateService"
],
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/YOUR_TAG_KEY": "YOUR_TAG_VALUE"
}
}
}
]
}
```

## Set up a namespace

Create a DNS namespace using the AWS Cloud Map API:
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func main() {
log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
cfg.Registry = "aws-sd"
}
p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
case "azure-dns", "azure":
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.DryRun)
case "azure-private-dns":
Expand Down
7 changes: 6 additions & 1 deletion pkg/apis/externaldns/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type Config struct {
AWSPreferCNAME bool
AWSZoneCacheDuration time.Duration
AWSSDServiceCleanup bool
AWSSDCreateTag map[string]string
AWSZoneMatchParent bool
AWSDynamoDBRegion string
AWSDynamoDBTable string
Expand Down Expand Up @@ -255,6 +256,7 @@ var defaultConfig = &Config{
AWSPreferCNAME: false,
AWSZoneCacheDuration: 0 * time.Second,
AWSSDServiceCleanup: false,
AWSSDCreateTag: map[string]string{},
AWSDynamoDBRegion: "",
AWSDynamoDBTable: "external-dns",
AzureConfigFile: "/etc/kubernetes/azure.json",
Expand Down Expand Up @@ -355,7 +357,9 @@ var defaultConfig = &Config{

// NewConfig returns new Config object
func NewConfig() *Config {
return &Config{}
return &Config{
AWSSDCreateTag: map[string]string{},
}
}

func (cfg *Config) String() string {
Expand Down Expand Up @@ -473,6 +477,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
app.Flag("aws-zone-match-parent", "Expand limit possible target by sub-domains (default: disabled)").BoolVar(&cfg.AWSZoneMatchParent)
app.Flag("aws-sd-service-cleanup", "When using the AWS CloudMap provider, delete empty Services without endpoints (default: disabled)").BoolVar(&cfg.AWSSDServiceCleanup)
app.Flag("aws-sd-create-tag", "When using the AWS CloudMap provider, add tag to created services. The flag can be used multiple times").StringMapVar(&cfg.AWSSDCreateTag)
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure)").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
app.Flag("azure-subscription-id", "When using the Azure provider, override the Azure subscription to use (optional)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
Expand Down
9 changes: 7 additions & 2 deletions pkg/apis/externaldns/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var (
AWSProfiles: []string{""},
AWSZoneCacheDuration: 0 * time.Second,
AWSSDServiceCleanup: false,
AWSSDCreateTag: map[string]string{},
AWSDynamoDBTable: "external-dns",
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
Expand Down Expand Up @@ -166,6 +167,7 @@ var (
AWSProfiles: []string{"profile1", "profile2"},
AWSZoneCacheDuration: 10 * time.Second,
AWSSDServiceCleanup: true,
AWSSDCreateTag: map[string]string{"key1": "value1", "key2": "value2"},
AWSDynamoDBTable: "custom-table",
AzureConfigFile: "azure.json",
AzureResourceGroup: "arg",
Expand Down Expand Up @@ -322,6 +324,8 @@ func TestParseFlags(t *testing.T) {
"--aws-profile=profile2",
"--aws-zones-cache-duration=10s",
"--aws-sd-service-cleanup",
"--aws-sd-create-tag=key1=value1",
"--aws-sd-create-tag=key2=value2",
"--no-aws-evaluate-target-health",
"--policy=upsert-only",
"--registry=noop",
Expand Down Expand Up @@ -432,6 +436,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_AWS_PROFILE": "profile1\nprofile2",
"EXTERNAL_DNS_AWS_ZONES_CACHE_DURATION": "10s",
"EXTERNAL_DNS_AWS_SD_SERVICE_CLEANUP": "true",
"EXTERNAL_DNS_AWS_SD_CREATE_TAG": "key1=value1\nkey2=value2",
"EXTERNAL_DNS_DYNAMODB_TABLE": "custom-table",
"EXTERNAL_DNS_POLICY": "upsert-only",
"EXTERNAL_DNS_REGISTRY": "noop",
Expand Down Expand Up @@ -500,8 +505,8 @@ func restoreEnv(t *testing.T, originalEnv map[string]string) {

func TestPasswordsNotLogged(t *testing.T) {
cfg := Config{
PDNSAPIKey: "pdns-api-key",
RFC2136TSIGSecret: "tsig-secret",
PDNSAPIKey: "pdns-api-key",
RFC2136TSIGSecret: "tsig-secret",
}

s := cfg.String()
Expand Down
12 changes: 11 additions & 1 deletion provider/awssd/aws_sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,26 @@ type AWSSDProvider struct {
cleanEmptyService bool
// filter services for removal
ownerID string
// tags to be added to the service
tags []sdtypes.Tag
}

// NewAWSSDProvider initializes a new AWS Cloud Map based Provider.
func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, dryRun, cleanEmptyService bool, ownerID string, client AWSSDClient) (*AWSSDProvider, error) {
func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, dryRun, cleanEmptyService bool, ownerID string, tags map[string]string, client AWSSDClient) (*AWSSDProvider, error) {
p := &AWSSDProvider{
client: client,
dryRun: dryRun,
namespaceFilter: domainFilter,
namespaceTypeFilter: newSdNamespaceFilter(namespaceType),
cleanEmptyService: cleanEmptyService,
ownerID: ownerID,
tags: func() []sdtypes.Tag {
awsTags := make([]sdtypes.Tag, 0, len(tags))
for k, v := range tags {
awsTags = append(awsTags, sdtypes.Tag{Key: aws.String(k), Value: aws.String(v)})
}
return awsTags
}(),
github-vincent-miszczak marked this conversation as resolved.
Show resolved Hide resolved
}

return p, nil
Expand Down Expand Up @@ -400,6 +409,7 @@ func (p *AWSSDProvider) CreateService(ctx context.Context, namespaceID *string,
}},
},
NamespaceId: namespaceID,
Tags: p.tags,
})
if err != nil {
return nil, err
Expand Down