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.
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:
- An AWS account.
- Complete the AWS CDK getting started guide, including installing the CDK and learning the key concepts.
- Install the AWS CLI and set up your AWS credentials for command-line use.
- Create an Amazon EC2 key pair and record its name.
- Determine the public IP address of the computer that will be deploying the resources.
- Install a Microsoft Remote Desktop (RDP) client.
- Install the latest version of the Docker runtime.
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.
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:
- Visual C++ Redistributable (the x64 version,
vc_redist.x64.exe
) - ODBC Driver for SQL Server (the x64 version)
- SQLCMD utility
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.
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:
cdk destroy ecs-kerberos-sample--web-application-stack
cdk destroy ecs-kerberos-sample--web-database-stack
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.