Docker, AspNetCore, рекомендации по использованию строки подключения к БД
Я провел последнюю неделю или около того, пытаясь изучить докер и все, что он может сделать, однако одна вещь, которую я пытаюсь обдумать, это лучшая практика о том, как управлять секретами, особенно в отношении строк подключения к базе данных и как они должны храниться.
У меня в голове есть план, где я хочу иметь образ докера, который будет содержать веб-сайт ASP.NET Core, базу данных MySQL и интерфейс PHPMyAdmin, и развернуть его на капле, которую я имею в DigitalOcean.
Я немного поигрался, и у меня есть файл 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 для меня, и я могу подключиться к ней с работающим внешним интерфейсом PHPMyAdmin, используя root / 0001 в качестве комбинированного имени пользователя / пароля.
Я знаю, что теперь мне нужно добавить к этому свое веб-приложение AspNetCore, но я все еще нахожусь в тупике из-за лучшего способа получить пароль БД.
Я изучил рой / секреты Docker, но до сих пор не до конца понимаю, как это работает, особенно если я хочу проверить свой файл docker-compose в GIT/SCM. Другие вещи, которые я прочитал, предложили использовать переменные среды, но я все еще не понимаю, как это отличается от простой проверки строки подключения в моем файле appsettings.json или, в этом отношении, как это будет работать в полный конвейер сборки CI/CD.
Этот вопрос помог мне немного разобраться в этом вопросе, но у них все еще есть пароль БД в файле docker-compose.
Может быть, я пытаюсь переосмыслить это
Любая помощь, руководство или предложения будут с благодарностью приняты.
1 ответ
Если вы используете Docker Swarm, вы можете воспользоваться функцией секретов и сохранить всю конфиденциальную информацию, такую как пароли или даже всю строку подключения, как секрет докера.
Для каждого созданного секрета 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" я подробно объясняю, как создать настраиваемый поставщик конфигурации, который позволяет хранить в секрете только пароль и обновлять строку конфигурации во время выполнения. Также полный исходный код этой статьи размещен на https://www.github.com/gabihodoroaga/blog-app-secrets.
Секреты сложны. Я скажу, что включение их в переменные среды немного поднимает проблему, особенно когда вы используете только docker-compose (а не что-то более изощренное, например, kubernetes или swarm). Ваш файл docker-compose.yaml будет выглядеть примерно так:
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
Compose будет извлекать MYSQL_ROOT_PASSWORD из файла.env или переменной командной строки / среды, когда вы раскручиваете свои сервисы. Большинство служб CI/CD предоставляют способы (либо через графический интерфейс, либо через некоторый интерфейс командной строки) шифрования секретов, которые сопоставляются с переменными среды на сервере CI.
Нельзя сказать, что переменные среды обязательно являются лучшим способом обработки секретов. Но если вы перейдете на платформу оркестровки, такую как kubernetes, будет прямой путь к сопоставлению секретов kubernetes с теми же переменными среды.