The Redis component, as a fab unit #1

Merged
marcos-mendez merged 1 commit from feat/the-redis-component into main 2026-09-27 21:58:02 -03:00
marcos-mendez commented 2026-09-27 21:04:16 -03:00 (Migrated from github.com)

Closes the component half of Keel-Linux/keel-redis#1: keel-redis composes this through unit.d/redis.

Redis is the third engine of decision 0013's standard set, and the first one whose component is written rather than extracted. turnkeylinux/common has no conf/redis, no overlays/redis and no plans/turnkey/redis, so there was nothing in the shared tree to take out.

What the declared secret means on Redis

secrets.db_password renders to DB_PASS, the same variable the MariaDB and PostgreSQL hooks read. Redis has no database account to put it on: the secret is either requirepass, the built in default user's password, or an ACL user with a password of its own. Decision 0013 leaves both open, and this chooses the ACL user for two reasons that are checkable properties, not preferences.

  1. requirepass would also lock INFO. keel inspect reads database.server.role from INFO replication and INFO cluster, never reads a secret to get past a refusal, and reports a Redis it cannot ask as a role it could not infer. An appliance that cannot say it is standalone cannot later say it is a primary or a replica, and that reading is the seam the replication modes are built on. So default keeps one command, INFO, no key and no channel: inspect's three questions are answered without a secret and GET is refused with NOPERM.
  2. requirepass lives in a world readable file. The package ships /etc/redis/redis.conf as root:root 0644, so the upstream appliance wrote the Redis password where every local account could read it and shipped turnkey-redis-pw get to print it back. An ACL rule takes the SHA-256 instead, written to /etc/redis/redis.conf.d/50-keel-secret.conf as root:redis 0640.

Before the first boot the account is published off, which authenticates nothing: the same reasoning as the invalid hash keel-mariadb publishes its account with.

The bind addresses

bind ::1 127.0.0.1. Debian ships bind 127.0.0.1 -::1, where the dash makes ::1 optional, so a server that cannot bind it starts anyway and answers one family. No name is used anywhere, because Debian maps ::1 to ip6-localhost and never to localhost — how the PostgreSQL appliance shipped listening on IPv4 only.

The Redis trap every check is shaped around

redis-cli exits 0 when the server answers with an error. A wrong password prints AUTH failed: WRONGPASS ... and exits 0. So the hook, the conf script and the tests read the answer and never the exit code, and one test drives the hook with a wrong password through a stub that exits 0 and requires the hook to fail anyway.

Coverage

65 bats tests over four files; conf, 35redispass and lib/redis.sh at 100 percent each, 123 of 123 lines. The workflow threshold is raised from the bootstrap 0 to 100. The two configuration fragments are asserted as content, because the one line the PostgreSQL appliance got wrong was in a file exactly like them.

Test plan

  • COVERAGE_THRESHOLD=100 tests/coverage.sh locally: 100/100/100
  • The ACL design measured against redis-server 8.0.2 from Debian 13 before any of this was written: INFO replication, INFO cluster and INFO server answered unauthenticated, GET refused NOPERM, the admin user accepted with the digest, a wrong password refused, and ss -lntH showing [::1]:6379 and 127.0.0.1:6379
  • tests / coverage green here
  • The acceptance test: keel-redis's appliance / build-and-boot against a published layer that carries units redis@1.0.0
Closes the component half of Keel-Linux/keel-redis#1: `keel-redis` composes this through `unit.d/redis`. Redis is the third engine of decision 0013's standard set, and the first one whose component is written rather than extracted. `turnkeylinux/common` has no `conf/redis`, no `overlays/redis` and no `plans/turnkey/redis`, so there was nothing in the shared tree to take out. ## What the declared secret means on Redis `secrets.db_password` renders to `DB_PASS`, the same variable the MariaDB and PostgreSQL hooks read. Redis has no database account to put it on: the secret is either `requirepass`, the built in `default` user's password, or an ACL user with a password of its own. Decision 0013 leaves both open, and this chooses the ACL user for two reasons that are checkable properties, not preferences. 1. **`requirepass` would also lock `INFO`.** `keel inspect` reads `database.server.role` from `INFO replication` and `INFO cluster`, never reads a secret to get past a refusal, and reports a Redis it cannot ask as a role it could not infer. An appliance that cannot say it is `standalone` cannot later say it is a `primary` or a `replica`, and that reading is the seam the replication modes are built on. So `default` keeps one command, `INFO`, no key and no channel: inspect's three questions are answered without a secret and `GET` is refused with `NOPERM`. 2. **`requirepass` lives in a world readable file.** The package ships `/etc/redis/redis.conf` as `root:root 0644`, so the upstream appliance wrote the Redis password where every local account could read it and shipped `turnkey-redis-pw get` to print it back. An ACL rule takes the **SHA-256** instead, written to `/etc/redis/redis.conf.d/50-keel-secret.conf` as `root:redis 0640`. Before the first boot the account is published `off`, which authenticates nothing: the same reasoning as the invalid hash `keel-mariadb` publishes its account with. ## The bind addresses `bind ::1 127.0.0.1`. Debian ships `bind 127.0.0.1 -::1`, where the dash makes `::1` optional, so a server that cannot bind it starts anyway and answers one family. No name is used anywhere, because Debian maps `::1` to `ip6-localhost` and never to `localhost` — how the PostgreSQL appliance shipped listening on IPv4 only. ## The Redis trap every check is shaped around `redis-cli` exits 0 when the server answers with an error. A wrong password prints `AUTH failed: WRONGPASS ...` and exits 0. So the hook, the conf script and the tests read the answer and never the exit code, and one test drives the hook with a wrong password through a stub that exits 0 and requires the hook to fail anyway. ## Coverage 65 bats tests over four files; `conf`, `35redispass` and `lib/redis.sh` at **100 percent each, 123 of 123 lines**. The workflow threshold is raised from the bootstrap 0 to 100. The two configuration fragments are asserted as content, because the one line the PostgreSQL appliance got wrong was in a file exactly like them. ## Test plan - [x] `COVERAGE_THRESHOLD=100 tests/coverage.sh` locally: 100/100/100 - [x] The ACL design measured against `redis-server 8.0.2` from Debian 13 before any of this was written: `INFO replication`, `INFO cluster` and `INFO server` answered unauthenticated, `GET` refused `NOPERM`, the admin user accepted with the digest, a wrong password refused, and `ss -lntH` showing `[::1]:6379` and `127.0.0.1:6379` - [ ] `tests / coverage` green here - [ ] The acceptance test: `keel-redis`'s `appliance / build-and-boot` against a published layer that carries `units redis@1.0.0`
Sign in to join this conversation.
No description provided.