htpasswd-alike util
This commit is contained in:
parent
a546f7627d
commit
de8cbc0033
2
go.sum
2
go.sum
|
@ -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-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-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-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/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-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/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
|
21
main.go
21
main.go
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -65,12 +66,9 @@ type CLIArgs struct {
|
||||||
autocertACME string
|
autocertACME string
|
||||||
autocertEmail string
|
autocertEmail string
|
||||||
autocertHTTP string
|
autocertHTTP string
|
||||||
}
|
passwd string
|
||||||
|
passwdCost int
|
||||||
func list_ciphers() {
|
positionalArgs []string
|
||||||
for _, cipher := range tls.CipherSuites() {
|
|
||||||
fmt.Println(cipher.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_args() CLIArgs {
|
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.autocertACME, "autocert-acme", autocert.DefaultACMEDirectory, "custom ACME endpoint")
|
||||||
flag.StringVar(&args.autocertEmail, "autocert-email", "", "email used for ACME registration")
|
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.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()
|
flag.Parse()
|
||||||
|
args.positionalArgs = flag.Args()
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +112,13 @@ func run() int {
|
||||||
return 0
|
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)
|
logWriter := NewLogWriter(os.Stderr)
|
||||||
defer logWriter.Close()
|
defer logWriter.Close()
|
||||||
|
|
||||||
|
|
76
utils.go
76
utils.go
|
@ -16,6 +16,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const COPY_BUF = 128 * 1024
|
const COPY_BUF = 128 * 1024
|
||||||
|
@ -192,6 +195,62 @@ func makeCipherList(ciphers string) []uint16 {
|
||||||
return cipherIDList
|
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) {
|
func fileModTime(filename string) (time.Time, error) {
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -206,3 +265,20 @@ func fileModTime(filename string) (time.Time, error) {
|
||||||
|
|
||||||
return fi.ModTime(), nil
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue