使用集成安全性从 Linux Docker 容器认证 SQL Server 的 .NET Core 客户端





5.00/5 (17投票s)
演示了在 Linux 容器中运行的 .NET Core 应用程序连接到使用集成安全性的 SQL Server 数据库的实现。
面临的问题
在 Docker 化使用 SQL Server 的 .NET 应用程序时,必须考虑连接到数据库的方式。
幸运的是,Microsoft 提供了几种建立安全可靠连接的途径,这些途径同样适用于 Windows 容器。然而,当需要从 Linux 容器连接时,并非所有这些概念都能开箱即用。这也影响了集成安全性在从已认证客户端(例如:单一登录系统)建立连接时的使用。
本文将集成安全性概念(该概念建立在 Kerberos 认证过程之上)应用于 Linux 容器。该解决方案无需更改 .NET Core 应用程序的代码。相反,它演示了 Docker 镜像的准备和系统级别的 Kerberos 认证配置。最终,您可以从已认证的 Linux 容器通过集成安全性连接到 SQL Server。
一步一步发现解决方案
为了找到解决方案,我们需要走一条更长的路,拼凑出一个更大的图景,然后才能创造出最终的解决方案。
必备组件
在您的环境中重建解决方案所需的内容
- Docker 主机(例如:Windows 上的 Docker、Docker Toolbox 等)
- 启用了集成安全性的 SQL Server 实例(本地部署或甚至是 Docker 化)
- 域控制器(充当密钥分发服务器的 AD)
我们的演示应用程序
为了验证数据库连接,我创建了一个非常简单的解决方案。该应用程序无限循环查询 SQL Server 实例 myDataBaseServer
的数据库 myDataBase
中的表 dat.MyDataTable
。检索到的结果集(我假设 MyDataTable
至少包含三列)将作为控制台输出打印出来,可以通过附加到容器来查看。
using System;
using System.Data.SqlClient;
namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
while (true)
{
try
{
using (var connection = new SqlConnection("Server=tcp:myDataBaseServer,46005;
Initial Catalog=myDataBase;Integrated Security=true;;"))
{
var command = new SqlCommand
("SELECT TOP 10 * FROM dat.myDataTable", connection);
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"{reader[0]}:{reader[1]} ${reader[2]}");
}
}
}
}
catch (Exception ex)
{
Console.Write(ex);
}
System.Threading.Thread.Sleep(10000);
}
}
}
}
请注意指定集成安全性的连接字符串
"Server=tcp:myDataBaseServer,46005;Initial Catalog=myDataBase;Integrated Security=true;;"
在容器中准备 Kerberos 认证
为了在 Linux 容器外部与密钥分发中心 (KDC) 通信,需要对容器镜像和配置进行一些准备。
- 安装用于 KDC 消费的包。
- 创建适当的 krb5.conf 文件以访问托管在 AD 域控制器中的 KDC 应用程序
- 生成 keytab 文件(以避免明文暴露密码)
KDC 消费所需的包
演示应用程序运行在 microsoft/dotnet:aspnetcore-runtime
镜像上,该镜像基本上基于 debian:stretch-slim
镜像。这要求我们安装以下包(代码粘贴自底层 dockerfile
)
...
RUN apt install -y krb5-config
RUN apt-get install -y krb5-user
...
这些包使我们能够在容器内运行 kinit
命令,从 KDC 获取 Kerberos 票据。此外,还提供了 ktutil
工具来创建上述 keytab
文件。
创建适当的 krb5.conf 文件
为了与底层 KDC 通信,创建了一个适当的 krb5.conf 文件并将其存储在容器的 /etc 文件夹中。为了构建一个合适的示例,假设我们处于 my.company.local
域,并且我们的 KDC 是 mydomaincontroller.my.company.local
(在 Windows AD 驱动的环境中,您可以通过在 AD 中登录后运行命令 echo %logonserver%
来获取 mydomaincontroller
的值)。还必须记住 KDC 位于域控制器中,并使用 AD 数据库。
# /etc/krb5.conf -- Kerberos V5 general configuration.
# $Id: krb5.conf,v 1.43 2011/09/23 00:37:20 eagle Exp $
#
# This is my default Kerberos v5 configuration file. The
# canonical location of this file is http://www...
#
# This configuration allows any enctypes. Some systems with really old
# Kerberos software may have to limit to triple-DES and DES.
[appdefaults]
default_lifetime = 25hrs
krb4_convert = false
krb4_convert_524 = false
ksu = {
forwardable = false
}
pam = {
minimum_uid = 100
forwardable = true
}
pam-afs-session = {
minimum_uid = 100
}
[libdefaults]
default_realm = MY.COMPANY.LOCAL
ticket_lifetime = 25h
renew_lifetime = 7d
forwardable = true
noaddresses = true
allow_weak_crypto = true
rdns = false
[realms]
MY.COMPANY.LOCAL = {
kdc = mydomaincontroller.my.company.local
default_domain = my.company.local
}
[domain_realm]
my.company.local = MY.COMPANY.LOCAL
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
krb5.conf 文件必须位于容器内的 /etc/kerb5.conf。
生成 keytab 文件
为了避免将密码传递给 kinit
命令(例如:执行 kinit username
然后输入密码),我决定生成适当的 keytab
文件。这些文件用于在不需要明文密码的情况下获取 Kerberos 票据。要生成这样的 keytab
,您需要在 Linux shell 中运行以下命令
ktutil
ktutil: add_entry -password -p myUserName@MY.COMPANY.LOCAL -k 1 -e RC4-HMAC
# ktutil will prompt for entering the password ...
ktutil: write_kt myUserName.keytab
ktuilt: extit
接收到的 keytab
文件可以映射或复制到容器中。在使用 keytab
文件请求 Kerberos 票据时,您可以运行
kinit myUserName -k -t myUserName.keytab
通过运行 klist
,您可以看到已经收到了 Kerberos 票据。
Docker 化演示应用程序
现在我们准备好了,可以看看 Docker 文件了
FROM microsoft/dotnet:sdk AS build-env
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
# Install krb5 packages
RUN apt-get update
RUN apt-get remove krb5-config krb5-user
RUN apt install -y krb5-config
RUN apt-get install -y krb5-user
# Copy kerberso configuration and keytab
COPY krb5.conf /etc/krb5.conf
COPY myUserName.keytab /app/myUserName.keytab
# copy the launch script
COPY launch.sh /launch.sh
ENTRYPOINT /launch.sh
dockerfile
的作用可以描述为:
- 在基于
microsoft/dotnet:sdk AS build-env
镜像的容器中构建 .NET Core 应用程序 - 将构建的工件复制到运行时镜像(从
microsoft/dotnet:aspnetcore-runtime
) - 为
debian:stretch-slim
镜像(这是microsoft/dotnet:aspnetcore-runtime
的基础)安装 Kerberos 包 - 复制 krb5.conf 文件和
keytab
- 复制一个脚本,该脚本执行
kinit
并启动 .NET 应用程序 - 将启动脚本指定为入口点
启动脚本 launch.sh 必须包含 Kerberos 认证(kinit
命令)和应用程序的执行
...
kinit myUserName-k -t myUserName.keytab
dotnet MyApplication.dll
处理 Kerberos 票据的过期
通过 kinit
命令收到的每个 Kerberos 票据都有一个过期日期。为了避免在容器运行时丢失认证,必须在票据过期之前再次调用 kinit
。换句话说,在容器的整个生命周期中必须定期执行 kinit
。
处理 Keytab 文件的过期
与 Kerberos 票据类似,keytab
文件也会过期。您必须以周期性的方式创建并提供新的 keytab
文件。一个可能的选项是在容器启动过程中创建该文件并将其映射到容器的文件系统中。然而,根据底层环境的不同,也可能有其他选项。
运行它
要运行该应用程序,您必须创建(使用上面的 dockerfile
)一个镜像并运行一个容器。当您附加到 Docker 容器时,您可以看到数据库查询的结果,这些结果以控制台输出的形式写入。
历史
- 2019 年 1 月 10 日:创建文章