mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
bcrypt support for passwords and tokens
This commit is contained in:
@@ -1,9 +1,20 @@
|
||||
// Copyright 2014-2015 Apcera Inc. All rights reserved.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nats-io/gnatsd/server"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const BcryptPrefix = "$2a$"
|
||||
|
||||
func isBcrypt(password string) bool {
|
||||
return strings.HasPrefix(password, BcryptPrefix)
|
||||
}
|
||||
|
||||
type Plain struct {
|
||||
Username string
|
||||
Password string
|
||||
@@ -11,7 +22,15 @@ type Plain struct {
|
||||
|
||||
func (p *Plain) Check(c server.ClientAuth) bool {
|
||||
opts := c.GetOpts()
|
||||
if p.Username != opts.Username || p.Password != opts.Password {
|
||||
if p.Username != opts.Username {
|
||||
return false
|
||||
}
|
||||
// Check to see if the password is a bcrypt hash
|
||||
if isBcrypt(p.Password) {
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(p.Password), []byte(opts.Password)); err != nil {
|
||||
return false
|
||||
}
|
||||
} else if p.Password != opts.Password {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/nats-io/gnatsd/server"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
@@ -10,7 +11,12 @@ type Token struct {
|
||||
|
||||
func (p *Token) Check(c server.ClientAuth) bool {
|
||||
opts := c.GetOpts()
|
||||
if p.Token != opts.Authorization {
|
||||
// Check to see if the token is a bcrypt hash
|
||||
if isBcrypt(p.Token) {
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(p.Token), []byte(opts.Authorization)); err != nil {
|
||||
return false
|
||||
}
|
||||
} else if p.Token != opts.Authorization {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -156,3 +156,75 @@ func TestPasswordClientGoodConnect(t *testing.T) {
|
||||
doAuthConnect(t, c, "", AUTH_USER, AUTH_PASS)
|
||||
expectResult(t, c, okRe)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// The bcrypt username/password version
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// Generated with util/mkpasswd
|
||||
const BCRYPT_AUTH_PASS = "#00L2zPr!j11VsT@e9QGPt"
|
||||
const BCRYPT_AUTH_HASH = "$2a$11$wDaOBnEx0GbcFTOzJRywpexI/dxH3sqV3.adFefmDDMJggnOTNqKS"
|
||||
|
||||
func runAuthServerWithBcryptUserPass() *server.Server {
|
||||
opts := DefaultTestOptions
|
||||
opts.Port = AUTH_PORT
|
||||
opts.Username = AUTH_USER
|
||||
opts.Password = BCRYPT_AUTH_HASH
|
||||
|
||||
auth := &auth.Plain{Username: AUTH_USER, Password: BCRYPT_AUTH_HASH}
|
||||
return RunServerWithAuth(&opts, auth)
|
||||
}
|
||||
|
||||
func TestBadBcryptPassword(t *testing.T) {
|
||||
s := runAuthServerWithBcryptUserPass()
|
||||
defer s.Shutdown()
|
||||
c := createClientConn(t, "localhost", AUTH_PORT)
|
||||
defer c.Close()
|
||||
expectAuthRequired(t, c)
|
||||
doAuthConnect(t, c, "", AUTH_USER, BCRYPT_AUTH_HASH)
|
||||
expectResult(t, c, errRe)
|
||||
}
|
||||
|
||||
func TestGoodBcryptPassword(t *testing.T) {
|
||||
s := runAuthServerWithBcryptUserPass()
|
||||
defer s.Shutdown()
|
||||
c := createClientConn(t, "localhost", AUTH_PORT)
|
||||
defer c.Close()
|
||||
expectAuthRequired(t, c)
|
||||
doAuthConnect(t, c, "", AUTH_USER, BCRYPT_AUTH_PASS)
|
||||
expectResult(t, c, okRe)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// The bcrypt authorization token version
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
const BCRYPT_AUTH_TOKEN = "743&@WeTlIwtHDytI5Bnxl"
|
||||
const BCRYPT_AUTH_TOKEN_HASH = "$2a$11$Gp5x2rvdzfm9rUREuyQeBOFd61oPYKoLWSI2fJN7DAFMF34Z9o4s2"
|
||||
|
||||
func runAuthServerWithBcryptToken() *server.Server {
|
||||
opts := DefaultTestOptions
|
||||
opts.Port = AUTH_PORT
|
||||
opts.Authorization = BCRYPT_AUTH_TOKEN_HASH
|
||||
return RunServerWithAuth(&opts, &auth.Token{Token: BCRYPT_AUTH_TOKEN_HASH})
|
||||
}
|
||||
|
||||
func TestBadBcryptToken(t *testing.T) {
|
||||
s := runAuthServerWithBcryptToken()
|
||||
defer s.Shutdown()
|
||||
c := createClientConn(t, "localhost", AUTH_PORT)
|
||||
defer c.Close()
|
||||
expectAuthRequired(t, c)
|
||||
doAuthConnect(t, c, BCRYPT_AUTH_TOKEN_HASH, "", "")
|
||||
expectResult(t, c, errRe)
|
||||
}
|
||||
|
||||
func TestGoodBcryptToken(t *testing.T) {
|
||||
s := runAuthServerWithBcryptToken()
|
||||
defer s.Shutdown()
|
||||
c := createClientConn(t, "localhost", AUTH_PORT)
|
||||
defer c.Close()
|
||||
expectAuthRequired(t, c)
|
||||
doAuthConnect(t, c, BCRYPT_AUTH_TOKEN, "", "")
|
||||
expectResult(t, c, okRe)
|
||||
}
|
||||
|
||||
76
util/mkpasswd.go
Normal file
76
util/mkpasswd.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015 Apcera Inc. All rights reserved.
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
log.Fatalf("Usage: mkpasswd -p <stdin password> \n")
|
||||
}
|
||||
|
||||
const (
|
||||
PasswordLength = 22
|
||||
Cost = 11
|
||||
)
|
||||
|
||||
func main() {
|
||||
var pw = flag.Bool("p", false, "Input password via stdin")
|
||||
|
||||
log.SetFlags(0)
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
var password string
|
||||
|
||||
if *pw {
|
||||
fmt.Printf("Enter Password: ")
|
||||
bytePassword, _ := terminal.ReadPassword(0)
|
||||
fmt.Printf("\nReenter Password: ")
|
||||
bytePassword2, _ := terminal.ReadPassword(0)
|
||||
if !bytes.Equal(bytePassword, bytePassword2) {
|
||||
log.Fatalf("Error, passwords do not match\n")
|
||||
}
|
||||
password = string(bytePassword)
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
password = genPassword()
|
||||
}
|
||||
|
||||
if *pw {
|
||||
fmt.Printf("\n")
|
||||
} else {
|
||||
fmt.Printf("pass: %s\n", password)
|
||||
}
|
||||
|
||||
cb, err := bcrypt.GenerateFromPassword([]byte(password), Cost)
|
||||
if err != nil {
|
||||
log.Fatalf("Error producing bcrypt hash: %v\n", err)
|
||||
}
|
||||
fmt.Printf("bcrypt hash: %s\n", cb)
|
||||
}
|
||||
|
||||
func genPassword() string {
|
||||
var ch = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@$#%^&*()")
|
||||
b := make([]byte, PasswordLength)
|
||||
max := big.NewInt(int64(len(ch)))
|
||||
for i := range b {
|
||||
ri, err := rand.Int(rand.Reader, max)
|
||||
if err != nil {
|
||||
log.Fatalf("Error producing random integer: %v\n", err)
|
||||
}
|
||||
b[i] = ch[int(ri.Int64())]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user