Secrets management в Unity: как не хранить ключи сборки и деплоя в Git
Разбираем, что считать секретом, а что конфигурацией, и как настроить Infisical для Unity‑проекта так, чтобы новый разработчик получал доступ к сборке за пару минут.
Проблемы, которые решает эта статья:
Как безопасно хранить секреты;
Как автоматически собрать production без ручного ввода паролей;
Где взять
.envновому разработчику.
В этой статье разберём подход с Infisical, Unity.
В своем опыте стартап‑команд и аутсорса, несмотря на отделы контроля, я встречал хард‑код секретов, либо нежелание какой‑либо автоматизации процесса — ввод данных для подписи Android сборки при каждой сборке по стандартным механизмам Unity.
Вероятно, такое приходилось видеть, потому что в Unity нет штатной интеграции с .env, и разработчики, которые работали только с играми могут о таком понятии и не знать.
1. Как хранят секреты
Существует соглашение: хранить секреты в файле .env в корне проекта, добавляя его в .gitignore, чтобы он не попал в репозиторий git.
Чтобы .env не попадал в git добавьте в .gitignore:
.env
.env.*
!.env.exampleПочему секреты не хранят в репозитории:
Компрометация при доступе. Передавая репозиторий третьим лицам (например, фрилансерам) или случайно сделав его публичным, вы мгновенно дарите злоумышленникам доступ к своей инфраструктуре.
История коммитов. Удаление секретов из кода свежим коммитом не поможет — они останутся в
git history. Чтобы их вычистить, придется переписывать историю репозитория.Нарушение безопасности (Least Privilege). Все, у кого есть доступ к коду (включая стажеров), автоматически получают доступ к боевым базам данных и платным API.
Пример .env на Unity проекте выглядит примерно так:
ANDROID_KEYSTORE_PASSWORD=secret
ANDROID_KEY_ALIAS=release
ANDROID_KEY_PASSWORD=secret
ADDRESSABLES_SSH_HOST=cdn.example.com
ADDRESSABLES_SSH_USER=deploy
ADDRESSABLES_SSH_PRIVATE_KEY=/Users/me/.ssh/game_deploy
TELEGRAM_BOT_TOKEN=123456:ABC...
TELEGRAM_CHAT_ID=123456789
И шаблон:
ANDROID_KEYSTORE_PASSWORD=
ANDROID_KEY_ALIAS=
ANDROID_KEY_PASSWORD=
ADDRESSABLES_SSH_HOST=
ADDRESSABLES_SSH_USER=
ADDRESSABLES_SSH_PRIVATE_KEY=
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
В Git хранится только .env.example, а .env игнорируется
В этом примере.env есть данные для подписи Unity Android сборки, данные SSH для подключения к серверу для загрузки Addressables сборки (догружаемых данных приложения), токен для оповещения в Telegram об окончании сборки.
В Unity не часто используют.env, потому что для того, чтобы хранить данные ключ‑значение — можно создать конфиг ScriptableObject, который можно будет смотреть и редактировать прямо из Unity. Это правда, и редактирование из Unity — плюс. В такие конфиги обычно пишут URL сервера и ключи для аналитики. Однако — не всякая конфигурация является секретом.
Что вообще считать секретом?
Перед тем как подключать secrets manager, стоит разобраться, какие данные туда действительно нужно помещать.
Пример конфигурации (не секрета):
ServerUrl = https://api.example.com
AddressablesUrl = https://cdn.example.comЕсли Unity‑клиент обращается к серверу по этому адресу, пользователь всё равно сможет узнать его из приложения или сетевого трафика.
Поэтому production‑конфигурацию вполне нормально хранить в Git. В Unity для этого отлично подходят ScriptableObject.
Секретом становится значение, утечка которого позволяет получить доступ, выполнить действие от имени команды или подписать/опубликовать артефакт.
Например:
Данные | Секрет? | Где хранить |
|---|---|---|
| Нет |
|
| Нет |
|
Keystore password | Да | Infisical |
Key alias password | Да | Infisical |
SSH private key | Да | Infisical |
Telegram bot token | Да | Infisical |
CI access token | Да | Infisical |
2. Как автоматически собрать production без ручного ввода паролей
Предлагаемая структура проекта:
UnityGame/
├── Assets/
│ └── Config/ // Конфигурация
│ ├── DevelopmentConfig.asset
│ ├── StagingConfig.asset
│ └── ProductionConfig.asset
│
├── .env (git-ignored) // Секреты
├── .env.example
└── .gitignore
Конфигурация открыто хранится в Git:
DevelopmentConfig.asset
ServerUrl = <https://dev-api.example.com>
StagingConfig.asset
ServerUrl = <https://staging-api.example.com>
ProductionConfig.asset
ServerUrl = <https://api.example.com>
Чтение.env из Unity
Можно сделать Editor‑only loader: Github Gist
using System;
using System.Collections.Generic;
using System.IO;
public static class EnvironmentLoader
{
public static Dictionary<string, string> Load(string path)
{
if (!File.Exists(path))
throw new FileNotFoundException(
$".env not found: {path}");
var result = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase);
foreach (var line in File.ReadAllLines(path))
{
var trimmed = line.Trim();
if (string.IsNullOrEmpty(trimmed))
continue;
if (trimmed.StartsWith("#"))
continue;
var separator = trimmed.IndexOf('=');
if (separator <= 0)
continue;
var key = trimmed[..separator].Trim();
var value = trimmed[(separator + 1)..].Trim();
if (value.StartsWith("\"") &&
value.EndsWith("\""))
{
value = value[1..^1];
}
result[key] = value;
}
return result;
}
public static string Required(
Dictionary<string, string> env,
string key)
{
if (!env.TryGetValue(key, out var value) ||
string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException(
$"Required environment variable is missing: {key}");
}
return value;
}
}
Теперь можно программно настроить Android signing: Github Gist
using UnityEditor;
using UnityEngine;
public static class AndroidSigningConfig
{
[MenuItem("Build/Load Signing Credentials")]
public static void Load()
{
var env = EnvironmentLoader.Load(".env");
PlayerSettings.Android.keystorePass =
EnvironmentLoader.Required(
env,
"ANDROID_KEYSTORE_PASSWORD");
PlayerSettings.Android.keyaliasName =
EnvironmentLoader.Required(
env,
"ANDROID_KEY_ALIAS");
PlayerSettings.Android.keyaliasPass =
EnvironmentLoader.Required(
env,
"ANDROID_KEY_PASSWORD");
Debug.Log("Android signing configuration loaded.");
}
}
Теперь пароль не нужно каждый раз вводить руками через Unity UI. При желании можно добавить [InitializeOnLoad] к классу, чтобы креды вбивались при запуске проекта.
А остальные секреты (при наличии) имплементировать в приложение самому.
На проекте может быть больше использований.env чем заполнить Android signing — например, подключение к серверу для загрузки Asset Bundles, Addressables, Content Directory, правки/просмотра своих реализаций Remote Config, быстрого lookup в аналитику на бекенде, чтобы проверить жив ли какой‑то ивент.
Все эти доступы не должны лежать хардкодом в репозитории.
3. Централизованное хранение секретов
Последняя проблема — файл нужно передавать новым разработчикам, синхронизировать между машинами и CI, ротировать при компрометации. Для таких задач существуют secrets manager. Себе я выбрал Infisical, хотя на рынке популярных вариантов как минимум три.
Настройка
Для начала нужно регнуть учетку infisical, создать компанию, создать проект и секретов в него. Ссылка на US сервер‑ https://app.infisical.com/.
Проблема с которой можно столкнуться
У сервиса несколько субдоменов — на 10.2026 есть как минимум как минимум app. (US server), eu. (EU server). Аккаунты и проекты у них раздельные. Выбирайте одинаковый сервер при работе в веб интерфейсе и интеграции в CLI.
Установка (windows):
winget install infisical
infisical loginУстановит пакет, и запустит логин, который откроет браузер для входа. для CI/CD процесс иной, в команду вписываются данные специальной учетки Machine Identity. Для других платформ пакет называется идентично и поддерживает установку через пакетные менеджеры.
Настройка проекта:
cd myProjectPath
infisical initЭто запустит интерактивное создание / привязку к проекту, по итогу в директории появится .infisical.json. Он содержит такое:
{
"workspaceId": "7a10de5f-ed66-43ff-838d-c88b6926d59a",
"defaultEnvironment": "",
"gitBranchToEnvironmentMapping": null
}Указанные поля по необходимости можно настроить — сопоставление среды к ветки git и среду по стандарту. А workspaceId при необходимости можно посмотреть в настройках проекта в веб интерфейсе и выставить в конфиге.
.infisical.jsonможно коммитить в репозиторий, секретов в нём нет. Далее разработчик может создать секреты в .env файл через команду:
infisical export --format dotenv --output-file=.envПосле этого можно Build/Load Signing Credentials в Unity, чтобы вписать данные подписи автоматически.
Что не нужно делать
Не надо складывать в Infisical вообще всё
SERVER_URL, ADDRESSABLES_URL не становятся секретами только потому, что они находятся в .env.
Если значение является обычной конфигурацией, хранить его в Unity‑проекте часто проще и удобнее.
Не надо считать client‑side API key настоящим секретом
Если приложение должно содержать:
SENTRY_DSN
FIREBASE_APP_ID
PUBLIC_CLIENT_ID
то secrets manager не сделает их недоступными пользователю.
После сборки они всё равно окажутся внутри приложения.
Не надо сохранять secrets в Unity Assets
Плохо: Прочитал.env, сложил результат в ScriptableObject.
Так секреты попадут в git.
Главная идея
В результате получается довольно простое правило:
Конфигурация приложения хранится вместе с приложением. Credentials для действий от имени команды — отдельно.
Именно такое разделение начинает иметь смысл, когда Unity‑проект перестаёт быть одним локальным проектом разработчика и превращается в production pipeline команды.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.