I have a .NET Core application running on Linux container using AWS ECS Fargate.
How to connect from that application to SQL Server using integrated security?
Step 1: ensure that SQL Server supports Kerberos authentication # Using SQL Server Management Studio (SSMS), connect to your database and execute following statement:
select auth_scheme
from sys.dm_exec_connections
where session_id = @@spid
If result of the query is KERBEROS
, you are all set and proceed to Step 2. Otherwise, if result is NTLM
, this means that Kerberos authentication failed, and SSMS silently fell back to using NTLM authentication. Since integrated security between SQL Server and clients running in Linux environments solely rely on Kerberos authentication, this issue must be addressed first.
Note: when connecting to SQL Server, it is important to use server hostname or FQDN instead of IP address, otherwise Kerberos authentication will not work.
Check SPN configuration
Ensure that SPN is properly configured for SQL Server.
Microsoft has also released several diagnostic tools that can help with SPN verification and configuration:
Last but not least, you can use following setspn
command to query for a specific SPN(s):
setspn -T CONTOSO.COM -F -Q MSSQLSvc/your_sql_server.contoso.com
Query above does support *
for a wildcard (replace CONTOSO.COM
with your domain)
Configure encryption types allowed for Kerberos
Within Active Directory, find an account under which SQL Server is running. Under Account tab and Account options section, confirm that applicable Kerberos cyphers are selected.
Example:
Step 2: Configure ECS Task To use Kerberos authentication, Application Task within ECS Service will be composed of two containers:
kinit
command.Both containers will mount the same volume. 1st container will cache/write TGT ticket to it, 2nd container will read cached TGT ticket from it.
TGT acquisition container (sidecar container)
There are only 3 files needed to setup TGT acquisition container:
# krb5.conf
[libdefaults]
dns_lookup_realm = true
dns_lookup_kdc = true
forwardable = true
default_ccache_name = FILE:/var/kerberos/krbcache # TGT cache location
default_realm = CONTOSO.COM
permitted_enctypes = aes256-cts aes128-cts
[realms]
CONTOSO.COM = {
kdc = CONTOSO.COM
admin_server = CONTOSO.COM
}
[domain_realm]
.contoso.com = CONTOSO.COM
contoso.com = CONTOSO.COM
[logging]
default = STDERR
# renew.sh
#!/bin/bash
# Refresh the token periodically.
# Set the length of time that the script will wait to refresh the token.
[[ "$DELAY_SECONDS" == "" ]] && DELAY_SECONDS=3600
# If the AWS region hasn't been set, get it from instance metadata. This will work in an instance as well as in an ECS container.
[[ "$AWS_REGION" == "" ]] && AWS_REGION=$(curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
# Use the ECS container as the source for AWS credentials. This allows the AWS CLI to use the permissions of the task role.
aws configure set credential_source EcsContainer
while true
do
echo "Starting ticket renewal at: " + $(date)
# Get the credentials from Secrets Manager.
CREDENTIALS_SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id $CREDENTIALS_SECRET_ARN --region $AWS_REGION --query SecretString --output text)
# Use `jq` to parse the credentials into username & password.
CREDENTIALS_USERNAME=$(echo $CREDENTIALS_SECRET_VALUE | jq -r '.username')
CREDENTIALS_PASSWORD=$(echo $CREDENTIALS_SECRET_VALUE | jq -r '.password')
# Use the username & password to authenticate to Kerberos. The resulting token is written to the token cache,
# which is set up in `krb5.conf` to use the task scratch volume, shared by all containers.
echo $CREDENTIALS_PASSWORD | kinit $CREDENTIALS_USERNAME -f -V $OPTIONS
echo "Ticket renewal complete, waiting for $DELAY_SECONDS seconds"
sleep $DELAY_SECONDS &
wait
done
# Dockerfile
FROM amazonlinux:2
COPY renew.sh /
COPY krb5.conf /etc/krb5.conf
# Install the Kerberos tools -- to authenticate;
# `jq` -- to parse the credentials from the AWS Secrets Manager, which returns JSON
# `unzip` -- to install the latest version of the AWS CLI
RUN yum install -y krb5-workstation jq unzip
# Download and install the latest version of the AWS CLI
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN ./aws/install
VOLUME ["/var/kerberos"]
ENTRYPOINT ["/renew.sh"]
Based on value specified in CREDENTIALS_SECRET_ARN
, renew.sh will periodically renew TGT and cache/save it in location specified at krb5.conf file (e.g. /var/kerberos/krbcache
).
To test that container successfully acquires TGT for a given principle, establish interactive session with the container and execute klist
command. When successful, you should see the details of TGT ticket, containing principle name, expiration date, etc.
Application Container
Application container runs your .NET Core application. To enable Kerberos on that container add following lines in Dockerfile:
...
RUN apt update
RUN apt install -y krb5-config krb5-user
COPY krb5.conf /etc/krb5.conf
VOLUME ["/var/kerberos"]
...
The content of krb5.conf file should be identical to one in TGT acquisition container, and it will instruct application to locate Kerberos TGT at FILE:/var/kerberos/krbcache
.
Your application SQL connection string should look similar to this:
Server=yourSqlServer.contoso.com;Initial Catalog=YourDB;Integrated Security=true;
To test that container has access to cached TGT, establish interactive session with the container and execute klist
. When successful, you should see same TGT ticket, as in another container.
If everything went well, you should be able to successfully connect to SQL Server using Integrated Security from your .NET Core application running on Linux.
Additional resources