Public IP endpoint with GET /ip fixing #319

This commit is contained in:
Quentin McGaw
2020-12-27 21:06:00 +00:00
parent 2dc674559e
commit 82a02287ac
7 changed files with 92 additions and 5 deletions

View File

@@ -267,7 +267,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go
controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port) controlServerAddress := fmt.Sprintf("0.0.0.0:%d", allSettings.ControlServer.Port)
controlServerLogging := allSettings.ControlServer.Log controlServerLogging := allSettings.ControlServer.Log
httpServer := server.New(controlServerAddress, controlServerLogging, httpServer := server.New(controlServerAddress, controlServerLogging,
logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper) logger, buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
wg.Add(1) wg.Add(1)
go httpServer.Run(ctx, wg) go httpServer.Run(ctx, wg)

View File

@@ -2,6 +2,7 @@ package publicip
import ( import (
"context" "context"
"net"
"sync" "sync"
"time" "time"
@@ -18,6 +19,7 @@ type Looper interface {
Stop() Stop()
GetPeriod() (period time.Duration) GetPeriod() (period time.Duration)
SetPeriod(period time.Duration) SetPeriod(period time.Duration)
GetPublicIP() (publicIP net.IP)
} }
type looper struct { type looper struct {
@@ -26,6 +28,8 @@ type looper struct {
getter IPGetter getter IPGetter
logger logging.Logger logger logging.Logger
fileManager files.FileManager fileManager files.FileManager
ipMutex sync.RWMutex
ip net.IP
ipStatusFilepath models.Filepath ipStatusFilepath models.Filepath
uid int uid int
gid int gid int
@@ -115,6 +119,7 @@ func (l *looper) Run(ctx context.Context, wg *sync.WaitGroup) {
l.logAndWait(ctx, err) l.logAndWait(ctx, err)
continue continue
} }
l.setPublicIP(ip)
l.logger.Info("Public IP address is %s", ip) l.logger.Info("Public IP address is %s", ip)
const userReadWritePermissions = 0600 const userReadWritePermissions = 0600
err = l.fileManager.WriteLinesToFile( err = l.fileManager.WriteLinesToFile(

View File

@@ -0,0 +1,17 @@
package publicip
import "net"
func (l *looper) GetPublicIP() (publicIP net.IP) {
l.ipMutex.RLock()
defer l.ipMutex.RUnlock()
publicIP = make(net.IP, len(l.ip))
copy(publicIP, l.ip)
return publicIP
}
func (l *looper) setPublicIP(publicIP net.IP) {
l.ipMutex.Lock()
defer l.ipMutex.Unlock()
l.ip = publicIP
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/qdm12/gluetun/internal/dns" "github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/updater" "github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
@@ -16,15 +17,17 @@ func newHandler(logger logging.Logger, logging bool,
openvpnLooper openvpn.Looper, openvpnLooper openvpn.Looper,
unboundLooper dns.Looper, unboundLooper dns.Looper,
updaterLooper updater.Looper, updaterLooper updater.Looper,
publicIPLooper publicip.Looper,
) http.Handler { ) http.Handler {
handler := &handler{} handler := &handler{}
openvpn := newOpenvpnHandler(openvpnLooper, logger) openvpn := newOpenvpnHandler(openvpnLooper, logger)
dns := newDNSHandler(unboundLooper, logger) dns := newDNSHandler(unboundLooper, logger)
updater := newUpdaterHandler(updaterLooper, logger) updater := newUpdaterHandler(updaterLooper, logger)
publicip := newPublicIPHandler(publicIPLooper, logger)
handler.v0 = newHandlerV0(logger, openvpnLooper, unboundLooper, updaterLooper) handler.v0 = newHandlerV0(logger, openvpnLooper, unboundLooper, updaterLooper)
handler.v1 = newHandlerV1(logger, buildInfo, openvpn, dns, updater) handler.v1 = newHandlerV1(logger, buildInfo, openvpn, dns, updater, publicip)
handlerWithLog := withLogMiddleware(handler, logger, logging) handlerWithLog := withLogMiddleware(handler, logger, logging)
handler.setLogEnabled = handlerWithLog.setEnabled handler.setLogEnabled = handlerWithLog.setEnabled

View File

@@ -11,13 +11,14 @@ import (
) )
func newHandlerV1(logger logging.Logger, buildInfo models.BuildInformation, func newHandlerV1(logger logging.Logger, buildInfo models.BuildInformation,
openvpn, dns, updater http.Handler) http.Handler { openvpn, dns, updater, publicip http.Handler) http.Handler {
return &handlerV1{ return &handlerV1{
logger: logger, logger: logger,
buildInfo: buildInfo, buildInfo: buildInfo,
openvpn: openvpn, openvpn: openvpn,
dns: dns, dns: dns,
updater: updater, updater: updater,
publicip: publicip,
} }
} }
@@ -27,6 +28,7 @@ type handlerV1 struct {
openvpn http.Handler openvpn http.Handler
dns http.Handler dns http.Handler
updater http.Handler updater http.Handler
publicip http.Handler
} }
func (h *handlerV1) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *handlerV1) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -39,6 +41,8 @@ func (h *handlerV1) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.dns.ServeHTTP(w, r) h.dns.ServeHTTP(w, r)
case strings.HasPrefix(r.RequestURI, "/updater"): case strings.HasPrefix(r.RequestURI, "/updater"):
h.updater.ServeHTTP(w, r) h.updater.ServeHTTP(w, r)
case strings.HasPrefix(r.RequestURI, "/publicip"):
h.publicip.ServeHTTP(w, r)
default: default:
errString := fmt.Sprintf("%s %s not found", r.Method, r.RequestURI) errString := fmt.Sprintf("%s %s not found", r.Method, r.RequestURI)
http.Error(w, errString, http.StatusNotFound) http.Error(w, errString, http.StatusNotFound)

View File

@@ -0,0 +1,55 @@
//nolint:dupl
package server
import (
"encoding/json"
"net/http"
"strings"
"github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/golibs/logging"
)
func newPublicIPHandler(
looper publicip.Looper,
logger logging.Logger) http.Handler {
return &publicIPHandler{
looper: looper,
logger: logger,
}
}
type publicIPHandler struct {
looper publicip.Looper
logger logging.Logger
}
func (h *publicIPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.RequestURI = strings.TrimPrefix(r.RequestURI, "/publicip")
switch r.RequestURI {
case "/ip":
switch r.Method {
case http.MethodGet:
h.getPublicIP(w)
default:
http.Error(w, "", http.StatusNotFound)
}
default:
http.Error(w, "", http.StatusNotFound)
}
}
type publicIPWrapper struct {
PublicIP string `json:"public_ip"`
}
func (h *publicIPHandler) getPublicIP(w http.ResponseWriter) {
publicIP := h.looper.GetPublicIP()
encoder := json.NewEncoder(w)
data := publicIPWrapper{PublicIP: publicIP.String()}
if err := encoder.Encode(data); err != nil {
h.logger.Warn(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/qdm12/gluetun/internal/dns" "github.com/qdm12/gluetun/internal/dns"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/updater" "github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
@@ -25,9 +26,11 @@ type server struct {
func New(address string, logging bool, logger logging.Logger, func New(address string, logging bool, logger logging.Logger,
buildInfo models.BuildInformation, buildInfo models.BuildInformation,
openvpnLooper openvpn.Looper, unboundLooper dns.Looper, updaterLooper updater.Looper) Server { openvpnLooper openvpn.Looper, unboundLooper dns.Looper,
updaterLooper updater.Looper, publicIPLooper publicip.Looper) Server {
serverLogger := logger.WithPrefix("http server: ") serverLogger := logger.WithPrefix("http server: ")
handler := newHandler(serverLogger, logging, buildInfo, openvpnLooper, unboundLooper, updaterLooper) handler := newHandler(serverLogger, logging, buildInfo,
openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
return &server{ return &server{
address: address, address: address,
logger: serverLogger, logger: serverLogger,