Resource icon

Tutorial DDNS with AWS Lambda and Route53

Intro
Amazon Web Services provide domain name servers and serverless computing with their products Route53 and Lambda, respectively.

Synology’s DSM ships with built in functionality to periodically send network information to a custom API endpoint to facilitate dynamically updating DNS records to point to it’s current IP.

This guide covers the steps to configure DSM’s DDNS service to send a servers IP to Lambda, which in turn, will update Route53’s DNS records.

The only assumptions in this article are that you have an AWS account and a domain name registered in Route53.

Step 1: Configure IAM
AWS has strict security and permissions are managed by the Identity and Access Management service (IAM). We need to create an IAM ‘Role’ to allow Lambda to access Route53 and update the DNS records.
  • Login to AWS
  • Go to: Services > IAM
  • Click “Roles”
  • Click “Create Role”
  • Under “Select type of trusted entity” choose “Lambda”
  • Click “Next: Permissions”
  • Search for “Route53”
  • Check “AmazonRoute53FullAccess”
  • Search for “Lambda”
  • Check “AWSLambdaBasicExecutionRole”
  • Click “Next: Tags”
  • Skip this step and click “Next: Review”
  • Give the Role a name under “Role name*”, let’s go with “Synology-DDNS”
  • Click “Create role”

Step 2: Configure Route53
  • Go to: Services > Route53
  • Select the domain you want to use.
  • Find your Hosted Zone ID Under the column “Hosted Zone ID” (see circled, below).
  • Keep a note of this ID, you’ll need it later.
1564421686506.png


Step 2B: (OPTIONAL) Create a sub-domain for your NAS.
  • Login to AWS
  • Go to: Services > Route53
  • Select the domain you want to subdomain by clicking on its name (e.g. “my-domain.com”)
  • Click “Create Record Set”
A panel will appear on the right.
  • Add a subdomain in the “Name” field. (e.g. “nas” will create the subdomain “nas.my-domain.com”)
  • Under “Value” enter “0.0.0.0” (any IP will do, it’s just a placeholder for now)
1564421724392.png


Step 3.1: Create a new Lambda function
  • Login to AWS
  • Go to: Services > Lambda
  • Click “Create function”
  • Select “Author from scratch”
  • Fill in the “Function name” field with whatever name you like
  • Select Node.js 10.x from “Runtime”
  • Open the dropdown “Choose or create an execution role”
  • Under “Execution role” select “Use an existing role”
  • Under “Existing role” choose the role you created in Step 1, “Synology-DDNS”
  • Click “Create function”.
1564421742538.png


Step 3.2: Add a Trigger
  • In the “Designer” component select “Add trigger”
1564421758495.png

  • Under “Select a trigger”, select “API Gateway” (it should be the first in the list).
  • Under “API”, select “Create a new API”
  • Under “Security” select “Open”
  • Click “Add”
1564421773233.png


Step 3.3: Copy your function’s API endpoint
  • You should now be back at your function screen. If not, go to Lambda > Functions > Synology-DDNS.
  • Click on “API Gateway”
  • Scroll down to the bottom and copy your “API endpoint”, you’ll need this later.
1564421820336.png


Step 3.4: Add your function’s code
  • At the top of your functions page select the function name, “Synology-DDNS”
1564421826624.png

  • The code editor should appear, scroll down and add the following code to the file “index.js”
  • You’ll need to update the following constants with your own values:
  • “USERNAME” – Make a new random username
  • “PASSWORD” – Make a new random password
  • “HOSTED_ZONE_ID” – You should have saved this from Step 2.
  • At the top of the page, click “Save”
JavaScript:
const AWS = require('aws-sdk');
const route53 = new AWS.Route53();

const USERNAME = 'YOUR_USERNAME';
const PASSWORD = 'YOUR_PASSWORD';
const ROUTE53_COMMENT = 'My Synology Sub-Domain';
const HOSTED_ZONE_ID = 'YOUR_ZONE_ID';

exports.handler = async (event) => {
    
    if (event.queryStringParameters.username === USERNAME &&
    event.queryStringParameters.password === PASSWORD){
        
        var params = {
            ChangeBatch: {
                Changes: [
                    {
                    Action: "UPSERT",
                    ResourceRecordSet: {
                    Name: event.queryStringParameters.hostname,
                    ResourceRecords: [
                        {
                        Value: event.queryStringParameters.ip
                        }
                    ],
                    TTL: 60,
                    Type: "A"
                    }
                }
                ],
                Comment: ROUTE53_COMMENT
            },
            HostedZoneId: HOSTED_ZONE_ID
        };
        
        return route53.changeResourceRecordSets(params).promise()
        .then(res =>{
            console.log('Record Set!');
            const response = {
                statusCode: 200,
                body: 'Record Set!',
            };
            return response;
        });
    
    } else {
        console.log('Say who now?');
        const response = {
            statusCode: 401,
            body: 'Say who now?',
        };
        return response;
    }
};

Note: Do not use a username/password you commonly use!
(Alternatively, you could modify the script to accept a single token, it’s up to you.)
1564422146169.png


Step 4.1: Configure Custom DDNS Provider in Disk Station Manager
  • Login to your Synology’s DSM
  • Go to: Control Panel > External Access
  • Click “Customize”
  • Under “Service provider” add a name for your custom service:
  • Under Query URL paste in your “API endpoint” from Step 3.3
1564422049295.png


  • Add the following query params to the end of the “API endpoint” URI:
Code:
?hostname=__HOSTNAME__&ip=__MYIP__&username=__USERNAME__&password=__PASSWORD__

The full query should look something like:
Code:
https://<Your Unique Lambda ID>.execute-api.eu-west-1.amazonaws.com/default/Synology-DDNS?hostname=__HOSTNAME__&ip=__MYIP__&username=__USERNAME__&password=__PASSWORD__

(__HOSTNAME__, __MYIP__, __USERNAME__, and __PASSWORD__ will all be injected by DSM's DDNS service)
  • Click “Save”

Step 4.2: Enable Custom DDNS Provider
  • You should be returned to the “External Access” view.
  • If not, go to: Control Panel > External Access
  • Click “Add”
  • Under “Service provider” select your custom service provider (“my-lambda-ddns”)
  • Under “Hostname” enter your domain name from Step 2 / Step2B
  • Enter the same USERNAME and PASSWORD you added to the function in Step 3.4
  • Click “OK”
  • Click “Update Now”
  • Finished!
  • See notes for known issues
1564422127140.png


Notes
In my experience, because the domain name was already registered on Route53, the update was immediate. However, propagation through DNS caches can be delayed if the domain name was pointing to another IP in the past, so in some cases you may need to give it up to 24 hours to resolve. Creating a brand-new sub-domain, as per Step 2B, should resolve immediately, unless you have switched DNS nameservers to Route53 in the past 24 hours.

For me, after clicking “Update Now” in the last step, the status comes returns an error stating “Failed to connect to the server. Please check the network connection of the server.”, this is a misleading error and a known bug, so don’t worry, it still works! You can verify it worked by checking the A record for your domain in Route53 matches the IP in the “External address” column of the table shown in Step 4.2.

If you are having any issues with this configuration, the first step is to check the CloudWatch logs for Lambda. A link can be found at the top of the page where we created the Lambda function. You may want to add some console.log() statements to inspect your query parameters, etc.



Latest reviews

Super useful saved me a ton of work
Upvote 0
Worked excellent, thanks a lot to the author!
Note: I had to change the response body in the lambda to 'good' to make it work. But this solution is so elegant, I couldn't have come up with this by myself.
Upvote 0
Back
Top