fix: send ssh username to guacd (default root) for console SSH
This commit is contained in:
@@ -21,6 +21,7 @@ func consoleConnect(c *gin.Context) {
|
||||
KeyID string `json:"key_id"`
|
||||
RDPUsername string `json:"rdp_username"`
|
||||
RDPPassword string `json:"rdp_password"`
|
||||
SSHUsername string `json:"ssh_username"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
@@ -51,6 +52,13 @@ func consoleConnect(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if body.Protocol == "ssh" {
|
||||
if err := services.SetConsoleSSHUser(sess.SessionID, body.SSHUsername); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
services.LogEvent("console.opened", actorFromCtx(c), srv.ServerID, "",
|
||||
"console session opened ("+body.Protocol+")")
|
||||
|
||||
@@ -98,7 +106,7 @@ func consoleTunnel(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
gp, err := services.BuildGuacParams(srv, sess.Protocol, privKey, rdpUser, rdpPass)
|
||||
gp, err := services.BuildGuacParams(srv, sess.Protocol, sess.SSHUsername, privKey, rdpUser, rdpPass)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
||||
@@ -17,6 +17,8 @@ type ConsoleSession struct {
|
||||
EndedAt *time.Time `bson:"ended_at,omitempty" json:"ended_at,omitempty"`
|
||||
ClientIP string `bson:"client_ip,omitempty" json:"client_ip,omitempty"`
|
||||
|
||||
SSHUsername string `bson:"ssh_username,omitempty" json:"ssh_username,omitempty"`
|
||||
|
||||
RDPUserEnc string `bson:"rdp_user_enc,omitempty" json:"-"`
|
||||
RDPPassEnc string `bson:"rdp_pass_enc,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func portOr(v, def int) string {
|
||||
// BuildGuacParams assembles the guacd connection parameter map for a protocol.
|
||||
// privateKey is the decrypted SSH private key (ssh only); rdpUser/rdpPass are
|
||||
// used for rdp. None of these values are persisted or logged by the caller.
|
||||
func BuildGuacParams(srv *models.Server, protocol, privateKey, rdpUser, rdpPass string) (*GuacParams, error) {
|
||||
func BuildGuacParams(srv *models.Server, protocol, sshUser, privateKey, rdpUser, rdpPass string) (*GuacParams, error) {
|
||||
host := srv.IPAddress
|
||||
switch protocol {
|
||||
case "ssh":
|
||||
@@ -97,6 +97,10 @@ func BuildGuacParams(srv *models.Server, protocol, privateKey, rdpUser, rdpPass
|
||||
"hostname": host,
|
||||
"port": portOr(srv.SSHPort, 22),
|
||||
}
|
||||
if sshUser == "" {
|
||||
sshUser = "root"
|
||||
}
|
||||
p["username"] = sshUser
|
||||
if privateKey != "" {
|
||||
p["private-key"] = privateKey
|
||||
}
|
||||
@@ -200,6 +204,16 @@ func ConsumeConsoleRDPCreds(sessionID string) (username, password string, err er
|
||||
return username, password, nil
|
||||
}
|
||||
|
||||
// SetConsoleSSHUser persists the SSH username to use on the session doc.
|
||||
func SetConsoleSSHUser(sessionID, username string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_, err := db.Col("console_sessions").UpdateOne(ctx,
|
||||
bson.M{"session_id": sessionID},
|
||||
bson.M{"$set": bson.M{"ssh_username": username}})
|
||||
return err
|
||||
}
|
||||
|
||||
func EndConsoleSession(sessionID string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestSessionTokenTampered(t *testing.T) {
|
||||
|
||||
func TestBuildGuacParamsSSH(t *testing.T) {
|
||||
srv := &models.Server{IPAddress: "10.0.0.5", SSHPort: 22}
|
||||
p, err := BuildGuacParams(srv, "ssh", "PRIVATE-KEY-DATA", "", "")
|
||||
p, err := BuildGuacParams(srv, "ssh", "", "PRIVATE-KEY-DATA", "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
@@ -59,11 +59,14 @@ func TestBuildGuacParamsSSH(t *testing.T) {
|
||||
if p.Params["private-key"] != "PRIVATE-KEY-DATA" {
|
||||
t.Fatalf("missing private-key")
|
||||
}
|
||||
if p.Params["username"] != "root" {
|
||||
t.Fatalf("expected default username root, got %q", p.Params["username"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildGuacParamsRDP(t *testing.T) {
|
||||
srv := &models.Server{IPAddress: "10.0.0.9", RDPPort: 3389}
|
||||
p, err := BuildGuacParams(srv, "rdp", "", "administrator", "s3cret")
|
||||
p, err := BuildGuacParams(srv, "rdp", "", "", "administrator", "s3cret")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
@@ -77,7 +80,7 @@ func TestBuildGuacParamsRDP(t *testing.T) {
|
||||
|
||||
func TestBuildGuacParamsUnknownProtocol(t *testing.T) {
|
||||
srv := &models.Server{IPAddress: "10.0.0.9"}
|
||||
if _, err := BuildGuacParams(srv, "telnet", "", "", ""); err == nil {
|
||||
if _, err := BuildGuacParams(srv, "telnet", "", "", "", ""); err == nil {
|
||||
t.Fatalf("expected error for unknown protocol")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function ServerConsolePage() {
|
||||
|
||||
const [protocol, setProtocol] = useState<string>(searchParams.get("protocol") || "");
|
||||
const [keyId, setKeyId] = useState<string>("");
|
||||
const [sshUsername, setSshUsername] = useState<string>("root");
|
||||
const [rdpUsername, setRdpUsername] = useState("");
|
||||
const [rdpPassword, setRdpPassword] = useState("");
|
||||
const [connecting, setConnecting] = useState(false);
|
||||
@@ -72,6 +73,7 @@ export default function ServerConsolePage() {
|
||||
};
|
||||
if (protocol === "ssh") {
|
||||
body.key_id = keyId || undefined;
|
||||
body.ssh_username = sshUsername || undefined;
|
||||
} else if (protocol === "rdp") {
|
||||
body.rdp_username = rdpUsername || undefined;
|
||||
body.rdp_password = rdpPassword || undefined;
|
||||
@@ -152,6 +154,14 @@ export default function ServerConsolePage() {
|
||||
|
||||
{protocol === "ssh" && (
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-text-secondary">SSH Username</label>
|
||||
<input
|
||||
type="text"
|
||||
value={sshUsername}
|
||||
onChange={(e) => setSshUsername(e.target.value)}
|
||||
placeholder="root"
|
||||
className="mb-3 w-full rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm text-text-primary focus:border-accent/50 focus:outline-none focus:ring-1 focus:ring-accent/30"
|
||||
/>
|
||||
<label className="mb-1.5 block text-sm font-medium text-text-secondary">SSH Key</label>
|
||||
<select
|
||||
value={keyId}
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface ConsoleConnectRequest {
|
||||
key_id?: string;
|
||||
rdp_username?: string;
|
||||
rdp_password?: string;
|
||||
ssh_username?: string;
|
||||
}
|
||||
|
||||
export interface ConsoleConnectResponse {
|
||||
|
||||
Reference in New Issue
Block a user