AWS Developer Blog

Automating the Deployment of Encrypted Web Services with the AWS SDK for PHP

by Joseph Fontes | on | in PHP | Permalink | Comments |  Share

Having worked in the web hosting space, one of the areas I find so fun about AWS is the ease of automating tasks that have historically been quite disjointed.  The process of supporting a customer request to register a domain, create or update DNS entries, configure the load balancer, deploy servers, etc., had me working across a multitude of systems, interfaces, and APIs.  Now, with the release of AWS Certificate Manager (ACM) in addition to existing AWS services, AWS provides all the tools and capabilities needed to support the provisioning of these services within customer accounts.

In this three-part series of posts, we’ll review how to use the AWS SDK for PHP to automate web service deployment, domain registration, DNS administration, and SSL certificate generation and assignment.  Using the examples outlined in these posts, as well as other features and functions of the AWS SDK for PHP, you’ll learn how to programmatically create a process for automatically purchasing a domain and then deploying an HTTPS (SSL-secured) web service on AWS by using either Amazon EC2 or AWS Elastic Beanstalk.

The examples in this post focus on using Amazon Route 53 to automate the registration of domain names and DNS administration.  Next, we’ll showcase how to use ACM to create and manage SSL certificates.  In subsequent posts, we will show how to automate the setup of encrypted HTTPS web services with the new domain and newly created certificates on Elastic Beanstalk.  Then, we’ll show how to automate deployments to EC2 and Elastic Load Balancing.  Once complete, we’ll have two web application stacks.  We’ll run the www.dev-null.link site from Elastic Beanstalk, and use EC2 and Elastic Load Balancing to run the second web application stack.  The following diagrams illustrate the final designs.

   

Amazon Route53 Domain Registration

The first task in building the web infrastructure is to identify and register an available domain name.  We can use the AWS SDK for PHP to check domain name availability.  We will use a method called checkDomainAvailability, which is part of the Route 53 Domains client.  We can automate the process of testing domains until we have a name that meets our application’s needs and is also available for registration.  The example below loops through an array of domain names, listing their current status for registration.

$route53Client = $sdk->createRoute53Domains();

$domainNames = [ "test.com", "dev.com", "dev-null.link", "null38.link" ];

foreach($domainNames as $domainNameElement) {
        $route53CheckDomainAvailData = [ 'DomainName' => $domainNameElement ];
        $route53CheckDomainResults = $route53Client->checkDomainAvailability($route53CheckDomainAvailData);
        print "Domain $domainNameElement is ".$route53CheckDomainResults['Availability']."n";
}

You can view the results of the check below.

There are two domain names available for registration.  In this example, we’ll register the domain dev-null.link.  This name contains the “.link” top-level domain (TLD), and the “dev-null” second-level domain. Now, register the domain by using the registerDomain method.  The registration has several required fields that we need to complete. These requirements are specific to each top level domain. For this example, we can use the following data ( provided in this Github Gist):
$route53DomainRegData = [
    'AdminContact' => [
        'AddressLine1' => $address1,
        'AddressLine2' => $address2,
        'City' => $city,
        'ContactType' => 'PERSON',
        'CountryCode' => 'US',
.....

$route53Client = $sdk->createRoute53Domains();
$route53CreateDomRes = $r53Client->registerDomain($route53DomainRegData);

print_r($route53DomainRegData);

Notice that the PhoneNumber data element must be in the format of  “+1.1231231212” to be valid.

We can now register the domain as follows.
[user@dev1 scripts]# php aws-route53-register-domain.php 
...
            [statusCode] => 200 
            [effectiveUri] => https://route53domains.us-east-1.amazonaws.com 
...

While we wait for the registration process to finish, we can check on the status of the domain.  First, use the listOperations method to print the list of current operations, and then enter the operation number into the getOperationDetail method.  Let’s look at all of the pending operations with the code.

$route53ListOperationsResults = $route53Client->listOperations();
print_r($route53ListOperationsResults);

$route53OperDetails = [ 'OperationId' => $operationId ];
$route53OperResults = $route53Client->getOperationDetail($route53OperDetails);
print_r($route53OperResults);

Result

[user@dev1 scripts]#
[user@dev1 scripts]# php aws-route53-list-operations.php
…
[Status] => IN_PROGRESS
                    [Type] => REGISTER_DOMAIN
…

AWS Certificate Manager

With ACM, we no longer have to worry about certificate expirations, securing the certificate private keys, copying self-signed CA certificates to clients, making sure servers all have the right certificate, or even the cost of a managed SSL certificate.  AWS provides managed SSL certificates at no cost.  Also, AWS handles the responsibility of renewing the certificate and placing it on the devices used to terminate SSL connections.  ACM can be used across managed AWS services such as ELB, Amazon CloudFront, and Elastic Beanstalk.

ACM in Action

Let’s go through how to create multiple certificates to secure connections to different websites.  We must first request a new certificate with the corresponding public and private keys.  The requestCertificate method automates this process.

The following example shows how to generate the certificate for our first domain.

$acmClient = $sdk->createAcm();

$acmRequestCertData = [ 'DomainName' => "www.dev-null.link",
    'DomainValidationOptions' => [
        [
            'DomainName' => "www.dev-null.link",
            'ValidationDomain' => "dev-null.link",
        ],
    ],
    'IdempotencyToken' => 'TOKENSTRINGDEVNULL01',
    'SubjectAlternativeNames' => ['dev-null.link', 'images.dev-null.link'],
];

$acmRequestCertResults = $acmClient->requestCertificate($acmRequestCertData);
print_r($acmRequestCertResults);

Result

[user@dev1 scripts]# php aws-acm-requestCertificate.php
...
    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
    [@metadata] => Array
        (
            [statusCode] => 200
...
 

SubjectAlternativeNames identifies other DNS entries that the certificate should cover.  In this example, they are all subdomains of dev-null.link but can also include other domain names that could be used synonymously with our requested domain.  You can repeat this call for any additional certificates you need.  Be sure to update the value of IdempotencyToken for each certificate request created.  You should save the value of CertificateArn because you’ll need to use it later.

We want to secure the primary hostname www.dev-null.link.  ACM requires validation for the domain from an email address that is tied to the registration information.  Domain validation requests are sent to multiple locations.  These validation emails are sent to domain email addresses in the following order: admin@dev-null.link, administrator@dev-null.link, hostmaster@dev-null.link, postmaster@dev-null.link, and webmaster@dev-null.link.  In addition, a validation request is also sent to the email contacts for the Administrative, Technical, and Domain Registrant.  The following figure shows a copy of the received email.

When you click the link in the email, you’re taken to the page shown below.

Next, click I Approve.  A confirmation page appears that you can save for your records.

Let’s now use the listCertificates and describeCertificate methods to show all of the certificates we’ve generated.

$acmListCertResults = $acmClient->listCertificates();
print_r($acmListCertResults);

Result

[user@dev1 scripts]# php aws-acm-list.php
...
    [CertificateSummaryList] => Array
...
                    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/CERTIFICATE-ID
                    [DomainName] => www.dev-null.link
...
                    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/CERTIFICATE-ID
                    [DomainName] => api.dev-null.link
...
 
You can view details about the certificates by calling the describeCertificates method with the CertificateARN received from the 
previous listCertificates call.
$certificateArn = $acmListCertResults['CertificateSummaryList'][0]['CertificateArn'];

$acmDescribeCertData = [ 'CertificateArn' => $certificateArn ];
$acmDescribeCertResults = $acmClient->describeCertificate($acmDescribeCertData);

print_r($acmDescribeCertResults);

You can view the full output here with abbreviated output shown below.

[Certificate] => ...
Array
(
        [CertificateArn] => CERTIFICATE-ARN
        [DomainName] => www.dev-null.link
        [SubjectAlternativeNames] => Array
        (
                [0] => www.dev-null.link
                [1] => dev-null.link
                [2] => images.dev-null.link
        )
...

Finally, view the full certificate with the certificate chain.

$acmGetCertificateData = [ 'CertificateArn' => $certificateArn ];

$acmGetCertificateResults = $acmClient->getCertificate($acmGetCertificateData);

print_r($acmGetCertificateResults);
Result
[Certificate] => -----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----

 [CertificateChain] => -----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
    [@metadata] => Array
        (
            [statusCode] => 200
…
This code will return the public certificate and the chain of trust leading to the public CA that is signing the certificate allowing web browsers to trust the website being visited.
To get more information about the certificate and the request process, we can use the describeCertificate method.  This method takes the certificateArn value as input and produces information about the referenced certificate.  This information includes the list of email addresses that received validation emails, the encryption algorithm used, the certificate creation and expiration dates, and the full list of host names covered by the certificate.  Looking forward, we can delete certificates and resend the validation email with the available ACM methods.
Now that we’ve covered the setup, configuration, and deployment of ACM and DNS, we now have a registered domain available for use.  In the next post, we’ll review how to use the domain, dev-null.link, for our website.  We’ll deploy an HTTPS website that is secured with SSL/TLS by using the AWS SDK with ELB, Amazon EC2, and Elastic Beanstalk.  We’ll also cover creating and deleting Amazon Route 53 resource records, and how to assign our ACM certificate to newly created load balancers.