mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-10 22:38:10 +00:00
147 lines
3.0 KiB
Go
147 lines
3.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
|
"tailscale.com/client/local"
|
|
"tailscale.com/tsnet"
|
|
)
|
|
|
|
type TailscaleService struct {
|
|
log *logger.Logger
|
|
wg *sync.WaitGroup
|
|
config model.Config
|
|
ctx context.Context
|
|
|
|
srv *tsnet.Server
|
|
lc *local.Client
|
|
ln *net.Listener
|
|
}
|
|
|
|
func NewTailscaleService(log *logger.Logger, config model.Config, ctx context.Context, wg *sync.WaitGroup) (*TailscaleService, error) {
|
|
srv := new(tsnet.Server)
|
|
|
|
// node options
|
|
srv.Dir = config.Tailscale.Dir
|
|
srv.Hostname = config.Tailscale.Hostname
|
|
srv.AuthKey = config.Tailscale.AuthKey
|
|
srv.Ephemeral = config.Tailscale.Ephemeral
|
|
|
|
// redirect logs to zerolog
|
|
srv.Logf = log.App.Printf
|
|
srv.UserLogf = log.App.Printf
|
|
|
|
err := srv.Start()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to start tailscale server: %w", err)
|
|
}
|
|
|
|
lc, err := srv.LocalClient()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get tailscale local client: %w", err)
|
|
}
|
|
|
|
service := &TailscaleService{
|
|
log: log,
|
|
wg: wg,
|
|
config: config,
|
|
ctx: ctx,
|
|
srv: srv,
|
|
lc: lc,
|
|
}
|
|
|
|
connectCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
|
defer cancel()
|
|
|
|
err = service.waitForConn(connectCtx)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to connect to tailscale network: %w", err)
|
|
}
|
|
|
|
wg.Go(service.watchAndClose)
|
|
|
|
return service, nil
|
|
}
|
|
|
|
func (ts *TailscaleService) watchAndClose() {
|
|
<-ts.ctx.Done()
|
|
ts.log.App.Debug().Msg("Shutting down Tailscale service")
|
|
if ts.ln != nil {
|
|
(*ts.ln).Close()
|
|
}
|
|
if ts.srv != nil {
|
|
ts.srv.Close()
|
|
}
|
|
}
|
|
|
|
func (ts *TailscaleService) Whois(ctx context.Context, addr string) (*model.TailscaleWhoisResponse, error) {
|
|
who, err := ts.lc.WhoIs(ctx, addr)
|
|
|
|
if err != nil {
|
|
if errors.Is(err, local.ErrPeerNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("failed to get client whois: %w", err)
|
|
}
|
|
|
|
res := model.TailscaleWhoisResponse{
|
|
UserID: who.UserProfile.ID.String(),
|
|
LoginName: who.UserProfile.LoginName,
|
|
DisplayName: who.UserProfile.DisplayName,
|
|
NodeName: strings.TrimSuffix(who.Node.Name, "."),
|
|
Tags: who.Node.Tags,
|
|
}
|
|
|
|
return &res, nil
|
|
}
|
|
|
|
func (ts *TailscaleService) CreateListener() (net.Listener, error) {
|
|
if ts.ln != nil {
|
|
return *ts.ln, nil
|
|
}
|
|
ln, err := ts.srv.ListenTLS("tcp", ":443")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ts.ln = &ln
|
|
return ln, nil
|
|
}
|
|
|
|
func (ts *TailscaleService) GetHostname() string {
|
|
status, err := ts.lc.Status(ts.ctx)
|
|
|
|
if err != nil {
|
|
ts.log.App.Error().Err(err).Msg("Failed to get Tailscale status")
|
|
return ""
|
|
}
|
|
|
|
return strings.TrimSuffix(status.Self.DNSName, ".")
|
|
}
|
|
|
|
func (ts *TailscaleService) waitForConn(ctx context.Context) error {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("timed out waiting for tailscale connection")
|
|
default:
|
|
ip4, _ := ts.srv.TailscaleIPs()
|
|
if !ip4.IsValid() {
|
|
time.Sleep(1 * time.Second)
|
|
continue
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|