Skip to content

Commit 1d3ffb8

Browse files
committed
test: drop plain-text secrets from fixtures, use <attr>File indirection
- services/keycloak/checks.nix: openid_clients.acme_app.client_secret "topsecret" -> client_secretFile = /etc/acme-app-client-secret. Asserts the literal stays out of the generated .tf.json. - services/forgejo/checks.nix: users.alice.password "hackme" in the widenScope specialisation -> passwordFile = /etc/forgejo-alice-password. (bob already used passwordFile; alice was the one literal left.) The fixtures now exclusively exercise the secret-file indirection, so the tests stay honest examples for operators.
1 parent a72e59b commit 1d3ffb8

2 files changed

Lines changed: 13 additions & 4 deletions

File tree

services/forgejo/checks.nix

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
# curl drives the post-convergence API assertions.
2525
environment.systemPackages = [ pkgs.curl ];
2626

27-
# Stand-in for an operator-managed secret file (sops/agenix in production):
28-
# bob's password, fed to the reconciler via LoadCredential, never the store.
27+
# mock agenix secrets: passwords supplied as host files, fed to the
28+
# reconciler via LoadCredential and never the world-readable store.
2929
environment.etc."forgejo-bob-password".text = "hackme";
30+
environment.etc."forgejo-alice-password".text = "hackme";
3031

3132
services.forgejo = {
3233
enable = true;
@@ -98,7 +99,7 @@
9899
specialisation.widenScope.configuration = {
99100
services.forgejo.runtime.users.alice = {
100101
email = "alice@localhost.localdomain";
101-
password = "hackme";
102+
passwordFile = "/etc/forgejo-alice-password";
102103
must_change_password = false;
103104
};
104105
};

services/keycloak/checks.nix

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ in
247247
name = "declarative-keycloak-clients";
248248

249249
containers.keycloak = mkHost {
250+
extraEtc."acme-app-client-secret".text = "topsecret";
250251
runtime = {
251252
realms.acme.display_name = "ACME";
252253

@@ -264,7 +265,7 @@ in
264265
client_id = "acme-app";
265266
name = "ACME App";
266267
access_type = "CONFIDENTIAL";
267-
client_secret = "topsecret";
268+
client_secretFile = "/etc/acme-app-client-secret";
268269
standard_flow_enabled = true;
269270
direct_access_grants_enabled = true;
270271
service_accounts_enabled = true;
@@ -320,6 +321,13 @@ in
320321
assert "acme-profile" in names, \
321322
f"acme-profile not bound as default scope: {names}"
322323
324+
with subtest("client_secret was supplied via <attr>File, never written to .tf.json"):
325+
tfjson = keycloak.succeed(
326+
"cat /var/lib/keycloak/declarative-terraform/main.tf.json"
327+
)
328+
assert "topsecret" not in tfjson, \
329+
"openid_clients.acme_app.client_secret leaked into .tf.json"
330+
323331
with subtest("protocol mapper attached to client scope"):
324332
# protocolMappers ride along on the client-scope representation.
325333
scopes = admin_get(keycloak, "acme/client-scopes")

0 commit comments

Comments
 (0)