feat: ship Rust login manager and GitLab release pipeline
Replace the legacy Go implementation as the production path, quarantine the reference tree, add deterministic release packaging and version metadata, and run verification through GitLab CI.
This commit is contained in:
81
legacy/go/auth/faillock.go
Normal file
81
legacy/go/auth/faillock.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// auth — faillock integration.
|
||||
//
|
||||
// Reads /etc/security/faillock.conf for deny count, executes
|
||||
// faillock --user to get tally, supports --reset via admin bypass.
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FaillockStatus struct {
|
||||
Enabled bool
|
||||
Failures int
|
||||
MaxTries int
|
||||
Remaining int
|
||||
LockedOut bool
|
||||
}
|
||||
|
||||
func GetFaillockStatus(user string) (FaillockStatus, error) {
|
||||
s := FaillockStatus{}
|
||||
|
||||
data, err := os.ReadFile("/etc/security/faillock.conf")
|
||||
if err != nil {
|
||||
return s, nil
|
||||
}
|
||||
s.Enabled = true
|
||||
s.MaxTries = 3
|
||||
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "deny") {
|
||||
parts := strings.Split(line, "=")
|
||||
if len(parts) == 2 {
|
||||
if n, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
|
||||
s.MaxTries = n
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("faillock", "--user", user)
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return s, fmt.Errorf("faillock: %w", err)
|
||||
}
|
||||
|
||||
s.Failures = countFaillockLines(out.String())
|
||||
s.Remaining = s.MaxTries - s.Failures
|
||||
if s.Remaining < 0 {
|
||||
s.Remaining = 0
|
||||
}
|
||||
s.LockedOut = s.Remaining <= 0
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func ResetFaillock(user string) error {
|
||||
return exec.Command("faillock", "--user", user, "--reset").Run()
|
||||
}
|
||||
|
||||
func countFaillockLines(out string) int {
|
||||
n := 0
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "When") ||
|
||||
strings.HasPrefix(line, "Source") || strings.HasPrefix(line, "Date") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, ":") {
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
Reference in New Issue
Block a user