feat: interactive password input for pass_mgr admin init

- Remove --key-path parameter requirement
- Add interactive password prompt (hidden input like sudo)
- Require password confirmation
- Password must be at least 6 characters
- Uses golang.org/x/term for secure password input
This commit is contained in:
zhi
2026-03-05 11:07:47 +00:00
parent 84c9df633a
commit 9ecb065b43
3 changed files with 66 additions and 29 deletions

View File

@@ -1,10 +1,12 @@
module pass_mgr
go 1.22
go 1.24.0
require github.com/spf13/cobra v1.8.0
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/term v0.40.0 // indirect
)

View File

@@ -6,5 +6,9 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -12,9 +12,11 @@ import (
"os"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/spf13/cobra"
"golang.org/x/term"
)
const (
@@ -173,27 +175,17 @@ func rotateCmd() *cobra.Command {
}
func adminInitCmd() *cobra.Command {
var keyPath string
cmd := &cobra.Command{
return &cobra.Command{
Use: "admin init",
Short: "Initialize pass_mgr with admin key",
Run: func(cmd *cobra.Command, args []string) {
// Require --key-path parameter
if keyPath == "" {
fmt.Fprintln(os.Stderr, "Error: --key-path is required")
fmt.Fprintln(os.Stderr, "Usage: pass_mgr admin init --key-path <path-to-key-file>")
fmt.Fprintln(os.Stderr, "The key file must contain a password with at least 6 characters")
os.Exit(1)
}
if err := initAdmin(keyPath); err != nil {
if err := initAdminInteractive(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("pass_mgr initialized successfully")
},
}
cmd.Flags().StringVar(&keyPath, "key-path", "", "Path to admin key file (required, password must be >= 6 chars)")
return cmd
}
func setCmd() *cobra.Command {
@@ -256,32 +248,56 @@ func loadAdminKey() ([]byte, error) {
return hash[:], nil
}
func initAdmin(keyPath string) error {
func initAdminInteractive() error {
fmt.Print("Enter admin password: ")
password1, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return fmt.Errorf("failed to read password: %w", err)
}
fmt.Println()
// Trim whitespace/newlines
password1 = []byte(strings.TrimSpace(string(password1)))
// Validate password length
if len(password1) < 6 {
return fmt.Errorf("password must be at least 6 characters long (got %d)", len(password1))
}
fmt.Print("Confirm admin password: ")
password2, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return fmt.Errorf("failed to read password confirmation: %w", err)
}
fmt.Println()
// Trim whitespace/newlines
password2 = []byte(strings.TrimSpace(string(password2)))
// Check passwords match
if string(password1) != string(password2) {
return fmt.Errorf("passwords do not match")
}
// Save the key
return saveAdminKey(password1)
}
func saveAdminKey(key []byte) error {
homeDir := getHomeDir()
adminDir := filepath.Join(homeDir, AdminKeyDir)
// Create admin directory
if err := os.MkdirAll(adminDir, 0700); err != nil {
return fmt.Errorf("failed to create admin directory: %w", err)
}
// Read provided key
key, err := os.ReadFile(keyPath)
if err != nil {
return fmt.Errorf("failed to read key file: %w", err)
}
// Validate password length (must be >= 6 characters)
if len(key) < 6 {
return fmt.Errorf("password must be at least 6 characters long (got %d)", len(key))
}
// Save key
keyFile := filepath.Join(adminDir, AdminKeyFile)
if err := os.WriteFile(keyFile, key, 0600); err != nil {
return fmt.Errorf("failed to save key: %w", err)
}
// Save config
config := Config{
KeyHash: fmt.Sprintf("%x", sha256.Sum256(key)),
@@ -292,10 +308,25 @@ func initAdmin(keyPath string) error {
if err := os.WriteFile(configPath, configData, 0600); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
return nil
}
func initAdmin(keyPath string) error {
// Read provided key
key, err := os.ReadFile(keyPath)
if err != nil {
return fmt.Errorf("failed to read key file: %w", err)
}
// Validate password length (must be >= 6 characters)
if len(key) < 6 {
return fmt.Errorf("password must be at least 6 characters long (got %d)", len(key))
}
return saveAdminKey(key)
}
func getSecretsDir() string {
if workspaceDir != "" && agentID != "" {
return filepath.Join(workspaceDir, SecretsDirName, agentID)