Smail/backend/cmd/smail-admin/main.go
smail 97c815deb0
Some checks are pending
Security Checks / security (push) Waiting to run
限制管理员重置为显式启动并隔离终端输入
2026-09-19 23:43:25 +08:00

137 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"git.pcux.cn/xuans/Smail/internal/buildinfo"
"git.pcux.cn/xuans/Smail/services/adminreset"
)
func main() {
code := run(os.Args[1:])
if code != 0 {
os.Exit(code)
}
}
func run(args []string) int {
if len(args) == 1 && (args[0] == "version" || args[0] == "--version") {
fmt.Println(buildinfo.Current().Version)
return 0
}
if len(args) == 1 && (args[0] == "doctor" || args[0] == "--doctor") {
return runDoctor()
}
if len(args) != 0 || hasPasswordArgument(args) {
fmt.Fprintln(os.Stderr, "smail-admin 只允许由 systemctl start smail-admin 无参数启动")
return 2
}
signalContext, stopSignals := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stopSignals()
invocationContext, closeInvocation, err := adminreset.OpenInvocation(signalContext)
if err != nil {
fmt.Fprintln(os.Stderr, "超级管理员重置已拒绝:请在本机管理员终端使用 systemctl start smail-admin,停止请使用 systemctl stop smail-admin")
return 1
}
defer closeInvocation()
ctx, cancel := context.WithTimeout(invocationContext, 5*time.Minute)
defer cancel()
conn, err := adminreset.DialControl(ctx, adminreset.DefaultControlPath)
if err != nil {
return finishResetContext(ctx, "超级管理员重置通道不可用", 1)
}
defer conn.Close()
stopClosing := context.AfterFunc(ctx, func() { _ = conn.Close() })
defer stopClosing()
if err := adminreset.SendVerifiedResetWithInput(ctx, conn, adminreset.ReadVisibleInputContext, func() (string, error) {
return adminreset.ReadProtectedPasswordContext(ctx)
}); err != nil {
if ctx.Err() != nil {
fmt.Fprintln(os.Stderr, "超级管理员重置已取消")
return 0
}
return finishResetContext(ctx, "超级管理员重置失败:"+safeError(err), 1)
}
return finishResetContext(ctx, "超级管理员密码重置成功,请使用新密码登录", 0)
}
func finishReset(message string, code int) int {
return finishResetContext(context.Background(), message, code)
}
func finishResetContext(ctx context.Context, message string, code int) int {
fmt.Fprintln(os.Stderr, message)
adminreset.ShowResultContext(ctx, message)
return code
}
func runDoctor() int {
report := adminreset.CheckEnvironment()
status := "正常"
if !report.Supported {
status = "不支持"
} else if !report.Privileged || !report.ServiceManaged || !report.PasswordAgentAvailable {
status = "不满足重置条件"
}
fmt.Printf("平台:%s\n重置支持:%t\n部署管理员身份:%t\n系统服务上下文:%t\n密码代理:%t\n控制通道:%s\n状态:%s\n",
report.Platform,
report.Supported,
report.Privileged,
report.ServiceManaged,
report.PasswordAgentAvailable,
report.ControlPath,
status,
)
return 0
}
func hasPasswordArgument(args []string) bool {
for _, arg := range args {
lower := strings.ToLower(arg)
if strings.Contains(lower, "password") || strings.Contains(lower, "passwd") || strings.Contains(lower, "secret") {
return true
}
}
return false
}
func safeError(err error) string {
if err == nil {
return ""
}
if errors.Is(err, adminreset.ErrVerificationUnavailable) {
return "没有可用的安全验证方式,请先登录安全中心配置邮箱、手机动态码或安全码"
}
if errors.Is(err, adminreset.ErrVerificationFailed) {
return "安全验证失败、已过期或请求过于频繁,请稍后重新启动重置"
}
if errors.Is(err, adminreset.ErrEmailDeliveryFailed) {
return "邮件验证码发送失败或请求过于频繁,请稍后重试或选择其他验证方式"
}
if errors.Is(err, adminreset.ErrExpiredSession) {
return "重置会话已过期,请重新启动重置"
}
if errors.Is(err, adminreset.ErrInvalidRequest) || errors.Is(err, adminreset.ErrUnsupported) {
return "请求无效或当前平台不支持"
}
return "本机受保护通道拒绝了请求"
}
func clearString(value *string) {
if value == nil {
return
}
buffer := []byte(*value)
for i := range buffer {
buffer[i] = 0
}
*value = ""
}