server-test / test (push) Successful in 44s
If the IdP is misconfigured, unreachable, or the admin group is a typo, the
operator is locked out of their own server with no way back short of editing
JSON on disk. A fallback that only matters when everything else is broken is
exactly the thing you cannot add later - by then you cannot get in to add it.
Stored as PBKDF2-HMAC-SHA256 from the standard library (Go 1.24+ has it, so no
dependency), 600k iterations, per-credential salt. A password rather than a
bearer token on purpose: a break-glass credential is the one most likely to end
up in a backup or a config-management repo, and a hash survives that where a
token does not. There is no email reset flow and should not be -
--set-admin-password on the host is the reset, and whoever can run it already
has the machine.
The password is read from stdin, never a flag, so it stays out of shell history
and the process list; piping still works for automation.
Details the tests pin, each for a reason:
- the username is compared in constant time too, or a fast rejection is a
timing oracle for which usernames exist;
- the *stored* iteration count is used, so raising the constant later does not
lock out existing passwords;
- the throttle grows with consecutive failures but stays bounded and forgives
after a quiet minute - a break-glass credential an attacker can lock out is
a denial of service against the one person who needs it;
- sessions are MAC-checked before anything in them is read, and rotating the
secret invalidates every one at once, which is how they are revoked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
35 lines
1019 B
Go
35 lines
1019 B
Go
// SPDX-FileCopyrightText: 2026 Echolot contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
//go:build linux
|
|
|
|
package system
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// DisableEcho turns off terminal echo while a password is typed, returning a function that puts
|
|
// the terminal back. Both are best-effort: when stdin is a pipe (the automation case) there is
|
|
// no terminal to change and nothing to restore.
|
|
func DisableEcho(f *os.File) (func(), error) {
|
|
fd := f.Fd()
|
|
var t syscall.Termios
|
|
if _, _, errno := syscall.Syscall6(syscall.SYS_IOCTL, fd,
|
|
syscall.TCGETS, uintptr(unsafe.Pointer(&t)), 0, 0, 0); errno != 0 {
|
|
return nil, errno // not a terminal; nothing to do
|
|
}
|
|
original := t
|
|
t.Lflag &^= syscall.ECHO
|
|
if _, _, errno := syscall.Syscall6(syscall.SYS_IOCTL, fd,
|
|
syscall.TCSETS, uintptr(unsafe.Pointer(&t)), 0, 0, 0); errno != 0 {
|
|
return nil, errno
|
|
}
|
|
return func() {
|
|
_, _, _ = syscall.Syscall6(syscall.SYS_IOCTL, fd,
|
|
syscall.TCSETS, uintptr(unsafe.Pointer(&original)), 0, 0, 0)
|
|
}, nil
|
|
}
|