66 "encoding/base64"
77 "fmt"
88 "net/http"
9+ "net/netip"
910 "strings"
1011
1112 "github.com/gin-gonic/gin"
@@ -29,6 +30,31 @@ type APIKey struct {
2930 TlsAuth * TLSAuth
3031}
3132
33+ // baseBouncerName removes any trailing "@<ip>" segments from a bouncer's name.
34+ //
35+ // When a bouncer changes its IP address, it is detected by LAPI as a new bouncer,
36+ // to allow for key sharing. LAPI then creates a new DB entry by appending "@<ip>"
37+ // to the existing name. If the existing name already ends with "@<ip>", this can lead to
38+ // chained names like "[email protected] @5.6.7.8". To prevent runaway suffixes, 39+ // this helper repeatedly strips the final "@<ip>" token until no valid IPv4/IPv6
40+ // address remains, returning the "base" bouncer name.
41+ func baseBouncerName (name string ) string {
42+ for {
43+ i := strings .LastIndexByte (name , '@' )
44+ if i < 0 {
45+ return name
46+ }
47+
48+ tail := name [i + 1 :]
49+ if _ , err := netip .ParseAddr (tail ); err == nil {
50+ name = name [:i ]
51+ continue
52+ }
53+
54+ return name
55+ }
56+ }
57+
3258func GenerateAPIKey (n int ) (string , error ) {
3359 bytes := make ([]byte , n )
3460 if _ , err := rand .Read (bytes ); err != nil {
@@ -128,10 +154,12 @@ func (a *APIKey) authPlain(c *gin.Context, logger *log.Entry) *ent.Bouncer {
128154 logger .Errorf ("while fetching bouncer info: %s" , err )
129155 return nil
130156 }
157+
131158 if len (bouncer ) == 0 {
132159 logger .Debugf ("no bouncer found with this key" )
133160 return nil
134161 }
162+
135163 return bouncer [0 ]
136164 }
137165
@@ -148,6 +176,7 @@ func (a *APIKey) authPlain(c *gin.Context, logger *log.Entry) *ent.Bouncer {
148176 logger .Errorf ("bouncer isn't allowed to auth by API key" )
149177 return nil
150178 }
179+
151180 return bouncer
152181 }
153182
@@ -173,7 +202,8 @@ func (a *APIKey) authPlain(c *gin.Context, logger *log.Entry) *ent.Bouncer {
173202
174203 // Bouncers are ordered by ID, first one *should* be the manually created one
175204 // Can probably get a bit weird if the user deletes the manually created one
176- bouncerName := fmt .Sprintf ("%s@%s" , bouncers [0 ].Name , clientIP )
205+ base := baseBouncerName (bouncers [0 ].Name )
206+ bouncerName := fmt .Sprintf ("%s@%s" , base , clientIP )
177207
178208 logger .Infof ("Creating bouncer %s" , bouncerName )
179209
@@ -224,6 +254,7 @@ func (a *APIKey) MiddlewareFunc() gin.HandlerFunc {
224254 logger .Errorf ("Failed to update ip address for '%s': %s\n " , bouncer .Name , err )
225255 c .JSON (http .StatusForbidden , gin.H {"message" : "access forbidden" })
226256 c .Abort ()
257+
227258 return
228259 }
229260 }
0 commit comments