AWS Policy: Allow update specific record in route53 hosted zone

AWS Policy: Allow update specific record in route53 hosted zone

Assume the documentation, I should use policy, like this:
{
   "Version": "2017-11-27",
   "Statement":[
      {   
         "Effect":"Allow",
         "Action": [
           "route53:ChangeResourceRecordSets"
         ],  
         "Resource": [
          "arn:aws:route53:::hostedzone/"
         ]
   ]   
}

I need a very safe policy.
I cannot add specific resource record set (one record in zone) in arn.
I can use Condition to check what record should be changed with ChangeResourceRecordSets API call. If I'm not mistaken.
This is necessary for automatic update only one record in public domain zone. Updates _acme-challenge.ldap.example.com. record for automatically update let's encrypt certificates. I know that acme.sh is available to achive my goal. But I want to write my own custom and simple script to do this.

Solutions/Answers:

Solution 1:

I can use Condition to check what record should be changed with ChangeResourceRecordSets API call. If I’m not mistaken.

I believe you may be mistaken.

Amazon Route 53 has no service-specific [condition] context keys that can be used in an IAM policy.

http://docs.aws.amazon.com/IAM/latest/UserGuide/list_route53.html

Additionally, none of the global condition keys seem applicable.

However, I believe there’s a workaround for this.

Create a second public hosted zone for the domain _acme-challenge.ldap.example.com. I know you’re probably thinking “but that’s not a domain!” but in the relevant sense, it actually is still a domain.

Route 53 will assign 4 new nameservers to this new hosted zone. Make a note of those servers.

Back in your original hosted zone, create a record for _acme-challenge.ldap.example.com of type NS. The value you will use to create this record will be the 4 nameservers that Route 53 assigned to the new hosted zone, one per line. Do not change any of the existing NS records in either of the zones.

This is called a delegation — you’re delegating authority for this particular subdomain to a different hosted zone, which you will notice was automatically assigned a completely different set of 4 Route 53 servers from those that handle your parent domain.

You can now create a new record in the root of the new hosted zone, and when you do a DNS query for _acme-challenge.ldap.example.com, the answer returned will be the answer from the new hosted zone.

Now, you can give your script permission only to modify records in the new zone, and it will be unable to modify anything in the parent zone, because you gave it no permissions, there.

Solution 2:

In the scenario of allowing starting EC2 instances with a script to add/remove record set of a domain programmatically, but limit the access to specific record safely without harming example.com.

Create another public hosted zone (without purchasing another domain from anyone), just use
a subdomain like delegate.example.com. Copy the name server assigned to delegate.example.com by AWS like:

ns-1143.awsdns-14.org. 
ns-1686.awsdns-18.co.uk. 
ns-133.awsdns-16.com. 
ns-965.awsdns-56.net.

And for example, the created zone is ZONEABCD

The goal of created domain will be instance-xxx.delegate.example.com without harming
example.com

Create a record set of NS Type in the hosted zone under your root example.com.
Create a record set of type NS and paste the 4 name servers copied from delegate.example.com.

The you can create a IAM user (copy the accesskey/secretkey for script) with the following policy attached. Limit resource to the created arn:aws:route53:::hostedzone/{ZONEABCD}.

{
"Version": "2012-10-17",
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "route53:ChangeResourceRecordSets"
        ],
        "Resource": [
            "arn:aws:route53:::hostedzone/{ZONEABCD}"
        ]
    },
    {
        "Effect": "Allow",
        "Action": [
            "route53:ListHostedZonesByName"
        ],
        "Resource": [
            "*"
        ]
    }
]}

Launch a VM with name instance-xxx and run script with aws-cli

$aws route53 change-resource-record-sets --cli-input-json '{
"HostedZoneId": "ZONEABCD",
"ChangeBatch": {
    "Comment": "This is a test and may be deleted.",
    "Changes": [
        {
            "Action": "CREATE",
            "ResourceRecordSet": {
                "Name": "instance-xxx.delegate.example.com",
                "Type": "A",
                "TTL": 600,
              "ResourceRecords": [
                {
                  "Value": "some.ip.v4.address"
                }
              ]
            }
        }
    ]
}}'

$nslookup instance-xxx.delegate.example.com 

Returning {some.ip.v4.address} for instance-xxx.delegate.example.com proves the above is working.

References

Related:  Let's encrypt docker nginx on AWS ECS