mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 10:12:29 +08:00 
			
		
		
		
	cmd: hash-password: Support reading from stdin (#3373)
Closes #3365 * http: Add support in hash-password for reading from terminals/stdin * FIXUP: Run gofmt -s * FIXUP * FIXUP: Apply suggestions from code review Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * FIXUP Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
		@ -15,26 +15,34 @@
 | 
				
			|||||||
package caddyauth
 | 
					package caddyauth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/caddyserver/caddy/v2"
 | 
						"github.com/caddyserver/caddy/v2"
 | 
				
			||||||
	caddycmd "github.com/caddyserver/caddy/v2/cmd"
 | 
						caddycmd "github.com/caddyserver/caddy/v2/cmd"
 | 
				
			||||||
	"golang.org/x/crypto/bcrypt"
 | 
						"golang.org/x/crypto/bcrypt"
 | 
				
			||||||
	"golang.org/x/crypto/scrypt"
 | 
						"golang.org/x/crypto/scrypt"
 | 
				
			||||||
 | 
						"golang.org/x/crypto/ssh/terminal"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	caddycmd.RegisterCommand(caddycmd.Command{
 | 
						caddycmd.RegisterCommand(caddycmd.Command{
 | 
				
			||||||
		Name:  "hash-password",
 | 
							Name:  "hash-password",
 | 
				
			||||||
		Func:  cmdHashPassword,
 | 
							Func:  cmdHashPassword,
 | 
				
			||||||
		Usage: "--plaintext <password> [--salt <string>] [--algorithm <name>]",
 | 
							Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]",
 | 
				
			||||||
		Short: "Hashes a password and writes base64",
 | 
							Short: "Hashes a password and writes base64",
 | 
				
			||||||
		Long: `
 | 
							Long: `
 | 
				
			||||||
Convenient way to hash a plaintext password. The resulting
 | 
					Convenient way to hash a plaintext password. The resulting
 | 
				
			||||||
hash is written to stdout as a base64 string.
 | 
					hash is written to stdout as a base64 string.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--plaintext, when omitted, will be read from stdin. If
 | 
				
			||||||
 | 
					Caddy is attached to a controlling tty, the plaintext will
 | 
				
			||||||
 | 
					not be echoed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--algorithm may be bcrypt or scrypt. If script, the default
 | 
					--algorithm may be bcrypt or scrypt. If script, the default
 | 
				
			||||||
parameters are used.
 | 
					parameters are used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,16 +60,47 @@ be provided (scrypt).
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func cmdHashPassword(fs caddycmd.Flags) (int, error) {
 | 
					func cmdHashPassword(fs caddycmd.Flags) (int, error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	algorithm := fs.String("algorithm")
 | 
						algorithm := fs.String("algorithm")
 | 
				
			||||||
	plaintext := []byte(fs.String("plaintext"))
 | 
						plaintext := []byte(fs.String("plaintext"))
 | 
				
			||||||
	salt := []byte(fs.String("salt"))
 | 
						salt := []byte(fs.String("salt"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(plaintext) == 0 {
 | 
						if len(plaintext) == 0 {
 | 
				
			||||||
		return caddy.ExitCodeFailedStartup, fmt.Errorf("password is required")
 | 
							if terminal.IsTerminal(int(os.Stdin.Fd())) {
 | 
				
			||||||
 | 
								fmt.Print("Enter password: ")
 | 
				
			||||||
 | 
								plaintext, err = terminal.ReadPassword(int(os.Stdin.Fd()))
 | 
				
			||||||
 | 
								fmt.Println()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return caddy.ExitCodeFailedStartup, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Print("Confirm password: ")
 | 
				
			||||||
 | 
								confirmation, err := terminal.ReadPassword(int(os.Stdin.Fd()))
 | 
				
			||||||
 | 
								fmt.Println()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return caddy.ExitCodeFailedStartup, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !bytes.Equal(plaintext, confirmation) {
 | 
				
			||||||
 | 
									return caddy.ExitCodeFailedStartup, fmt.Errorf("password does not match")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								rd := bufio.NewReader(os.Stdin)
 | 
				
			||||||
 | 
								plaintext, err = rd.ReadBytes('\n')
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return caddy.ExitCodeFailedStartup, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								plaintext = plaintext[:len(plaintext)-1] // Trailing newline
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(plaintext) == 0 {
 | 
				
			||||||
 | 
								return caddy.ExitCodeFailedStartup, fmt.Errorf("plaintext is required")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var hash []byte
 | 
						var hash []byte
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	switch algorithm {
 | 
						switch algorithm {
 | 
				
			||||||
	case "bcrypt":
 | 
						case "bcrypt":
 | 
				
			||||||
		hash, err = bcrypt.GenerateFromPassword(plaintext, bcrypt.DefaultCost)
 | 
							hash, err = bcrypt.GenerateFromPassword(plaintext, bcrypt.DefaultCost)
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user