Networking & Content Delivery
Implementing fine-grained Amazon Route 53 access using AWS IAM condition keys (Part 1)
Users implement multi-account strategies to support multiple teams to deploy workloads. This post is for Amazon Web Services (AWS) administrators and network engineers who need to manage DNS permissions across multiple teams with shared Amazon Route 53 private hosted zone or public hosted zones. There may be situations where multiple teams shared the same hosted zones, and access control is necessary to either allow or deny the update of one or more DNS records within the shared hosted zones. One example is multiple teams operating in a shared subnet with only one shared hosted zone, where each team is delegating the responsibility to manage a specific DNS suffix within the shared hosted zone. Another example is a sandbox shared hosted zone for user to experiment with, and the users are denied management of DNS records for some shared services within the sandbox shared hosted zone.
This post discusses a scalable solution for fine-grained access to Route 53 hosted zones, private or public, to grant conditional access to update subset of DNS records in a shared hosted zone using AWS Identity and Access Management (IAM) condition keys and AWS principal tags. This post guides you through an example of how to grant fine-grained access within the same AWS account for IAM users.
Solution overview
We assume you have familiarity with Route 53 hosted zones for DNS management, IAM policy and permissions for access management, and creating key-value pairs as custom attribute with tags for IAM users. This solution uses the functionalities of IAM condition element and Route53 IAM actions, resources, and conditions keys for fine-grained access of Route 53 hosted zones. IAM policy condition
is an optional element in a policy statement that specifies the circumstances under which the policy grants or denies permissions. This solution streamlines access management by allowing you to create fine-grained permissions based on user attributes and reducing the permissions to align with least-privilege principles.
The following diagram shows the fine-grained Route 53 access architecture. The diagram shows how IAM users (left) are assigned a custom tag key “dnsrecords
” with values of their permitted DNS updates. When users attempt to modify DNS records in the Route 53 hosted zone (right), the IAM policy (center) evaluates the route53:ChangeResourceRecordSets action and compares the DNS record name update against the user tag value using the condition key route53:ChangeResourceRecordSetsNormalizedRecordNames to grant access.
The following four permission policies show the use of the four condition operators to grant permissions to DNS records. The Route 53 condition key route53:ChangeResourceRecordSetsNormalizedRecordNames
controls the DNS record names in the request of route53:ChangeResourceRecordSets
action by matching the ${aws:PrincipalTag/{custom-attribute}}
key to grant permission to manage specific DNS records. The ${aws:PrincipalTag/{custom-attribute}}
condition key specifies the tag value attached to IAM user.
Allow access to specific DNS record using StringEquals
Use the ForAllValues:StringEquals
operator when you want to grant permission to manage a specific DNS record name. This approach is ideal when you want to limit users to manage a precisely defined record without allowing access to other records in shared hosted zone. The following permission policy, replacing the {custom-attribute}
and {Route53HostedZoneID}
with your custom tag attribute and hosted zone ID respectively, allows the update of one DNS record.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/{Route53HostedZoneID}",
"Condition": {
"ForAllValues:StringEquals": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"${aws:PrincipalTag/{custom-attribute}}"
]
}
}
}
]
}
This policy and a principal tag value of “svc1.example.com
” mean that a user can only manage the DNS record “svc1.example.com
”. The same user is denied permission for other records, such as the following:
- Create new record “
api.svc1.example.com
” - Update existing record “
marketing.example.com
” - Delete existing record “
svc2.example.com
”
Deny access to specific DNS record using StringNotEquals
Use the ForAllValues:StringNotEquals
operator when you want to deny permission to manage a specific DNS record. This approach is ideal when you want to protect a specific DNS record while users can create any DNS record’s shared hosted zone. The following permission policy, replacing the {custom-attribute}
and {Route53HostedZoneID}
with your custom tag attribute and hosted zone ID respectively, denies the update of one DNS record.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/{Route53HostedZoneID}",
"Condition": {
"ForAllValues:StringNotEquals": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"${aws:PrincipalTag/{custom-attribute}}"
]
}
}
}
]
}
This policy and a principal tag value of “svc1.example.com
” mean that a user is denied access to manage the DNS record “svc1.example.com
”. The user would be allowed to update other DNS records, such as the following:
- Create new record “
api.svc1.example.com
” - Update existing record “
marketing.example.com
” - Delete existing record “
svc2.example.com
”
Allow access to DNS records ending with pattern using StringLike
Use the ForAllValues:StringLike
operator with wildcard “*
” and period “.
” in front of the custom attribute to grant permission to manage DNS records in a domain suffix. This approach is ideal when you want to limit users to manage a domain suffix without allowing access to other records in shared hosted zone. The following permission policy, replacing the {custom-attribute}
and {Route53HostedZoneID}
with your custom tag attribute and hosted zone ID respectively, allows you to update the DNS records in a domain suffix.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/{Route53HostedZoneID}",
"Condition": {
"ForAllValues:StringLike": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"*.${aws:PrincipalTag/{custom-attribute}}"
]
}
}
}
]
}
This policy and a principal tag value of “svc1.example.com
” mean that a user would be able to manage any DNS records in the “.svc1.example.com
” suffix, such as the following:
- Create new record “
api.svc1.example.com
” - Update existing record “
dev.svc1.example.com
” - Delete existing record “
prod.svc1.example.com
”
However, the same user would be denied permission for other records, such as the following:
- Create new record “
svc1.example.com
” - Update existing record “
marketing.example.com
” - Delete existing record “
api.svc2.example.com
”
Deny access to DNS records ending with pattern using StringNotLike
Use the ForAllValues:StringNotLike
operator with wildcard “*
” and period “.
” in front of the custom attribute to deny the update of DNS records in the domain suffix. This approach is ideal when you want to protect the specific DNS suffix in shared environments while users are allowed to create any DNS records in the shared hosted zone. The following permission policy, replacing the {custom-attribute}
and {Route53HostedZoneID}
with your custom tag attribute and hosted zone ID respectively, denies the update of DNS records ending with a specific pattern.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/{Route53HostedZoneID}",
"Condition": {
"ForAllValues:StringNotLike": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"*.${aws:PrincipalTag/{custom-attribute}}"
]
}
}
}
]
}
This policy and a principal tag value of “svc1.example.com
” mean that a user would be denied access to manage DNS records in the “.svc1.example.com
” suffix, such as the following:
- Create a new record “
api.svc1.example.com
” - Update an existing record “
dev.svc1.example.com
” - Delete an existing record “
prod.svc1.example.com
”
However, the same user is granted permission for other records, such as the following:
- Create a new record “
svc1.example.com
” - Update an existing record “
marketing.example.com
” - Delete an existing record “
api.svc2.example.com
”
Solution implementation
You create a solution that allows users to manage only DNS records that match their assigned tag values. Following these steps allows you to configure IAM user tags and policies that enable fine-grained access control to your Route 53 hosted zone. When completed, users can only manage DNS records that match the value in their custom tag.
The example uses the value in the “dnsrecords
” custom attribute to grant conditional access to update the DNS records ending with the “.svc1.example.com
” domain suffix in the shared hosted zone “example.com
” using one permission policy in the same account.
Prerequisites
Before proceeding, you need the following:
- Hosted zone for domain “
example.com
” - IAM user in the same AWS account as the hosted zone
- Log in to the AWS account of the hosted zone with permissions to update IAM users and permissions
Step 1: Create principal tag
Using the steps in tag IAM users, create the attribute/tag “dnsrecords
” with the value of “svc1.example.com
” for the IAM users in the AWS Management Console.
You can use the following AWS Command Line Interface (AWS CLI) to create the “dnsrecord
” key with the “svc1.example.com
” value, replacing <USER_NAME>
with your IAM user:
Step 2: Create the permission policy
Use the steps in the User Guide Define custom IAM permissions with customer managed policies to create the IAM permission policy using the following JSON policy file for fine-grained access of DNS records in the console. There is a wildcard “*
” and period “.
” in front of the ${aws:PrincipalTag/dnsrecords}
to enable the string match. The value of Z1R8UBAEXAMPLE
is the private hosted zone ID, and you must replace it for your hosted zone.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/Z1R8UBAEXAMPLE",
"Condition": {
"ForAllValues:StringLike": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"*.${aws:PrincipalTag/dnsrecords}"
]
}
}
}
]
}
You can use the following CLI command to create the permission policy, replacing <POLICY_NAME>
with name for policy and <DOCUMENT_FILE>
with the location of the preceding JSON policy document:
Step 3: Attached the permission policy to the IAM user or group
Attach the permission created in Step 2 to the IAM user with the custom tag in Step 1 or the group that the user is in to enable the fine-grained hosted zone access control for the user. Use the steps in the User Guide Changing permission for an IAM user to attach permission to the user, or use the steps in the User Guide Attach a policy to an IAM user group to attach permission to a group in the console.
You can use the following CLI command to attach permission policy to an IAM user, replacing <POLICY_ARN>
from the policy created in Step 2 and <USER_NAME>
with the IAM user created in Step 1:
You can use the following CLI command to attach permission policy to an IAM group, replacing <POLICY_ARN>
from the policy created in Step 2 and <GROUP_NAME>
with the group containing the IAM user created in Step 1:
Step 4: Verified DNS update permissions
The IAM user now has permission to managed DNS records in the “.svc1.example.com
” domain suffix in the shared hosted zone “example.com
”. You can verify the permission is working as expected by using following the steps in the Developer Guide Route 53 console to edit DNS records or the Developer Guide change-resource-record-sets CLI commands.
For example, using the following CLI command with the JSON configuration file to create the DNS record “dev.svc1.example.com
” can succeed.
{
"Comment": "configuration to create dev.svc1.example.com record",
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "dev.svc1.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "10.1.1.1"
}
]
}
}
]
}
Using the following CLI command with the JSON configuration file to create the DNS record “dev.svc2.example.com
” failed.
{
"Comment": "configuration to create dev.svc2.example.com record",
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "dev.svc2.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "10.1.1.1"
}
]
}
}
]
}
Considerations
Although the route53:ChangeResourceRecordSetsNormalizedRecordNames
condition is a multi-valued key and can match on multiple values using comma “,
” to separate the value, the tag value cannot contain commas “,
” to separate multiple values. Therefore, the custom attribute can only contain one DNS record value to work with a permission policy. If there is a need for access control to multiple DNS record values, then you must use multiple custom attributes to work with the corresponding permission policy.
More IAM policy conditions for Route 53 are available for more access controls on DNS records. The route53:ChangeResourceRecordSetsActions
condition key can limited actions to CREATE
, UPDATE
, and DELETE
. The route53:ChangeResourceRecordSetsRecordTypes
condition key can limit actions on DNS record types.
As you manage your IAM access, consider the IAM quotas and character limitations for users, groups, and policies.
If your permissions are not working as expected, then the troubleshoot IAM Policies documentation includes useful guides for common issues.
For environments not using shared hosted zones, where each team manages private hosted zones for their services, Route 53 Profiles is the recommended best practice for centralized management of DNS configurations for Amazon Virtual Private Clouds (Amazon VPCs) and accounts. The post Using Amazon Route 53 Profiles for scalable multi-account AWS environments reviews the architecture.
Conclusion
In this post, we reviewed a scalable solution of using AWS IAM conditional keys and AWS principal tags for fine-grained access control of shared Amazon Route 53 hosted zones for multiple teams to managed subsets of DNS records in a shared hosted zone. The review included an example of implementing conditional access of a hosted zone within the same account. In the next two posts, we review cross-account and federated user fine-grain access with IAM conditional keys and AWS principal tags.
About the author