上周我一直在尝试学习码头工作以及它可以做的所有事情,但是我努力想要解决的一件事就是如何管理机密的最佳做法,尤其是数据库连接字符串以及如何存储它们。
我有一个计划,我希望有一个docker镜像,它将包含一个ASP.NET Core网站,MySQL数据库和一个PHPMyAdmin前端,并将其部署到我在DigitalOcean的Droplet上。
我一直在玩一下,我有一个docker-compose.yml文件,它将MySQL DB和PhpMyAdmin正确链接在一起
version: "3"
services:
db:
image: mysql:latest
container_name: mysqlDatabase
environment:
- MYSQL_ROOT_PASSWORD=0001
- MYSQL_DATABASE=atestdb
restart: always
volumes:
- /var/lib/mysql
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: db-mgr
ports:
- "3001:80"
environment:
- PMA_HOST=db
restart: always
depends_on:
- db
这正确地为我创建了一个MySQL数据库,我可以使用root / 0001作为用户名/密码组合,使用正在运行的PHPMyAdmin前端连接到它。
我知道我现在需要添加我的AspNetCore网络应用程序,但我仍然难以获得数据库密码的最佳方式。
我查看了docker swarm / secrets,但我仍然不完全理解这是如何工作的,特别是如果我想将docker-compose文件检入GIT / SCM。我读过的其他内容建议使用环境变量,但我似乎还没理解如何只检查我的appsettings.json文件中的连接字符串,或者就此而言,这是如何工作的在完整的CI / CD构建管道中。
这个question帮我解决了这个问题,但是他们仍然在他们的docker-compose文件中有他们的数据库密码。
可能是我试图推翻这个
我们将非常感激地提供任何帮助,指导或建议。
答案 0 :(得分:1)
秘密很复杂。我会说把它们拉到环境变量中可以解决这个问题,特别是当你只使用docker-compose时(而不是像kubernetes或swarm这样的东西)。你的docker-compose.yaml文件看起来像这样:
UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mDevice.pressRecentApps();
mDevice.pressRecentApps();
当您启动服务时,Compose将从.env文件或命令行/环境变量中提取MYSQL_ROOT_PASSWORD。大多数CI / CD服务提供了加密秘密的方法(通过GUI或通过某些命令行界面),这些秘密被映射到CI服务器上的环境变量。
并不是说环境变量必然是处理机密的最佳方式。但是如果你转移到编排平台,比如kubernetes,那么将有一条直接的路径将kubernetes秘密映射到那些相同的环境变量。
答案 1 :(得分:1)
如果您正在使用Docker Swarm,则可以利用secrets功能并将所有敏感信息(例如密码)或整个连接字符串存储为docker secret。
对于创建的每个秘密,Docker都会在容器内安装一个文件。默认情况下,它将在/ run / secrets文件夹中挂载所有机密。
您可以创建一个自定义配置提供程序来读取机密并将其映射为配置值
public class SwarmSecretsConfigurationProvider : ConfigurationProvider
{
private readonly IEnumerable<SwarmSecretsPath> _secretsPaths;
public SwarmSecretsConfigurationProvider(
IEnumerable<SwarmSecretsPath> secretsPaths)
{
_secretsPaths = secretsPaths;
}
public override void Load()
{
var data = new Dictionary<string, string>
(StringComparer.OrdinalIgnoreCase);
foreach (var secretsPath in _secretsPaths)
{
if (!Directory.Exists(secretsPath.Path) && !secretsPath.Optional)
{
throw new FileNotFoundException(secretsPath.Path);
}
foreach (var filePath in Directory.GetFiles(secretsPath.Path))
{
var configurationKey = Path.GetFileName(filePath);
if (secretsPath.KeyDelimiter != ":")
{
configurationKey = configurationKey
.Replace(secretsPath.KeyDelimiter, ":");
}
var configurationValue = File.ReadAllText(filePath);
data.Add(configurationKey, configurationValue);
}
}
Data = data;
}
}
然后,您必须将自定义提供程序添加到应用程序配置中
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddSwarmSecrets();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
然后,如果您创建一个名称为“ my_connection_secret”的机密
$ echo "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;" \
| docker secret create my_connection_secret -
并将其作为连接字符串映射到您的服务:DatabaseConnection
services:
app:
secrets:
- target: ConnectionStrings:DatabaseConnection
source: my_connection_secret
与将其写入appsettings.config相同
{
"ConnectionStrings": {
"DatabaseConnection": "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"
}
}
如果您不想将所有连接字符串都存储为秘密,则可以使用占位符作为密码
Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd={{pwd}};
,然后使用另一个自定义配置提供程序将其替换为存储为密钥的密码。
在我的博客文章How to manage passwords in ASP.NET Core configuration files上,我详细解释了如何创建自定义配置提供程序,该提供程序允许您仅将密码保密,并在运行时更新配置字符串。另外,本文的完整源代码也托管在github.com/gabihodoroaga/blog-app-secrets上。