Skip to content

Commit

Permalink
Move to using golang http library verbs, add non-functional challenge
Browse files Browse the repository at this point in the history
  • Loading branch information
NHAS committed Jun 6, 2024
1 parent 8d61031 commit d64f3d4
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 46 deletions.
1 change: 1 addition & 0 deletions internal/data/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Device struct {
Active bool
Authorised time.Time

Challenge string
AssociatedNode types.ID
}

Expand Down
15 changes: 15 additions & 0 deletions internal/data/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ var (
exit = make(chan bool)
)

func DeregisterEventListener(key string) error {
clusterHealthLck.Lock()
defer clusterHealthLck.Unlock()
cancelFunc, ok := contextMaps[key]
if !ok {
return fmt.Errorf("even listener was not found: %s", key)
}

cancelFunc()

delete(contextMaps, key)

return nil
}

func RegisterEventListener[T any](path string, isPrefix bool, f func(key string, current, previous T, et EventType) error) (string, error) {

options := []clientv3.OpOption{
Expand Down
11 changes: 2 additions & 9 deletions internal/router/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func Setup(errorChan chan<- error, iptables bool) (err error) {
select {
case <-cancel:
return
case <-time.After(100 * time.Millisecond):
case <-time.After(500 * time.Millisecond):
dev, err := ctrl.Device(config.Values.Wireguard.DevName)
if err != nil {
errorChan <- fmt.Errorf("endpoint watcher: %s", err)
Expand Down Expand Up @@ -92,16 +92,9 @@ func Setup(errorChan chan<- error, iptables bool) (err error) {
if ourPeerAddresses[device.Address] != p.Endpoint.String() && p.Endpoint != nil {
ourPeerAddresses[device.Address] = p.Endpoint.String()

// If we register an endpoint change on our real world device, and the Endpoint is not the same as what the cluster knows
// i.e the peer has either roamed and its egress has changed, or it's an attacker using a stolen wireguard profile
// Deauthenticate it
if device.Endpoint.String() != p.Endpoint.String() {
// This condition will trigger a challenge on the cluster
log.Printf("%s:%s endpoint changed %s -> %s", device.Address, device.Username, device.Endpoint.String(), p.Endpoint.String())

err = data.DeauthenticateDevice(device.Address)
if err != nil {
log.Printf("failed to deauth device (%s:%s) endpoint: %s", device.Address, device.Username, err)
}
}

// Otherwise, just update the node association
Expand Down
6 changes: 3 additions & 3 deletions internal/users/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ func (u *user) Authenticate(device, mfaType string, authenticator types.Authenti
// Make sure that the attempts is always incremented first to stop race condition attacks
err := data.IncrementAuthenticationAttempt(u.Username, device)
if err != nil {
return err
return fmt.Errorf("failed to pre-emptively increment authentication attempt counter: %s", err)
}

mfa, userMfaType, attempts, locked, err := data.GetAuthenticationDetails(u.Username, device)
if err != nil {
return err
return fmt.Errorf("failed to get authenticator details: %s", err)
}

lockout, err := data.GetLockout()
Expand Down Expand Up @@ -158,7 +158,7 @@ func (u *user) Deauthenticate(device string) error {
func (u *user) MFA() (string, error) {
url, err := data.GetMFASecret(u.Username)
if err != nil {
return "", err
return "", fmt.Errorf("failed to get MFA details: %s", err)
}

return url, nil
Expand Down
10 changes: 5 additions & 5 deletions internal/webserver/authenticators/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ func (o *Oidc) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

if user.IsEnforcingMFA() {
log.Println(user.Username, clientTunnelIp, "tried to re-register mfa despite already being registered")

http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand All @@ -127,7 +127,7 @@ func (o *Oidc) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
err = data.SetUserMfa(user.Username, string(value), o.Type())
if err != nil {
log.Println(user.Username, clientTunnelIp, "unable to set authentication method as oidc key to db:", err)
http.Error(w, "Unknown error", 500)
http.Error(w, "Server error", http.StatusInternalServerError)
return
}

Expand All @@ -147,7 +147,7 @@ func (o *Oidc) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -182,7 +182,7 @@ func (o *Oidc) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {
for i := range groupsIntf {
conv, ok := groupsIntf[i].(string)
if !ok {
log.Println("Error, could not convert group claim to string, probably error in oidc idP configuration")
log.Println("Error, could not convert group claim to string, probably mistake in your OIDC idP configuration")
http.Error(w, "Server Error", http.StatusInternalServerError)
return
}
Expand Down
15 changes: 7 additions & 8 deletions internal/webserver/authenticators/pam.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ func (t *Pam) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

if user.IsEnforcingMFA() {
log.Println(user.Username, clientTunnelIp, "tried to re-register mfa despite already being registered")

http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -104,7 +104,7 @@ func (t *Pam) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand All @@ -131,15 +131,15 @@ func (t *Pam) AuthoriseFunc(w http.ResponseWriter, r *http.Request) types.Authen
return func(mfaSecret, username string) error {
err := r.ParseForm()
if err != nil {
http.Error(w, "Bad request", 400)
return err
http.Error(w, "Bad request", http.StatusBadRequest)
return fmt.Errorf("failed to parse form: %s", err)
}

passwd := r.FormValue("password")

pamDetails, err := data.GetPAM()
if err != nil {
http.Error(w, "Unable to get pam details: "+err.Error(), 500)
http.Error(w, "Unable to get pam details: "+err.Error(), http.StatusInternalServerError)
return err
}

Expand Down Expand Up @@ -177,10 +177,9 @@ func (t *Pam) AuthoriseFunc(w http.ResponseWriter, r *http.Request) types.Authen
pamUsername, err := t.GetItem(pam.User)
if err != nil {
return fmt.Errorf("PAM get user '%s' (%s) failed", pamUsername, username)
} else {
return nil
}

return nil
}
}

Expand Down
28 changes: 15 additions & 13 deletions internal/webserver/authenticators/totp.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ func (t *Totp) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

if user.IsEnforcingMFA() {
log.Println(user.Username, clientTunnelIp, "tried to re-register mfa despite already being registered")

http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand All @@ -77,7 +77,7 @@ func (t *Totp) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println(user.Username, clientTunnelIp, "unable to get issuer from datastore")

http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
key, err := totp.Generate(totp.GenerateOpts{
Expand All @@ -86,29 +86,29 @@ func (t *Totp) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
log.Println(user.Username, clientTunnelIp, "generate key failed:", err)
http.Error(w, "Unknown error", 500)
http.Error(w, "Unknown error", http.StatusInternalServerError)
return
}

err = data.SetUserMfa(user.Username, key.URL(), t.Type())
if err != nil {
log.Println(user.Username, clientTunnelIp, "unable to save totp key to db:", err)
http.Error(w, "Unknown error", 500)
http.Error(w, "Server Error", http.StatusInternalServerError)
return
}

image, err := key.Image(200, 200)
if err != nil {
log.Println(user.Username, clientTunnelIp, "generating image failed:", err)
http.Error(w, "Unknown error", 500)
http.Error(w, "Server Error", http.StatusInternalServerError)
return
}

var buff bytes.Buffer
err = png.Encode(&buff, image)
if err != nil {
log.Println(user.Username, clientTunnelIp, "encoding mfa secret as png failed:", err)
http.Error(w, "Unknown error", 500)
http.Error(w, "Server Error", http.StatusInternalServerError)
return
}

Expand All @@ -122,7 +122,7 @@ func (t *Totp) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
AccountName: key.AccountName(),
}

jsonResponse(w, &mfa, 200)
jsonResponse(w, &mfa, http.StatusOK)

case "POST":
err = user.Authenticate(clientTunnelIp.String(), t.Type(), t.AuthoriseFunc(w, r))
Expand All @@ -137,6 +137,7 @@ func (t *Totp) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
log.Println(user.Username, clientTunnelIp, "authorised")
if err := user.EnforceMFA(); err != nil {
log.Println(user.Username, clientTunnelIp, "enforce mfa failed:", err)
return
}

default:
Expand All @@ -162,7 +163,7 @@ func (t *Totp) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand All @@ -178,6 +179,7 @@ func (t *Totp) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {

if err != nil {
log.Println(user.Username, clientTunnelIp, "failed to authorise: ", err.Error())
// Intentionally missing http.Error as its returned via json
return
}

Expand All @@ -189,7 +191,7 @@ func (t *Totp) AuthoriseFunc(w http.ResponseWriter, r *http.Request) types.Authe
return func(mfaSecret, username string) error {
err := r.ParseForm()
if err != nil {
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return err
}

Expand All @@ -200,13 +202,13 @@ func (t *Totp) AuthoriseFunc(w http.ResponseWriter, r *http.Request) types.Authe
return err
}

lockULock.Lock()
defer lockULock.Unlock()

if !totp.Validate(code, key.Secret()) {
return errors.New("code does not match expected")
}

lockULock.Lock()
defer lockULock.Unlock()

e := usedCodes[username]
if e.code == code && e.usetime.Add(30*time.Second).After(time.Now()) {
return errors.New("code already used")
Expand Down
6 changes: 3 additions & 3 deletions internal/webserver/authenticators/webauthn.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ func (wa *Webauthn) RegistrationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

if user.IsEnforcingMFA() {
log.Println(user.Username, clientTunnelIp, "tried to re-register mfa despite already being registered")

http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -188,7 +188,7 @@ func (wa *Webauthn) AuthorisationAPI(w http.ResponseWriter, r *http.Request) {
user, err := users.GetUserFromAddress(clientTunnelIp)
if err != nil {
log.Println("unknown", clientTunnelIp, "could not get associated device:", err)
http.Error(w, "Bad request", 400)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}

Expand Down
Loading

0 comments on commit d64f3d4

Please sign in to comment.