Containers

Using Windows Authentication with Linux Containers on Amazon ECS

Windows (or Integrated) Authentication is the recommended mechanism for clients and applications to connect to SQL Server databases, but using Windows Authentication can be challenging when running containerized workloads. Typically, Windows Authentication clients are joined to the same domain as the SQL Server database, but since individual containers are ephemeral, joining them to a domain is not optimal.

This post shows how to configure a Linux container running on Amazon Elastic Container Service (Amazon ECS) to connect to a SQL Server database using Windows Authentication, without joining the container to the domain. The example will use AWS Fargate to run the containers, but this solution could be deployed onto a different container runtime or control plane with minor modifications.

Overview

This example is based on a managed Active Directory running in AWS Directory Service for Microsoft Active Directory. You will create a user in the Active Directory, then give that user access to a SQL Server database that is joined to the directory, running in Amazon Relational Database Service (Amazon RDS). The directory user’s credentials will be stored in AWS Secrets Manager.

Then, you will deploy an ECS service containing a Fargate task with two containers. One container contains a script that retrieves the directory user’s credentials from Secrets Manager and generates a Kerberos ticket by authenticating against the Active Directory. This ticket renewal “sidecar” container stores the Kerberos ticket in Fargate task storage, an ephemeral storage volume shared by all containers in a Fargate task. The other container in the task serves a web application that reads the Kerberos ticket from Fargate task storage and connects to the database using Windows Authentication.

Architecture of the solution "Using Windows Authentication with Linux Containers on Amazon ECS"

Solution

The sample code uses the AWS Cloud Development Kit (CDK) to provision cloud resources using TypeScript. The CDK also supports other familiar programming languages, including JavaScript, Python, Java, and C#.

The steps to deploy the solution include:

  • Deploying the networking infrastructure, Active Directory, and ECS cluster.
  • Deploying the database in Amazon RDS and configuring it to authenticate against the Active Directory.
  • Building the Docker container images for the web application and the Kerberos renewal sidecar and pushing them to repositories hosted in Amazon Elastic Container Registry (Amazon ECR).
  • Creating an ECS service with a Fargate task that includes both containers.

Code for this solution is available in GitHub.

Prerequisites

For this tutorial, you should have the following prerequisites:

Solution overview

Create a directory for the solution. Clone the Git repository at https://github.com/aws-samples/amazon-ecs-windows-authentication-blog into the solution directory.

The solution is composed of two CDK apps: a shared resource app and an app for the website. The dependencies between the CDK apps are minimal. The website app depends on the virtual private cloud (VPC) and ECS cluster created by the shared resources app. This facilitates a microservices architecture where each microservice is defined independently (while sharing an ECS cluster).

The shared resource CDK app is located in the solution directory under /cdk. The website CDK app is located in the solution directory under /web-site/cdk.

The Kerberos ticket renewal sidecar container is located in the solution directory under /kerberos-renewal-sidecar. This container does not have its own CDK app because it is not deployed on its own. Instead, it is incorporated into the website CDK app.

Deploy shared resources

The shared resources script will create a new Active Directory. Open a command prompt in the solution directory, then run the following commands:

export CDK_DEFAULT_ACCOUNT=AWS_ACCOUNT_ID
export CDK_DEFAULT_REGION=AWS_REGION
cd SOLUTION_DIRECTORY/cdk
npm install
cdk bootstrap
cdk deploy --parameters keyPairName=KEY_PAIR_NAME

(Throughout this tutorial, replace RED TEXT with the appropriate value.) The AWS_ACCOUNT_ID value is your numeric AWS account ID, and the AWS_REGION value is the region identifier that you will deploy resources into, such as us-east-1, eu-west-2, etc.

The new directory’s domain name is directory.ecs-kerberos-sample.com. If you change the domain name in the CDK script, use that domain name in all subsequent commands and settings.

The CDK output includes:

  • The Directory ID of the new Active Directory.
  • The Security Group ID of a security group that controls access to an Amazon EC2 instance that you will use to configure the Active Directory.
  • The Amazon Resource Name (ARN) that uniquely identifies the AWS Secrets Manager secret containing the Active Directory admin user password.

Copy all of these outputs for future use.

Grant access to the EC2 instance

The CDK script created an EC2 instance that you will use to configure the Active Directory. At the moment, this EC2 instance will block your incoming connections because there is no Security Group rule allowing access.

To add a rule to the EC2 instance’s Security Group, you can use the EC2 page in the AWS Management Console, or use the AWS CLI. To use the CLI, you will need to know your IP address; AWS provides a feature that will return it at checkip.amazonaws.com. Run the following commands to get your IP address and update the security group:

AWS_IP_ADDRESS=$(curl checkip.amazonaws.com)
aws ec2 authorize-security-group-ingress --protocol tcp --port 3389 --cidr "$AWS_IP_ADDRESS/32" --group-id DIRECTORY_MANAGEMENT_INSTANCE_SECURITY_GROUP_ID

Get the Active Directory admin password

The CDK script stored the password for the Active Directory admin user in AWS Secrets Manager. You will need this password in a later step, so run the following command:

aws secretsmanager get-secret-value --secret-id /ecs-kerberos-sample/active-directory-administrator-password

The admin password is the value of the SecureString key. Copy this password for future use.

Review VPC DHCP Options Set

The CDK app deployed a DHCP Options Set that uses the Active Directory’s DNS servers for all resources deployed into the VPC. You can verify this in the AWS Management Console, or by running the following commands:

DHCP_OPTIONS_ID=$(aws ec2 describe-vpcs --filters Name="tag:Name",Values="ecs-kerberos-stack/vpc" --output text --query 'Vpcs[*].DhcpOptionsId')
aws ec2 describe-dhcp-options --dhcp-options-ids $DHCP_OPTIONS_ID --output yaml

Note the domain-name-servers values, which should match the directory’s DNS address values in the console.

Example of the Directory Details in the AWS Management Console, with the Directory ID and DNS addresses fields highlighted

Deploy the database

The sample application is composed of two containers and a database. You will deploy the database first, then configure the application code to connect to it.

In the command prompt, run the following commands:

cd SOLUTION_DIRECTORY/web-site/cdk
ecs-kerberos-sample--web-database-stack

The CDK deployment will output the database instance identifier, instance endpoint address (a host name ending in rds.amazonaws.com), and the Amazon Resource Name (ARN) of the secret containing the database administrator credentials. Copy these values for later use.

Install the sample database

To deploy the sample database, you will need the credentials for the database’s administrator user. RDS stores these credentials in the AWS Secrets Manager. You can either retrieve them from the console, or run the following command:

aws secretsmanager get-secret-value --output yaml --secret-id DB_CREDENTIALS_SECRET_ARN

If you use the console, click the secret and then click “Retrieve secret value” to show the username and password. If you use the command line, the RDS administrator username and password will be displayed in the SecretString portion of the output.

The RDS instance has been deployed in a private subnet, so the database is not accessible from the internet. The first CDK deployment included an EC2 instance that you will use to connect to the RDS instance and deploy a sample database.

Return to EC2 in the console and select the EC2 instance named ecs-kerberos-stack/active-directory-management-instance, then follow these instructions to connect to the instance using Remote Desktop. Use the private key that matches the keyPairName parameter value from the first CDK deployment.

In the Remote Desktop session, download and install the following tools from Microsoft in order:

Once the tools are installed, click on the Start menu and then click “Windows PowerShell”.

The web application uses the Chinook sample database. Run the following commands in PowerShell to download the sample database:

mkdir ChinookSetup
cd .\ChinookSetup
Invoke-WebRequest -Uri https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_SqlServer_AutoIncrementPKs.sql -OutFile Chinook.sql

Run the following PowerShell command to install the sample database. Note that the database password must be in quotes.

sqlcmd -S RDS_INSTANCE_ENDPOINT_ADDRESS -U RDS_ADMINISTRATOR_USERNAME -P 'RDS_ADMINISTRATOR_PASSWORD' -i Chinook.sql

Configure Active Directory authentication to the sample database

Run the following PowerShell command to install the Active Directory management tools:

Install-WindowsFeature -Name "RSAT-AD-Tools" -IncludeAllSubFeature

Once the tools are installed, run the following command:

$credential=Get-Credential

You will be prompted for a username and password. Enter admin@directory.ecs-kerberos-sample.com as the username. The password is the Active Directory administrator password that you retrieved from Secrets Manager after you deployed the first CDK stack.

Next, run this PowerShell command to create the Active Directory user that will connect to the database.

New-ADUser -Name "db-user" -UserPrincipalName "db-user@directory.ecs-kerberos-sample.com" -Credential $credential -Server directory.ecs-kerberos-sample.com

Create a secure password for db-user, then run the following PowerShell command:

Set-ADAccountPassword -Identity db-user -Reset -Credential $credential -Server directory.ecs-kerberos-sample.com

When prompted, enter the password for the user db-user.

Run the following PowerShell command to enable the user:

Enable-ADAccount -Identity "db-user" -Credential $credential -Server directory.ecs-kerberos-sample.com

Now, you will give db-user access to the RDS database. Run the following PowerShell command to start a SQL prompt on the RDS instance:

sqlcmd -S RDS_INSTANCE_ENDPOINT_ADDRESS -U RDS_ADMINISTRATOR_USERNAME -P 'RDS_ADMINISTRATOR_PASSWORD'

When the SQL prompt appears, run the following commands:

CREATE LOGIN [directory\db-user] FROM WINDOWS WITH DEFAULT_DATABASE = [Chinook]
GO
USE Chinook
GO
CREATE USER [directory\db-user] FOR LOGIN [directory\db-user] WITH DEFAULT_SCHEMA=[dbo]
GO
EXEC sp_addrolemember N'db_owner', N'directory\db-user'
GO
EXIT

Log out of the Remote Desktop session.

Store the db-user credentials in the AWS Secrets Manager by running the following command in the command prompt:

aws secretsmanager create-secret --name /ecs-kerberos-sample/web-site/db-user-credentials --secret-string '{"username":"db-user","password":"DB-USER_PASSWORD"}'

Copy the value of the “ARN” key in the output of this command and store it for use in a future step.

Review the Kerberos configuration

Open the file SOLUTION_DIRECTORY/web-site/krb5.conf in a text editor. This is a standard Kerberos configuration file. You will not make any changes to this file, but it’s important to understand how authorization is configured.

[logging]
default = STDERR

[libdefaults]
dns_lookup_realm = true
dns_lookup_kdc = true
forwardable = true
rdns = false
default_ccache_name = FILE:/var/scratch/krbcache
default_realm = DIRECTORY.ECS-KERBEROS-SAMPLE.COM

[realms]
DIRECTORY.ECS-KERBEROS-SAMPLE.COM = {
  kdc = directory.ecs-kerberos-sample.com
  admin_server = directory.ecs-kerberos-sample.com
}

Note that the [realms] entry is the domain name of the Active Directory in all caps, and the kdc and admin_server values are the domain name of the Active Directory. Because of the VPC Options Set, this domain name will only resolve inside the VPC.

The default_ccache_name setting specifies where Kerberos tickets are stored (in this case, /var/scratch/krbcache). The CDK script creates a volume for the Fargate task and mounts this volume into /var/scratch in both containers, enabling the containers to share this storage.

The kerberos-renewal-sidecar directory also contains a krb5.conf configuration file, with settings that match the web-site configuration.

Build Docker Container images

Open the file SOLUTION_DIRECTORY/web-site/appsettings.Development_AWS.json in a text editor. Replace the string {{RDS_INSTANCE_IDENTIFIER}} with the instance identifier of the RDS instance. The server name in the connection string should end in the name of the directory. If your database instance identifier is foo, the server should be foo.directory.ecs-kerberos-sample.com. It’s important to use this host name, and not the RDS instance endpoint address.

Note that the connection string does not include credentials, instead specifying Integrated Security=true.

Run the following command to create an ECR repository for the website container images:

cd SOLUTION_DIRECTORY/web-site
aws ecr create-repository --repository-name ecs-kerberos-sample/web-site

After creating the repository, go to ECR in the console and click the ecs-kerberos-sample/web-site repository. Click the “View push commands” button and follow the directions to tag and push your image to the ECR repository.

Example ECR push commands in the AWS Management Console

Next, create the Docker image of the Kerberos renewal sidecar container. Run the following commands:

cd SOLUTION_DIRECTORY/kerberos-renewal-sidecar
aws ecr create-repository --repository-name ecs-kerberos-sample/kerberos-renewal-sidecar

After creating the repository, go to ECR in the console and click the ecs-kerberos-sample/kerberos-renewal-sidecar repository. Click the “View push commands” button and follow the directions to tag and push your image to the ECR repository.

Deploy the website ECS service

Run the following commands in the command prompt:

cd SOLUTION_DIRECTORY/web-site/cdk
cdk deploy --parameters databaseCredentialsSecretArn='DB-USER_CREDENTIALS_SECRET_ARN' ecs-kerberos-sample--web-application-stack

This CDK script deploys a Fargate service to the ECS cluster. The service consists of a single task that includes two containers: one for the website and one for the Kerberos renewal sidecar. The Kerberos renewal sidecar uses the database credentials secret to authenticate to the Active Directory and generate a Kerberos ticket. The Kerberos ticket is stored in a shared volume that both containers can access. The web application reads the Kerberos ticket from the shared volume when it connects to the database.

The CDK script deploys a public-facing load balancer in front of the Fargate service, and outputs the load balancer’s URL. You can also find the load balancer’s DNS name on the Load Balancer page in the console. Test the application by visiting the load balancer’s URL in a web browser. You should see a “Hello World” message and a list of 5 albums selected at random from the database.

Cleaning up

To avoid incurring future charges, delete the resources. You can use the cdk destroy command to delete the stacks, in the reverse order you deployed them:

  1. cdk destroy ecs-kerberos-sample--web-application-stack
  2. cdk destroy ecs-kerberos-sample--web-database-stack
  3. cdk destroy ecs-kerberos-stack

You can also navigate to the CloudFormation page in the console to delete the stacks.

In addition to deleting the stacks, you will need to delete the ECR repositories and the secrets.

Conclusion

In this post, you deployed a containerized workload on Amazon ECS that connects to a SQL Server database using Integrated Authentication, which is the recommended mechanism, without joining individual containers to the domain. The authentication for this solution is based on an AWS Managed Active Directory, which serves as the authentication source.