Skip to content

Commit

Permalink
enable automatic auth to pubkey with no-auth addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
mleku committed Dec 12, 2024
1 parent 2bd1b3a commit 3916c45
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 32 deletions.
1 change: 1 addition & 0 deletions cmd/realy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func main() {
r := &app.Relay{C: cfg, Store: storage}
go app.MonitorResources(c)
var server *realy.Server
log.I.S(cfg.NoAuthAddresses)
if server, err = realy.NewServer(realy.ServerParams{
Ctx: c,
Cancel: cancel,
Expand Down
2 changes: 1 addition & 1 deletion realy/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type C struct {
MemLimit no `env:"MEMLIMIT" default:"250000000" usage:"set memory limit, default is 250Mb"`
UseCompact bo `env:"USE_COMPACT" default:"false" usage:"use the compact database encoding for the ratel event store"`
Compression st `env:"COMPRESSION" default:"none" usage:"compress the database, [none|snappy|zstd]"`
NoAuthAddresses []st `env:"NO_AUTH_ADDRESSES" usage:"IP addresses that don't require auth (for such things as clients using the relay as a cache relay)'"`
NoAuthAddresses []st `env:"NO_AUTH_ADDRESSES" usage:"IP addresses that don't require auth, optionally add a hex pubkey after a ':' to make the address automatically populate the websocket authed pubkey (mainly for use with cache relay feature of nostrudel)"`
// NWC st `env:"NWC" usage:"NWC connection string for relay to interact with an NWC enabled wallet"` // todo
}

Expand Down
2 changes: 1 addition & 1 deletion realy/handleAuth.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (s *Server) handleAuth(ws *web.Socket, req by) (msg by) {
if err = okenvelope.NewFrom(env.Event.ID, true, by{}).Write(ws); chk.E(err) {
return
}
log.D.F("%s authed to pubkey,%0x", ws.RealRemote(), env.Event.PubKey)
log.D.F("%s authed to pubkey %0x", ws.RealRemote(), env.Event.PubKey)
ws.SetAuthed(st(env.Event.PubKey))
}
}
Expand Down
12 changes: 2 additions & 10 deletions realy/handleCount.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package realy

import (
"strings"

"realy.lol/context"
"realy.lol/envelopes/authenvelope"
"realy.lol/envelopes/closedenvelope"
Expand All @@ -20,13 +18,7 @@ func (s *Server) handleCount(c context.T, ws *web.Socket, req by, store store.I)
if !ok {
return normalize.Restricted.F("this relay does not support NIP-45")
}
var noAuth bo
for _, v := range s.noAuthAddresses {
if strings.HasPrefix(v, ws.RealRemote()) {
// we are not requiring auth from this address (should be private address)
noAuth = true
}
}
noAuth := s.checkNoAuth(ws)
if !noAuth && (ws.AuthRequested() && len(ws.Authed()) == 0) {
return by("awaiting auth for count")
}
Expand All @@ -43,7 +35,7 @@ func (s *Server) handleCount(c context.T, ws *web.Socket, req by, store store.I)
return normalize.Error.F("COUNT has no <subscription id>")
}
allowed := env.Filters
if accepter, ok := s.relay.(relay.ReqAcceptor); ok {
if accepter, ok := s.relay.(relay.ReqAcceptor); ok && !noAuth {
var accepted bo
allowed, accepted = accepter.AcceptReq(c, ws.Req(), env.Subscription.T, env.Filters,
by(ws.Authed()))
Expand Down
8 changes: 1 addition & 7 deletions realy/handleEvent.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@ import (

func (s *Server) handleEvent(c cx, ws *web.Socket, req by, sto store.I) (msg by) {
log.T.F("handleEvent %s %s %v", ws.RealRemote(), req, s.noAuthAddresses)
var noAuth bo
for _, v := range s.noAuthAddresses {
if strings.HasPrefix(v, ws.RealRemote()) {
// we are not requiring auth from this address (should be private address)
noAuth = true
}
}
noAuth := s.checkNoAuth(ws)
if !noAuth && ws.AuthRequested() && len(ws.Authed()) == 0 {
return by("awaiting auth for event")
}
Expand Down
12 changes: 2 additions & 10 deletions realy/handleReq.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package realy
import (
"errors"
"sort"
"strings"

"github.com/dgraph-io/badger/v4"

"realy.lol/envelopes/authenvelope"
Expand All @@ -25,13 +23,7 @@ import (
)

func (s *Server) handleReq(c cx, ws *web.Socket, req by, sto store.I) (r by) {
var noAuth bo
for _, v := range s.noAuthAddresses {
if strings.HasPrefix(v, ws.RealRemote()) {
// we are not requiring auth from this address (should be private address)
noAuth = true
}
}
noAuth := s.checkNoAuth(ws)
if !noAuth && ws.AuthRequested() && len(ws.Authed()) == 0 {
return by("awaiting auth for req")
}
Expand All @@ -45,7 +37,7 @@ func (s *Server) handleReq(c cx, ws *web.Socket, req by, sto store.I) (r by) {
log.I.F("extra '%s'", rem)
}
allowed := env.Filters
if accepter, ok := s.relay.(relay.ReqAcceptor); ok {
if accepter, ok := s.relay.(relay.ReqAcceptor); ok && !noAuth {
var accepted bo
allowed, accepted = accepter.AcceptReq(c, ws.Req(), env.Subscription.T, env.Filters,
by(ws.Authed()))
Expand Down
50 changes: 50 additions & 0 deletions realy/noauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package realy

import (
"realy.lol/ec/schnorr"
"strings"
"realy.lol/web"
"realy.lol/hex"
)

// checkNoAuth examines whether the server has been configured to allow the
// client to have access based on their IP address, and optionally automatically
// stores their public key as the authed pubkey, thus automatically assuming that
// connections from this address are for a client using a given address.
//
// This is somewhat insecure, in that other apps that connect to this relay will
// be treated as though they are logged in with the provided key, meaning a
// malware or other malicious nostr-savvy app that knows the relay is connected
// to an address that is whitelisted, could then gain access to privileged
// messages. But this is only a small vulnerability, said attacker will not be
// able to sign for the key to fabricate events unless the signer is
// misconfigured.
func (s *Server) checkNoAuth(ws *web.Socket) (noAuth bo) {
if len(ws.Authed()) == 0 {
for _, v := range s.noAuthAddresses {
if strings.HasPrefix(v, ws.RealRemote()) {
// we are not requiring auth from this address (should be private address)
if strings.Contains(v, ":") {
// If there is a colon in the field then the second field should be a hex encoded
// public key. Anything else will be silently ignored.
split := strings.Split(v, ":")
if len(split) == 2 {
var k by
var err er
// check that the key is proper
if k, err = hex.Dec(split[1]); !chk.E(err) {
if len(k) == schnorr.PubKeyBytesLen {
log.I.S("whitelisted address has pubkey", split[1])
ws.SetAuthed(st(k))
noAuth = true
}
}
}
} else {
noAuth = true
}
}
}
}
return
}
2 changes: 1 addition & 1 deletion realy/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.3.6
v1.3.7
4 changes: 2 additions & 2 deletions web/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (ws *Socket) RequestAuth() { ws.authRequested.Store(true) }

func (ws *Socket) setRemoteFromReq(r *http.Request) {
var rr string
// reverse proxy should populate this field so we see the remote not the proxy
// reverse proxy should populate this field, so we see the remote not the proxy
rem := r.Header.Get("X-Forwarded-For")
if rem != "" {
splitted := strings.Split(rem, " ")
Expand All @@ -49,7 +49,7 @@ func (ws *Socket) setRemoteFromReq(r *http.Request) {
if len(splitted) == 2 {
rr = splitted[1]
}
// in case upstream doesn't set this or we are directly listening instead of
// in case upstream doesn't set this, or we are directly listening instead of
// via reverse proxy or just if the header field is missing, put the
// connection remote address into the websocket state data.
if rr == "" {
Expand Down

0 comments on commit 3916c45

Please sign in to comment.