Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore client connection closing - simpler challenge response error handling #18

Merged
merged 1 commit into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ services:
max-size: 10m
ports:
- '3000:3000'
image: 856da056de3abb7b317e59481bd500c27eb0f3b2a4c2432e8db930c1d20e71bf
container_name: ssh-sync-server-debug
image: b44427a64de93c20123c068387b0adc0434434ba709fbd91dd03d33ade489c3e
container_name: sshdbg
ssh-sync-db:
image: therealpaulgg/ssh-sync-db:latest
container_name: ssh-sync-db-debug
Expand All @@ -26,17 +26,17 @@ services:
- POSTGRES_DB=sshsync
restart: always
ssh-sync:
image: 9065faaa7a20a821f7323f42cbddac1b594f7d01d57f8b2a2837e433769b86f4
image: 62eab8fb32b34e0a2cf36e8635d810c20a38baa2d7beaf5b6918139339e23c23
container_name: ssh-sync
stdin_open: true # Allows Docker container to keep STDIN open
tty: true # Allocates a pseudo-TTY
ssh-sync-2:
image: 9065faaa7a20a821f7323f42cbddac1b594f7d01d57f8b2a2837e433769b86f4
image: 62eab8fb32b34e0a2cf36e8635d810c20a38baa2d7beaf5b6918139339e23c23
container_name: ssh-sync-2
stdin_open: true # Allows Docker container to keep STDIN open
tty: true # Allocates a pseudo-TTY
ssh-sync-3:
image: 9065faaa7a20a821f7323f42cbddac1b594f7d01d57f8b2a2837e433769b86f4
image: 62eab8fb32b34e0a2cf36e8635d810c20a38baa2d7beaf5b6918139339e23c23
container_name: ssh-sync-3
stdin_open: true # Allows Docker container to keep STDIN open
tty: true # Allocates a pseudo-TTY
Expand Down
60 changes: 18 additions & 42 deletions pkg/web/live/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package live
import (
"database/sql"
"errors"
"io"
"net"
"net/http"
"strings"
Expand Down Expand Up @@ -35,7 +34,7 @@ type ChallengeResponse struct {
ResponseChannel chan bool
}

type Something struct {
type ChallengeSession struct {
Username string
ChallengeAccepted chan bool
ChallengerChannel chan []byte
Expand All @@ -44,18 +43,18 @@ type Something struct {

type SafeChallengeResponseDict struct {
mux sync.Mutex
dict map[string]Something
dict map[string]ChallengeSession
}

// Utility method for safely writing to the dict
func (c *SafeChallengeResponseDict) WriteChallenge(challengePhrase string, data Something) {
func (c *SafeChallengeResponseDict) WriteChallenge(challengePhrase string, data ChallengeSession) {
c.mux.Lock()
c.dict[challengePhrase] = data
c.mux.Unlock()
}

// Utility method for safely reading from the dict
func (c *SafeChallengeResponseDict) ReadChallenge(challengePhrase string) (Something, bool) {
func (c *SafeChallengeResponseDict) ReadChallenge(challengePhrase string) (ChallengeSession, bool) {
c.mux.Lock()
data, exists := c.dict[challengePhrase]
c.mux.Unlock()
Expand All @@ -64,7 +63,7 @@ func (c *SafeChallengeResponseDict) ReadChallenge(challengePhrase string) (Somet

var ChallengeResponseChannel = make(chan ChallengeResponse)
var ChallengeResponseDict = SafeChallengeResponseDict{
dict: make(map[string]Something),
dict: make(map[string]ChallengeSession),
}

func MachineChallengeResponse(i *do.Injector, r *http.Request, w http.ResponseWriter) error {
Expand Down Expand Up @@ -107,6 +106,13 @@ func MachineChallengeResponseHandler(i *do.Injector, r *http.Request, w http.Res
// need a channel that both threads can access so that machine B can send public key to machine A
// and machine A can send back encrypted master key
key := <-chalChan.ChallengerChannel
if key == nil {
log.Debug().Msg("Response from challenger channel - key is nil. Exiting.")
if err := utils.WriteServerError[dto.ChallengeSuccessEncryptedKeyDto](&conn, "Error responding to challenge - client abruptly closed connection."); err != nil {
log.Err(err).Msg("Error writing server error")
}
return
}
keys := dto.ChallengeSuccessEncryptedKeyDto{
PublicKey: key,
}
Expand All @@ -133,25 +139,6 @@ func NewMachineChallenge(i *do.Injector, r *http.Request, w http.ResponseWriter)

func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.ResponseWriter, c *net.Conn) {
conn := *c
defer conn.Close()
closeChan := make(chan struct{}) // Channel to signal connection closure

// Start a goroutine to monitor connection for closure
go func() {
buf := make([]byte, 1)

for {
_, err := conn.Read(buf)
if err != nil {
if err == io.EOF {
close(closeChan)
} else {
close(closeChan)
}
return
}
}
}()
// first message sent should be JSON payload
userMachine, err := utils.ReadClientMessage[dto.UserMachineDto](&conn)
if err != nil {
Expand Down Expand Up @@ -207,7 +194,7 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
// Computer A will receive the public key, decrypt the master key, encrypt the master key with the public key, and send it back to the server.
// At this point Computer B will be able to communicate freely.

ChallengeResponseDict.WriteChallenge(challengePhrase, Something{
ChallengeResponseDict.WriteChallenge(challengePhrase, ChallengeSession{
Username: user.Username,
ChallengeAccepted: make(chan bool),
ChallengerChannel: make(chan []byte),
Expand All @@ -225,6 +212,7 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
}
}()
timer := time.NewTimer(30 * time.Second)
challengeResponse := make(chan bool)
go func() {
var challengeAcceptedChan chan bool

Expand All @@ -243,26 +231,14 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
for {
select {
case <-timer.C:
ChallengeResponseDict.mux.Lock()
// Check if the challenge still exists before sending to the channel
if _, exists := ChallengeResponseDict.dict[challengePhrase]; exists {
ChallengeResponseDict.dict[challengePhrase].ChallengeAccepted <- false
}
ChallengeResponseDict.mux.Unlock()
challengeResponse <- false
return
case chalWon := <-challengeAcceptedChan:
log.Debug().Msg("Gorountine received challenge response")
if chalWon {
timer.Stop()
}
return
case <-closeChan:
log.Debug().Msg("Connection closed by client")
ChallengeResponseDict.mux.Lock()
// Check if the challenge still exists before sending to the channel
if _, exists := ChallengeResponseDict.dict[challengePhrase]; exists {
ChallengeResponseDict.dict[challengePhrase].ChallengeAccepted <- false
}
ChallengeResponseDict.mux.Unlock()
challengeResponse <- chalWon
return
}
}
Expand All @@ -272,7 +248,7 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
log.Err(err).Msg("Error getting challenge from dict")
return
}
challengeResult := <-cha.ChallengeAccepted
challengeResult := <-challengeResponse

if !challengeResult {
if err := utils.WriteServerError[dto.MessageDto](&conn, "Challenge timed out"); err != nil {
Expand Down
Loading