Перенесены секреты из .env в SM

This commit is contained in:
mi
2026-07-30 19:22:48 +03:00
parent 049c45db5c
commit e24ed9d8ef
58 changed files with 3350 additions and 1054 deletions
+74 -43
View File
@@ -44,11 +44,13 @@ class InfrastructureConfigTests(unittest.TestCase):
application = (ROOT / "infra/compose/application.yml").read_text(encoding="utf-8")
self.assertIn("networks: [backend, observability, egress]", application)
self.assertIn("networks: [public, backend, observability, egress]", application)
self.assertIn('KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY: ""', application)
self.assertIn(
"KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY_FILE: "
"/run/secrets/keycloak_yandex_captcha_server_key",
application,
)
self.assertEqual(
application.count(
"IDGTL_SMS_API_KEY: ${IDGTL_SMS_API_KEY:?IDGTL_SMS_API_KEY is required}"
),
application.count("IDGTL_SMS_API_KEY_FILE: /run/secrets/idgtl_sms_api_key"),
1,
)
@@ -56,7 +58,7 @@ class InfrastructureConfigTests(unittest.TestCase):
self.assertEqual(jobs.count("networks: [backend, egress]"), 5)
observability = (ROOT / "observability/docker-compose.yml").read_text(encoding="utf-8")
self.assertIn("networks: [observability, backend, egress]", observability)
self.assertIn("networks: [observability, egress]", observability)
redis = (ROOT / "redis/docker-compose.yml").read_text(encoding="utf-8")
self.assertNotIn("egress", redis)
@@ -64,6 +66,8 @@ class InfrastructureConfigTests(unittest.TestCase):
def test_only_nginx_fragment_publishes_ports(self) -> None:
forbidden = (
ROOT / "infra/compose/application.yml",
ROOT / "api-backend/docker-compose.yml",
ROOT / "keycloak/docker-compose.yml",
ROOT / "redis/docker-compose.yml",
ROOT / "observability/docker-compose.yml",
ROOT / "deployment/docker-compose.jobs.yml",
@@ -177,6 +181,10 @@ class InfrastructureConfigTests(unittest.TestCase):
self.assertIn("storage: file_storage", config)
self.assertIn("retry_on_failure:", config)
self.assertIn('insecure: "${env:OTEL_REMOTE_TLS_INSECURE}"', config)
self.assertIn(
"authorization: ${file:/run/secrets/otel_remote_auth_header}", config
)
self.assertNotIn("OTEL_REMOTE_AUTH_HEADER", config)
self.assertIn("tail_sampling:", config)
self.assertNotIn("probabilistic_sampler:", config)
self.assertIn('targets: ["sms-service:8080"]', config)
@@ -231,7 +239,7 @@ class InfrastructureConfigTests(unittest.TestCase):
def test_smoke_script_does_not_source_env_as_shell(self) -> None:
smoke = (ROOT / "deployment/scripts/smoke.sh").read_text(encoding="utf-8")
self.assertNotIn('. "./$ENV_FILE"', smoke)
self.assertNotIn('. "./$CONFIG_FILE"', smoke)
for variable in ("PUBLIC_HOST", "PUBLIC_WEB_URL", "KEYCLOAK_REALM"):
self.assertIn(f"{variable}=$(env_value {variable})", smoke)
@@ -298,18 +306,64 @@ class InfrastructureConfigTests(unittest.TestCase):
for variable in (
"KC_DB_SCHEMA",
"KEYCLOAK_OTP_MOCK_ENABLED",
"KEYCLOAK_OTP_MOCK_CODE",
"KEYCLOAK_YANDEX_CAPTCHA_ENABLED",
"KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY",
"KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY",
"KEYCLOAK_OTP_HMAC_KEY",
"KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC",
"KEYCLOAK_SETTINGS_BRIDGE_URL",
"KEYCLOAK_SETTINGS_BRIDGE_TOKEN",
"KEYCLOAK_SMS_SERVICE_URL",
"KEYCLOAK_SMS_SERVICE_TOKEN",
):
self.assertIn(f" {variable}:", application)
for variable in (
"KEYCLOAK_OTP_MOCK_CODE",
"KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY",
"KEYCLOAK_OTP_HMAC_KEY",
"KEYCLOAK_SETTINGS_BRIDGE_TOKEN",
"KEYCLOAK_SMS_SERVICE_TOKEN",
):
self.assertIn(f" {variable}_FILE:", application)
self.assertIn(
" KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY: "
"${KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY:-}",
application,
)
def test_compose_secrets_do_not_enter_config_environment(self) -> None:
compose_paths = (
ROOT / "infra/compose/application.yml",
ROOT / "redis/docker-compose.yml",
ROOT / "observability/docker-compose.yml",
ROOT / "deployment/docker-compose.jobs.yml",
)
combined = "\n".join(path.read_text(encoding="utf-8") for path in compose_paths)
self.assertNotIn("env_file:", combined)
for path in compose_paths:
self.assertIn(
"core: {soft: 0, hard: 0}",
path.read_text(encoding="utf-8"),
path,
)
for variable in (
"DATABASE_URL",
"REDIS_URL",
"SMS_SERVICE_TOKEN",
"KEYCLOAK_DB_PASSWORD",
"OTEL_REMOTE_AUTH_HEADER",
):
self.assertNotIn(f"${{{variable}", combined)
self.assertIn(
"${HAN_SECRETS_DIR:-/run/han-chat/secrets}/DATABASE_URL", combined
)
self.assertNotIn("entrypoint: []", combined)
for service in (
"api-backend",
"sms-service",
"message-safety",
"bitrix-local-app",
"bitrix-sync",
"keycloak",
):
dockerfile = (ROOT / service / "Dockerfile").read_text(encoding="utf-8")
self.assertIn("han-container-entrypoint", dockerfile, service)
def test_env_validator_accepts_materialized_example(self) -> None:
example = (ROOT / ".env.example").read_text(encoding="utf-8")
@@ -317,15 +371,10 @@ class InfrastructureConfigTests(unittest.TestCase):
self.assertNotIn("currentSchema=", example)
self.assertIn("KEYCLOAK_DB_SCHEMA=keycloak", example)
self.assertIn("HAN_PG_PORT=5433", example)
for required in (
"CURSOR_HMAC_SECRET=",
"BITRIX_TOKEN_ENCRYPTION_KEY=",
"KEYCLOAK_INTERNAL_URL=http://keycloak:8080/auth",
"KEYCLOAK_SMS_SERVICE_URL=http://sms-service:8080",
"IDGTL_SMS_CALLBACK_PUBLIC_URL=https://chat.example.ru/callbacks/idgtl/sms",
):
self.assertIn(required, example)
materialized = example.replace("change-me", "0123456789abcdef0123456789abcdef")
self.assertIn("SECRETS_SOURCE=file", example)
self.assertNotIn("CURSOR_HMAC_SECRET=", example)
self.assertNotIn("BITRIX_TOKEN_ENCRYPTION_KEY=", example)
materialized = example
materialized = materialized.replace(
"KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=false",
"KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=true",
@@ -341,11 +390,9 @@ class InfrastructureConfigTests(unittest.TestCase):
)
self.assertEqual(result.returncode, 0, result.stderr)
def test_env_validator_requires_captcha_keys_only_when_enabled(self) -> None:
def test_env_validator_keeps_captcha_server_key_out_of_config(self) -> None:
example = (ROOT / ".env.example").read_text(encoding="utf-8")
materialized = example.replace(
"change-me", "0123456789abcdef0123456789abcdef"
).replace(
"KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=false",
"KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=true",
)
@@ -353,33 +400,17 @@ class InfrastructureConfigTests(unittest.TestCase):
"KEYCLOAK_YANDEX_CAPTCHA_ENABLED=false",
"KEYCLOAK_YANDEX_CAPTCHA_ENABLED=true",
)
enabled_with_keys = enabled_without_keys.replace(
"KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY=",
"KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY=client-key",
).replace(
"KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY=",
"KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY=server-key-0123456789",
)
with tempfile.TemporaryDirectory() as directory:
env_file = Path(directory) / ".env"
env_file.write_text(enabled_without_keys, encoding="utf-8")
missing = subprocess.run(
result = subprocess.run(
[sys.executable, str(ROOT / "scripts/validate-env"), str(env_file)],
text=True,
capture_output=True,
check=False,
)
env_file.write_text(enabled_with_keys, encoding="utf-8")
configured = subprocess.run(
[sys.executable, str(ROOT / "scripts/validate-env"), str(env_file)],
text=True,
capture_output=True,
check=False,
)
self.assertNotEqual(missing.returncode, 0)
self.assertIn("KEYCLOAK_YANDEX_CAPTCHA_CLIENT_KEY", missing.stderr)
self.assertIn("KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY", missing.stderr)
self.assertEqual(configured.returncode, 0, configured.stderr)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertNotIn("KEYCLOAK_YANDEX_CAPTCHA_SERVER_KEY=", example)
if __name__ == "__main__":