Change the authentication workflow. This will allow Sshwifty to run on a multi-node autobalanced cluster. After deploy this version, users might have to reload the frontend page before continue using Sshwifty.

This commit is contained in:
NI
2021-03-02 20:54:58 +08:00
parent 6001d6dd5e
commit c6683b1311
6 changed files with 264 additions and 198 deletions

View File

@@ -23,7 +23,6 @@ import (
"crypto/hmac"
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"fmt"
"io"
"net/http"
@@ -66,32 +65,14 @@ type socket struct {
commonCfg configuration.Common
serverCfg configuration.Server
randomKey string
authKey []byte
upgrader websocket.Upgrader
commander command.Commander
}
func getNewSocketCtlRandomSharedKey() string {
b := [32]byte{}
func hashCombineSocketKeys(addedKey string, privateKey string) []byte {
h := hmac.New(sha512.New, []byte(privateKey))
io.ReadFull(rand.Reader, b[:])
return base64.StdEncoding.EncodeToString(b[:])
}
func getSocketAuthKey(randomKey string, sharedKey string) []byte {
var k []byte
if len(sharedKey) > 0 {
k = []byte(sharedKey)
} else {
k = []byte(randomKey)
}
h := hmac.New(sha512.New, k)
h.Write([]byte(randomKey))
h.Write([]byte(addedKey))
return h.Sum(nil)
}
@@ -101,13 +82,9 @@ func newSocketCtl(
cfg configuration.Server,
cmds command.Commands,
) socket {
randomKey := getNewSocketCtlRandomSharedKey()
return socket{
commonCfg: commonCfg,
serverCfg: cfg,
randomKey: randomKey,
authKey: getSocketAuthKey(randomKey, commonCfg.SharedKey)[:32],
upgrader: buildWebsocketUpgrader(cfg),
commander: command.New(cmds),
}
@@ -234,19 +211,18 @@ func (s socket) createCipher(key []byte) (cipher.AEAD, cipher.AEAD, error) {
return gcmRead, gcmWrite, nil
}
func (s socket) privateKey() string {
if len(s.commonCfg.SharedKey) > 0 {
return s.commonCfg.SharedKey
}
return s.randomKey
func (s socket) mixerKey(r *http.Request) []byte {
return hashCombineSocketKeys(
r.UserAgent(), s.commonCfg.SharedKey+"+"+s.commonCfg.HostName)
}
func (s socket) buildCipherKey() [16]byte {
func (s socket) buildCipherKey(r *http.Request) [16]byte {
key := [16]byte{}
now := strconv.FormatInt(time.Now().Unix()/100, 10)
copy(key[:], getSocketAuthKey(now, s.privateKey()))
copy(key[:], hashCombineSocketKeys(
strconv.FormatInt(time.Now().Unix()/100, 10),
string(s.mixerKey(r))+"+"+s.commonCfg.SharedKey,
))
return key
}
@@ -300,7 +276,7 @@ func (s socket) Get(
"Unable to send server nonce to client: %s", nonceSendErr.Error()))
}
cipherKey := s.buildCipherKey()
cipherKey := s.buildCipherKey(r)
readCipher, writeCipher, cipherCreationErr := s.createCipher(cipherKey[:])

View File

@@ -81,6 +81,22 @@ func newSocketVerification(
}
}
func (s socketVerification) authKey(r *http.Request) []byte {
timeMixer := strconv.FormatInt(time.Now().Unix()/100, 10)
if len(s.commonCfg.SharedKey) > 0 {
return hashCombineSocketKeys(
timeMixer,
s.commonCfg.SharedKey,
)[:32]
}
return hashCombineSocketKeys(
timeMixer,
"DEFAULT VERIFY KEY",
)[:32]
}
func (s socketVerification) setServerConfigRespond(
hd *http.Header, w http.ResponseWriter) {
hd.Add("X-Heartbeat", s.heartbeat)
@@ -104,7 +120,7 @@ func (s socketVerification) Get(
key := r.Header.Get("X-Key")
if len(key) <= 0 {
hd.Add("X-Key", s.randomKey)
hd.Add("X-Key", base64.StdEncoding.EncodeToString(s.mixerKey(r)))
if len(s.commonCfg.SharedKey) <= 0 {
s.setServerConfigRespond(&hd, w)
@@ -129,11 +145,13 @@ func (s socketVerification) Get(
return NewError(http.StatusBadRequest, decodedKeyErr.Error())
}
if !hmac.Equal(s.authKey, decodedKey) {
authKey := s.authKey(r)
if !hmac.Equal(authKey, decodedKey) {
return ErrSocketAuthFailed
}
hd.Add("X-Key", s.randomKey)
hd.Add("X-Key", base64.StdEncoding.EncodeToString(s.mixerKey(r)))
s.setServerConfigRespond(&hd, w)
return nil