mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-03-13 02:02:04 +00:00
wip
This commit is contained in:
@@ -45,6 +45,7 @@ func (app *BootstrapApp) Setup() error {
|
||||
if app.config.Auth.SessionMaxLifetime != 0 && app.config.Auth.SessionMaxLifetime < app.config.Auth.SessionExpiry {
|
||||
return fmt.Errorf("session max lifetime cannot be less than session expiry")
|
||||
}
|
||||
|
||||
// Parse users
|
||||
users, err := utils.GetUsers(app.config.Auth.Users, app.config.Auth.UsersFile)
|
||||
|
||||
@@ -203,6 +204,19 @@ func (app *BootstrapApp) Setup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we have tailscale configured, we will listen on it
|
||||
if app.services.tailscaleService.IsConfigured() {
|
||||
ln, err := app.services.tailscaleService.CreateListener()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create listener: %w", err)
|
||||
}
|
||||
|
||||
if err := router.RunListener(ln); err != nil {
|
||||
tlog.App.Fatal().Err(err).Msg("Failed to start tailscale listener")
|
||||
}
|
||||
}
|
||||
|
||||
// Start server
|
||||
address := fmt.Sprintf("%s:%d", app.config.Server.Address, app.config.Server.Port)
|
||||
tlog.App.Info().Msgf("Starting server on %s", address)
|
||||
|
||||
@@ -12,6 +12,7 @@ type Services struct {
|
||||
dockerService *service.DockerService
|
||||
ldapService *service.LdapService
|
||||
oauthBrokerService *service.OAuthBrokerService
|
||||
tailscaleService *service.TailscaleService
|
||||
}
|
||||
|
||||
func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, error) {
|
||||
@@ -36,6 +37,19 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er
|
||||
tlog.App.Warn().Err(err).Msg("Failed to initialize LDAP service, continuing without it")
|
||||
}
|
||||
|
||||
tailscaleService := service.NewTailscaleService(service.TailscaleServiceConfig{
|
||||
AuthKey: app.config.Tailscale.AuthKey,
|
||||
Hostname: app.config.Tailscale.Hostname,
|
||||
})
|
||||
|
||||
err = tailscaleService.Init()
|
||||
|
||||
if err != nil {
|
||||
return Services{}, err
|
||||
}
|
||||
|
||||
services.tailscaleService = tailscaleService
|
||||
|
||||
dockerService := service.NewDockerService()
|
||||
|
||||
err = dockerService.Init()
|
||||
@@ -68,7 +82,7 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er
|
||||
SessionCookieName: app.context.sessionCookieName,
|
||||
IP: app.config.Auth.IP,
|
||||
LDAPGroupsCacheTTL: app.config.Ldap.GroupCacheTTL,
|
||||
}, dockerService, services.ldapService, queries)
|
||||
}, dockerService, services.ldapService, services.tailscaleService, queries)
|
||||
|
||||
err = authService.Init()
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ type Config struct {
|
||||
Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"`
|
||||
Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"`
|
||||
Log LogConfig `description:"Logging configuration." yaml:"log"`
|
||||
Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -84,6 +85,11 @@ type LogConfig struct {
|
||||
Streams LogStreams `description:"Configuration for specific log streams." yaml:"streams"`
|
||||
}
|
||||
|
||||
type TailscaleConfig struct {
|
||||
AuthKey string `description:"Tailscale authentication key." yaml:"authKey"`
|
||||
Hostname string `description:"Hostname for Tailscale." yaml:"hostname"`
|
||||
}
|
||||
|
||||
type LogStreams struct {
|
||||
HTTP LogStreamConfig `description:"HTTP request logging." yaml:"http"`
|
||||
App LogStreamConfig `description:"Application logging." yaml:"app"`
|
||||
|
||||
@@ -94,6 +94,17 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
||||
proto := c.Request.Header.Get("X-Forwarded-Proto")
|
||||
host := c.Request.Header.Get("X-Forwarded-Host")
|
||||
|
||||
// Is it from tailscale?
|
||||
tailscaleWho, err := controller.auth.IsTailscale(c)
|
||||
|
||||
if err != nil {
|
||||
tlog.App.Error().Err(err).Msg("Failed to get tailscale whois")
|
||||
controller.handleError(c, req, isBrowser)
|
||||
return
|
||||
}
|
||||
|
||||
tlog.App.Debug().Interface("who", tailscaleWho).Msg("Yoyo are we from tailscale")
|
||||
|
||||
// Get acls
|
||||
acls, err := controller.acls.GetAccessControls(host)
|
||||
|
||||
|
||||
@@ -37,6 +37,25 @@ func (m *ContextMiddleware) Init() error {
|
||||
|
||||
func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Before checking the cookie, let's check tailscale
|
||||
who, err := m.auth.IsTailscale(c)
|
||||
|
||||
if err != nil {
|
||||
tlog.App.Warn().Err(err).Msg("Failed to check Tailscale, checking other auth sources")
|
||||
}
|
||||
|
||||
if who != nil {
|
||||
c.Set("context", &config.UserContext{
|
||||
Username: who.Node.Name,
|
||||
Name: who.Node.ComputedName,
|
||||
Email: strings.Replace(who.Node.Name, ".", "@", 1),
|
||||
Provider: "tailscale",
|
||||
IsLoggedIn: true,
|
||||
})
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
cookie, err := m.auth.GetSessionCookie(c)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/steveiliop56/tinyauth/internal/repository"
|
||||
"github.com/steveiliop56/tinyauth/internal/utils"
|
||||
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
|
||||
tsLocal "tailscale.com/client/local"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
@@ -52,16 +54,18 @@ type AuthService struct {
|
||||
loginMutex sync.RWMutex
|
||||
ldapGroupsMutex sync.RWMutex
|
||||
ldap *LdapService
|
||||
tailscale *TailscaleService
|
||||
queries *repository.Queries
|
||||
}
|
||||
|
||||
func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapService, queries *repository.Queries) *AuthService {
|
||||
func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapService, tailscale *TailscaleService, queries *repository.Queries) *AuthService {
|
||||
return &AuthService{
|
||||
config: config,
|
||||
docker: docker,
|
||||
loginAttempts: make(map[string]*LoginAttempt),
|
||||
ldapGroupsCache: make(map[string]*LdapGroupsCache),
|
||||
ldap: ldap,
|
||||
tailscale: tailscale,
|
||||
queries: queries,
|
||||
}
|
||||
}
|
||||
@@ -553,3 +557,20 @@ func (auth *AuthService) IsBypassedIP(acls config.AppIP, ip string) bool {
|
||||
tlog.App.Debug().Str("ip", ip).Msg("IP not in bypass list, continuing with authentication")
|
||||
return false
|
||||
}
|
||||
|
||||
func (auth *AuthService) IsTailscale(c *gin.Context) (*apitype.WhoIsResponse, error) {
|
||||
if !auth.tailscale.IsConfigured() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
who, err := auth.tailscale.Whois(c, c.Request.RemoteAddr)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, tsLocal.ErrPeerNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return who, nil
|
||||
}
|
||||
|
||||
83
internal/service/tailscale_service.go
Normal file
83
internal/service/tailscale_service.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
|
||||
"tailscale.com/client/local"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/tsnet"
|
||||
)
|
||||
|
||||
var ErrNotConfigured = errors.New("tailscale service is not configured")
|
||||
|
||||
type TailscaleServiceConfig struct {
|
||||
AuthKey string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
type TailscaleService struct {
|
||||
config TailscaleServiceConfig
|
||||
server *tsnet.Server
|
||||
client *local.Client
|
||||
}
|
||||
|
||||
func NewTailscaleService(config TailscaleServiceConfig) *TailscaleService {
|
||||
return &TailscaleService{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (service *TailscaleService) Init() error {
|
||||
if !service.IsConfigured() {
|
||||
return nil
|
||||
}
|
||||
|
||||
server := new(tsnet.Server)
|
||||
|
||||
if service.config.AuthKey == "" {
|
||||
tlog.App.Info().Msg("Auth key is empty but Tailscale is configured, a login link will appear below so as you can authenticate")
|
||||
}
|
||||
|
||||
server.Hostname = service.config.Hostname
|
||||
server.AuthKey = service.config.AuthKey
|
||||
|
||||
client, err := server.LocalClient()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.client = client
|
||||
service.server = server
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *TailscaleService) IsConfigured() bool {
|
||||
return service.config.Hostname != ""
|
||||
}
|
||||
|
||||
func (service *TailscaleService) Whois(ctx context.Context, remoteAddr string) (*apitype.WhoIsResponse, error) {
|
||||
if !service.IsConfigured() {
|
||||
return nil, ErrNotConfigured
|
||||
}
|
||||
|
||||
return service.client.WhoIs(ctx, remoteAddr)
|
||||
}
|
||||
|
||||
func (service *TailscaleService) CreateListener() (net.Listener, error) {
|
||||
if !service.IsConfigured() {
|
||||
return nil, ErrNotConfigured
|
||||
}
|
||||
|
||||
ln, err := service.server.ListenTLS("tcp", ":443")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
Reference in New Issue
Block a user