htpasswd-alike util

This commit is contained in:
Vladislav Yarmak 2022-09-07 00:08:06 +03:00
parent a546f7627d
commit de8cbc0033
3 changed files with 93 additions and 6 deletions

2
go.sum
View File

@ -16,8 +16,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=

21
main.go
View File

@ -13,6 +13,7 @@ import (
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/crypto/bcrypt"
)
var (
@ -65,12 +66,9 @@ type CLIArgs struct {
autocertACME string
autocertEmail string
autocertHTTP string
}
func list_ciphers() {
for _, cipher := range tls.CipherSuites() {
fmt.Println(cipher.Name)
}
passwd string
passwdCost int
positionalArgs []string
}
func parse_args() CLIArgs {
@ -93,7 +91,11 @@ func parse_args() CLIArgs {
flag.StringVar(&args.autocertACME, "autocert-acme", autocert.DefaultACMEDirectory, "custom ACME endpoint")
flag.StringVar(&args.autocertEmail, "autocert-email", "", "email used for ACME registration")
flag.StringVar(&args.autocertHTTP, "autocert-http", "", "listen address for HTTP-01 challenges handler of ACME")
flag.StringVar(&args.passwd, "passwd", "", "update given htpasswd file and add/set password for username. "+
"Username and password can be passed as positional arguments or requested interactively")
flag.IntVar(&args.passwdCost, "passwd-cost", bcrypt.MinCost, "bcrypt password cost (for -passwd mode)")
flag.Parse()
args.positionalArgs = flag.Args()
return args
}
@ -110,6 +112,13 @@ func run() int {
return 0
}
if args.passwd != "" {
if err := passwd(args.passwd, args.passwdCost, args.positionalArgs...); err != nil {
log.Fatalf("can't set password: %v", err)
}
return 0
}
logWriter := NewLogWriter(os.Stderr)
defer logWriter.Close()

View File

@ -16,6 +16,9 @@ import (
"strings"
"sync"
"time"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/ssh/terminal"
)
const COPY_BUF = 128 * 1024
@ -192,6 +195,62 @@ func makeCipherList(ciphers string) []uint16 {
return cipherIDList
}
func list_ciphers() {
for _, cipher := range tls.CipherSuites() {
fmt.Println(cipher.Name)
}
}
func passwd(filename string, cost int, args ...string) error {
var (
username, password, password2 string
err error
)
if len(args) > 0 {
username = args[0]
} else {
username, err = prompt("Enter username: ", false)
if err != nil {
return fmt.Errorf("can't get username: %w", err)
}
}
if len(args) > 1 {
password = args[1]
} else {
password, err = prompt("Enter password: ", true)
if err != nil {
return fmt.Errorf("can't get password: %w", err)
}
password2, err = prompt("Repeat password: ", true)
if err != nil {
return fmt.Errorf("can't get password (repeat): %w", err)
}
if password != password2 {
return fmt.Errorf("passwords do not match")
}
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), cost)
if err != nil {
return fmt.Errorf("can't generate password hash: %w", err)
}
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return fmt.Errorf("can't open file: %w", err)
}
defer f.Close()
_, err = f.WriteString(fmt.Sprintf("%s:%s\n", username, hash))
if err != nil {
return fmt.Errorf("can't write to file: %w", err)
}
return nil
}
func fileModTime(filename string) (time.Time, error) {
f, err := os.Open(filename)
if err != nil {
@ -206,3 +265,20 @@ func fileModTime(filename string) (time.Time, error) {
return fi.ModTime(), nil
}
func prompt(prompt string, secure bool) (string, error) {
var input string
fmt.Print(prompt)
if secure {
b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", err
}
input = string(b)
fmt.Println()
} else {
fmt.Scanln(&input)
}
return input, nil
}