Skip to content

Commit 43b5111

Browse files
committed
feat: use user's private key on signature for web session
From now on, the request to register credentials to initiate the WebSocket connection and open the web terminal no longer receives the signature of the username. Now, when the fingerprint is sent, we identify the public key as the session’s authentication method and require a signature of data sent on the WebSocket connection by the client. It should be signed with the private key, and the content sent back to the WebSocket. If the signature matches, the connection continues as usual.
1 parent e267025 commit 43b5111

File tree

4 files changed

+49
-17
lines changed

4 files changed

+49
-17
lines changed

ssh/web/conn.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ func (c *Conn) ReadMessage(message *Message) (int, error) {
6161
}
6262

6363
message.Data = dim
64+
case messageKindSignature:
65+
var signed string
66+
67+
if err = json.Unmarshal(data, &signed); err != nil {
68+
return 0, errors.Join(ErrConnReadMessageJSONInvalid)
69+
}
70+
71+
message.Data = signed
6472
default:
6573
return 0, errors.Join(ErrConnReadMessageKindInvalid)
6674
}

ssh/web/messages.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ const (
88
// messageKindResize is the identifier to a resize request message. This kind of message contains the number of
99
// columns and rows what the terminal should have.
1010
messageKindResize
11+
// messageKindSignature is the identifier to a signature message. This kind of message contains the data to be
12+
// signed by the user's private key.
13+
messageKindSignature
1114
)
1215

1316
type Message struct {

ssh/web/session.go

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package web
33
import (
44
"bytes"
55
"context"
6-
"crypto/rsa"
76
"encoding/base64"
87
"errors"
98
"fmt"
@@ -14,7 +13,6 @@ import (
1413
"github.com/shellhub-io/shellhub/pkg/api/internalclient"
1514
"github.com/shellhub-io/shellhub/pkg/cache"
1615
"github.com/shellhub-io/shellhub/pkg/uuid"
17-
"github.com/shellhub-io/shellhub/ssh/pkg/magickey"
1816
log "github.com/sirupsen/logrus"
1917
"golang.org/x/crypto/ssh"
2018
)
@@ -34,7 +32,7 @@ func (b *BannerError) Error() string {
3432
}
3533

3634
// getAuth gets the authentication methods from credentials.
37-
func getAuth(creds *Credentials, magicKey *rsa.PrivateKey) ([]ssh.AuthMethod, error) {
35+
func getAuth(conn *Conn, creds *Credentials) ([]ssh.AuthMethod, error) {
3836
if creds.isPassword() {
3937
return []ssh.AuthMethod{ssh.Password(creds.Password)}, nil
4038
}
@@ -71,24 +69,48 @@ func getAuth(creds *Credentials, magicKey *rsa.PrivateKey) ([]ssh.AuthMethod, er
7169
return nil, ErrDataPublicKey
7270
}
7371

74-
digest, err := base64.StdEncoding.DecodeString(creds.Signature)
75-
if err != nil {
76-
return nil, ErrSignaturePublicKey
72+
signer := &Signer{
73+
conn: conn,
74+
publicKey: &pubKey,
7775
}
7876

79-
if err := pubKey.Verify([]byte(creds.Username), &ssh.Signature{ //nolint: exhaustruct
80-
Format: pubKey.Type(),
81-
Blob: digest,
82-
}); err != nil {
83-
return nil, ErrVerifyPublicKey
77+
return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil
78+
}
79+
80+
type Signer struct {
81+
conn *Conn
82+
publicKey *ssh.PublicKey
83+
}
84+
85+
func (s *Signer) PublicKey() ssh.PublicKey {
86+
return *s.publicKey
87+
}
88+
89+
func (s *Signer) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
90+
dataB64 := base64.StdEncoding.EncodeToString(data)
91+
if _, err := s.conn.WriteMessage(&Message{Kind: messageKindSignature, Data: dataB64}); err != nil {
92+
return nil, err
93+
}
94+
95+
var msg Message
96+
if _, err := s.conn.ReadMessage(&msg); err != nil {
97+
return nil, fmt.Errorf("invalid signature response")
98+
}
99+
100+
signed, ok := msg.Data.(string)
101+
if !ok {
102+
return nil, fmt.Errorf("data isn't a signed string")
84103
}
85104

86-
signer, err := ssh.NewSignerFromKey(magicKey)
105+
blob, err := base64.StdEncoding.DecodeString(signed)
87106
if err != nil {
88-
return nil, ErrSignerPublicKey
107+
return nil, err
89108
}
90109

91-
return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil
110+
return &ssh.Signature{
111+
Format: s.PublicKey().Type(),
112+
Blob: blob,
113+
}, nil
92114
}
93115

94116
func newSession(ctx context.Context, cache cache.Cache, conn *Conn, creds *Credentials, dim Dimensions, info Info) error {
@@ -107,7 +129,7 @@ func newSession(ctx context.Context, cache cache.Cache, conn *Conn, creds *Crede
107129
uuid := uuid.Generate()
108130

109131
user := fmt.Sprintf("%s@%s", creds.Username, uuid)
110-
auth, err := getAuth(creds, magickey.GetRerefence())
132+
auth, err := getAuth(conn, creds)
111133
if err != nil {
112134
logger.WithError(err).Debug("failed to get the credentials")
113135

ssh/web/utils.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ type Credentials struct {
1717
Password string `json:"password"`
1818
// Fingerprint is the identifier of the public key used in the device's OS.
1919
Fingerprint string `json:"fingerprint"`
20-
Signature string `json:"signature"`
2120
}
2221

2322
func (c *Credentials) encryptPassword(key *rsa.PrivateKey) error {
@@ -56,7 +55,7 @@ func (c *Credentials) decryptPassword(key *rsa.PrivateKey) error {
5655
}
5756

5857
func (c *Credentials) isPublicKey() bool { // nolint: unused
59-
return c.Fingerprint != "" && c.Signature != ""
58+
return c.Fingerprint != ""
6059
}
6160

6261
// isPassword checks if connection is using password method.

0 commit comments

Comments
 (0)