// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package auth import ( "context" "crypto/tls" "log/slog" "time" "github.com/gomodule/redigo/redis" "github.com/spf13/viper" "git.coopcloud.tech/wiki-cafe/member-console/internal/logging" ) // Session-store connection tuning. These bound a dial against an unreachable or // wedged store so the boot probe fails with an error instead of hanging, and so // a store that stops answering mid-request cannot hold a handler open past the // server's own write budget. const ( valkeyConnectTimeout = 5 * time.Second valkeyReadTimeout = 3 * time.Second valkeyWriteTimeout = 3 * time.Second ) // valkeyDialOptions builds the session store's dial options from configuration. // // Until the 2026-09 security audit the store was reached with an address and // nothing else, so anyone who could reach the port could read and forge // sessions, and sessions carry the person, organization and workspace ids that // authorization is decided from. A password is now required: config validation // refuses to start without valkey-password, so the branch below is reached with // a password in every configuration that boots. It stays a conditional because // this function is also exercised by tests that build partial configurations. // // TLS and the username are opt-in and absent by default, for a store that // terminates TLS or runs ACL users. A single-host stack that sets neither dials // as before, now authenticated. func valkeyDialOptions(ctx context.Context) []redis.DialOption { options := []redis.DialOption{ redis.DialConnectTimeout(valkeyConnectTimeout), redis.DialReadTimeout(valkeyReadTimeout), redis.DialWriteTimeout(valkeyWriteTimeout), } // A username without a password is an ACL user relying on a passwordless // rule; both are passed through as configured rather than second-guessed. if username := viper.GetString("valkey-username"); username != "" { options = append(options, redis.DialUsername(username)) } if password := viper.GetString("valkey-password"); password != "" { options = append(options, redis.DialPassword(password)) } if viper.GetBool("valkey-tls") { options = append(options, redis.DialUseTLS(true)) if viper.GetBool("valkey-tls-skip-verify") { // Certificate verification off means an attacker who can // intercept the connection can present any certificate, so the // encryption stops proving who is on the other end. It exists for // a store with a self-signed certificate and is worth saying out // loud at every boot. logging.FromContext(ctx).Warn("valkey-tls-skip-verify is on: the session store's certificate is NOT verified", slog.String("env", viper.GetString("env"))) options = append(options, redis.DialTLSSkipVerify(true), redis.DialTLSConfig(&tls.Config{InsecureSkipVerify: true}), //nolint:gosec // explicit opt-in, warned above ) } } return options }