3

过去一周左右我一直在尝试学习 docker 以及它可以做的所有事情,但是我正在努力解决的一件事是如何管理机密的最佳实践,尤其是在数据库连接字符串和这些应该如何存储。

我有一个计划,我想要一个 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 Web 应用程序添加到此,但我仍然对获得数据库密码的最佳方式感到困惑。

我看过 docker swarm/secrets,但我仍然不完全理解它是如何工作的,特别是如果我想将我的 docker-compose 文件检查到 GIT/SCM 中。我读过的其他内容建议使用环境变量,但我似乎仍然不明白这与仅在我的 appsettings.json 文件中检查连接字符串有何不同,或者就此而言,这将如何工作完整的 CI/CD 构建管道。

这个问题帮助我了解了这一点,但他们的 docker-compose 文件中仍然有他们的数据库密码。

可能是我想多想了

任何帮助、指导或建议将不胜感激。

4

2 回答 2

3

如果您使用的是 Docker Swarm,那么您可以利用机密功能并将所有敏感信息(例如密码甚至整个连接字符串)存储为 Docker 机密。

对于创建的每个秘密,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}};

并使用另一个自定义配置提供程序将其替换为存储为机密的密码。

在我的博客文章如何管理 ASP.NET Core 配置文件中的密码中,我详细解释了如何创建自定义配置提供程序,它允许您仅将密码保密并在运行时更新配置字符串。此外,本文的完整源代码托管在github.com/gabihodoroaga/blog-app-secrets上。

于 2019-11-15T18:38:03.180 回答
1

秘密很复杂。我会说将它们拉到环境变量中会稍微解决问题,尤其是当您只使用 docker-compose 时(而不是像 kubernetes 或 swarm 这样更高级的东西)。你的 docker-compose.yaml 文件看起来像这样:

environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}

当您启动服务时,Compose 将从 .env 文件或命令行/环境变量中提取 MYSQL_ROOT_PASSWORD。大多数 CI/CD 服务提供了对映射到 CI 服务器上的环境变量的机密进行加密的方法(通过 GUI 或通过某些命令行界面)。

并不是说环境变量一定是处理机密的最佳方式。但是,如果您确实迁移到编排平台,例如 Kubernetes,将有一条直接的路径将 Kubernetes 机密映射到相同的环境变量。

于 2018-03-22T20:49:38.790 回答