package cli import ( "context" "errors" "fmt" "os" "path/filepath" "strings" "github.com/spf13/cobra" "git.pcux.cn/xuans/Scert/backend/internal/dnsprovider" "git.pcux.cn/xuans/Scert/backend/internal/domain" "git.pcux.cn/xuans/Scert/backend/internal/secretstore" ) type dnsPermissionVerifier func(context.Context, string, map[string]string) (dnsprovider.AuthorizationReport, error) func newDNSCommand() *cobra.Command { command := &cobra.Command{Use: "dns", Short: "检查指定域名的阿里云 RAM 权限"} checker := dnsprovider.NewAliyunAuthorizationChecker() command.AddCommand(newDNSCheckCommand(checker.Verify)) return command } // 权限检查不启动应用、续期调度或数据库,也不调用全局域名发现。 func newDNSCheckCommand(verify dnsPermissionVerifier) *cobra.Command { return newDNSPermissionCommand(verify) } func newDNSPermissionCommand(verify dnsPermissionVerifier) *cobra.Command { var requestedDomain string var confirmWrite bool command := &cobra.Command{ Use: "check", Short: "对指定域名写入临时 TXT 并精确清理,验证当前权限", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { requestedDomain = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(requestedDomain), ".")) if !domain.IsValidName(requestedDomain) { return errors.New("必须提供有效的待检查域名") } if !confirmWrite { return errors.New("此检查会写入并删除专用临时 TXT 记录;确认已获目标域名测试授权后,请添加 --confirm-dns-write") } // 保护域必须在读取配置、凭据或构造云端客户端前拒绝,避免诊断入口产生任何外部副作用。 if err := dnsprovider.ValidateAliyunTargetDomains(dnsprovider.Aliyun, []string{requestedDomain}); err != nil { return err } cfg, err := loadConfig() if err != nil { return err } if cfg.DNS.Provider != dnsprovider.Aliyun || strings.TrimSpace(cfg.DNS.CredentialRef) == "" { return errors.New("请先配置阿里云 DNS 凭据引用") } // 不允许诊断入口在密钥缺失时生成新密钥,避免掩盖原有凭据损坏。 for _, name := range []string{"master.key", "secrets.json"} { info, err := os.Stat(filepath.Join(cfg.Paths.Config, name)) if err != nil || !info.Mode().IsRegular() { return fmt.Errorf("现有凭据文件 %s 不可用,请检查配置目录", name) } } store, err := secretstore.Open(cfg.Paths.Config) if err != nil { return err } credentials, err := store.Get(cmd.Context(), cfg.DNS.CredentialRef) if err != nil { return fmt.Errorf("读取阿里云 DNS 凭据失败:%w", err) } ctx, cancel := context.WithTimeout(cmd.Context(), dnsprovider.PermissionVerificationTimeoutFor(dnsprovider.Aliyun)) defer cancel() report, verifyErr := verify(ctx, requestedDomain, credentials) if err := printJSON(cmd, report); err != nil { return err } if verifyErr != nil { return fmt.Errorf("域名权限检查未通过:%w", verifyErr) } if !report.Verified { return errors.New("域名权限检查未通过:未获得写入及清理均成功的证据") } return nil }, } command.Flags().StringVar(&requestedDomain, "domain", "", "明确授权检查的单个域名,不枚举全账号域名") command.Flags().BoolVar(&confirmWrite, "confirm-dns-write", false, "确认允许专用临时 TXT 记录写入和精确清理") _ = command.MarkFlagRequired("domain") return command }