package admin import ( "encoding/json" "fmt" "net" "net/http" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" ) type TailscaleAuth struct { allowedUsers []string } type tailscaleWhoisResponse struct { UserProfile struct { LoginName string `json:"LoginName"` } `json:"UserProfile"` } func (t *TailscaleAuth) Middleware(c *gin.Context) { if len(t.allowedUsers) == 0 { // No allowed users configured - allow all (dev mode) c.Next() return } remoteAddr := c.Request.RemoteAddr host, _, err := net.SplitHostPort(remoteAddr) if err != nil { host = remoteAddr } whoisURL := fmt.Sprintf("http://100.100.100.100/localapi/v0/whois?addr=%s", host) resp, err := http.Get(whoisURL) if err != nil { log.Error().Err(err).Msg("tailscale whois error") c.String(http.StatusUnauthorized, "Unauthorized") c.Abort() return } defer resp.Body.Close() var whois tailscaleWhoisResponse if err := json.NewDecoder(resp.Body).Decode(&whois); err != nil { log.Error().Err(err).Msg("tailscale whois decode error") c.String(http.StatusUnauthorized, "Unauthorized") c.Abort() return } loginName := whois.UserProfile.LoginName allowed := false for _, u := range t.allowedUsers { if u == loginName { allowed = true break } } if !allowed { log.Error().Msgf("tailscale auth: user %q not in allowed list", loginName) c.String(http.StatusForbidden, "Forbidden") c.Abort() return } c.Next() }