From 1cac36f267f7c0953090c134b7afc0706ee080a8 Mon Sep 17 00:00:00 2001 From: terminaldweller Date: Thu, 1 May 2025 20:59:37 +0000 Subject: added the proxy protocol to the server --- docker-compose.yaml | 1 - go.mod | 4 +- go.sum | 2 + icanhazallips.go | 170 ------------------------------------------------ main.go | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 2 + 6 files changed, 190 insertions(+), 172 deletions(-) create mode 100644 go.sum delete mode 100644 icanhazallips.go create mode 100644 main.go diff --git a/docker-compose.yaml b/docker-compose.yaml index 15b75f7..22f5dd0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,3 @@ -version: "3" services: icanhazallips: image: icanhazallips diff --git a/go.mod b/go.mod index 6f0768c..e8954e0 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module icanhazallips -go 1.19 +go 1.24.0 + +require github.com/pires/go-proxyproto v0.8.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dcd5ca4 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0= +github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= diff --git a/icanhazallips.go b/icanhazallips.go deleted file mode 100644 index 1564ef4..0000000 --- a/icanhazallips.go +++ /dev/null @@ -1,170 +0,0 @@ -// https://gist.github.com/miguelmota/7b765edff00dc676215d6174f3f30216 -package main - -import ( - "errors" - "log" - "net" - "net/http" - "os" - "strconv" - "strings" - "time" -) - -const ( - defaultTimeOut = 10. -) - -var ( - errMalformedAdr = errors.New("malformed address") - errIPNotFound = errors.New("ip not found") - errBadFloatValue = errors.New("bad float value") - errBadConfig = errors.New("bad config") -) - -func getDefaultOptions() map[string]float64 { - return map[string]float64{ - "APP_CONTEXT_TIMEOUT": defaultTimeOut, - "APP_READ_HEADER_TIMEOUT": defaultTimeOut, - "APP_READ_TIMEOUT": defaultTimeOut, - "APP_WRITE_TIMEOUT": defaultTimeOut, - "APP_IDLE_TIMEOUT": defaultTimeOut, - } -} - -func getIP(request *http.Request) (string, error) { - ips := request.Header.Get("X-Forwarded-For") - splitIps := strings.Split(ips, ",") - - log.Println(request.RemoteAddr) - - if len(splitIps) > 0 { - netIP := net.ParseIP(splitIps[len(splitIps)-1]) - if netIP != nil { - return netIP.String(), nil - } - } - - ip, _, err := net.SplitHostPort(request.RemoteAddr) - if err != nil { - return "", errMalformedAdr - } - - if netIP := net.ParseIP(ip); netIP != nil { - ip := netIP.String() - if ip == "::1" { - return "127.0.0.1", nil - } - - return ip, nil - } - - return "", errIPNotFound -} - -func ipHandler(writer http.ResponseWriter, request *http.Request) { - ipAddr, err := getIP(request) - if err != nil { - writer.WriteHeader(http.StatusInternalServerError) - - return - } - - writer.WriteHeader(http.StatusOK) - - writtenLen, err := writer.Write([]byte(ipAddr)) - if err != nil || writtenLen != len(ipAddr) { - writer.WriteHeader(http.StatusInternalServerError) - - return - } -} - -type Config struct { - Addr string - ContextTimeOut float64 - ReadHeaderTimeout float64 - ReadTimeout float64 - WriteTimeout float64 - IdleTimeout float64 -} - -func getConfigValue(envVarName string) (float64, error) { - defaultOptions := getDefaultOptions() - - paramEnv := os.Getenv(envVarName) - if paramEnv != "" { - param, err := strconv.ParseFloat(paramEnv, 64) - if err != nil { - return defaultOptions[envVarName], errBadFloatValue - } - - return param, nil - } - - return defaultOptions[envVarName], nil -} - -func getConfig() (Config, error) { - var config Config - - var err error - - appAddrEnv := os.Getenv("APP_ADDR") - if appAddrEnv != "" { - config.Addr = appAddrEnv - } else { - config.Addr = ":8080" - } - - config.ContextTimeOut, err = getConfigValue("APP_CONTEXT_TIMEOUT") - if err != nil { - log.Println(err.Error()) - } - - config.ReadHeaderTimeout, err = getConfigValue("APP_READ_HEADER_TIMEOUT") - if err != nil { - log.Println(err.Error()) - } - - config.ReadTimeout, err = getConfigValue("APP_READ_TIMEOUT") - if err != nil { - log.Println(err.Error()) - } - - config.WriteTimeout, err = getConfigValue("APP_WRITE_TIMEOUT") - if err != nil { - log.Println(err.Error()) - } - - config.IdleTimeout, err = getConfigValue("APP_IDLE_TIMEOUT") - if err != nil { - log.Println(err.Error()) - } - - return config, err -} - -func main() { - log.SetOutput(os.Stdout) - http.HandleFunc("/", ipHandler) - - config, err := getConfig() - if err != nil { - log.Fatal(errBadConfig) - } - - server := http.Server{ - Addr: config.Addr, - ReadHeaderTimeout: time.Duration(config.ReadHeaderTimeout) * time.Second, - ReadTimeout: time.Duration(config.ReadTimeout) * time.Second, - WriteTimeout: time.Duration(config.WriteTimeout) * time.Second, - IdleTimeout: time.Duration(config.IdleTimeout) * time.Second, - TLSNextProto: nil, - ErrorLog: nil, - Handler: nil, - } - - log.Fatal(server.ListenAndServe()) -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..48222bf --- /dev/null +++ b/main.go @@ -0,0 +1,183 @@ +// https://gist.github.com/miguelmota/7b765edff00dc676215d6174f3f30216 +package main + +import ( + "errors" + "log" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/pires/go-proxyproto" +) + +const ( + defaultTimeOut = 10. +) + +var ( + errMalformedAdr = errors.New("malformed address") + errIPNotFound = errors.New("ip not found") + errBadFloatValue = errors.New("bad float value") + errBadConfig = errors.New("bad config") +) + +func getDefaultOptions() map[string]float64 { + return map[string]float64{ + "APP_CONTEXT_TIMEOUT": defaultTimeOut, + "APP_READ_HEADER_TIMEOUT": defaultTimeOut, + "APP_READ_TIMEOUT": defaultTimeOut, + "APP_WRITE_TIMEOUT": defaultTimeOut, + "APP_IDLE_TIMEOUT": defaultTimeOut, + } +} + +func getIP(request *http.Request) (string, error) { + ips := request.Header.Get("X-Forwarded-For") + splitIps := strings.Split(ips, ",") + + log.Println(request.RemoteAddr) + + if len(splitIps) > 0 { + netIP := net.ParseIP(splitIps[len(splitIps)-1]) + if netIP != nil { + return netIP.String(), nil + } + } + + ip, _, err := net.SplitHostPort(request.RemoteAddr) + if err != nil { + return "", errMalformedAdr + } + + if netIP := net.ParseIP(ip); netIP != nil { + ip := netIP.String() + if ip == "::1" { + return "127.0.0.1", nil + } + + return ip, nil + } + + return "", errIPNotFound +} + +func ipHandler(writer http.ResponseWriter, request *http.Request) { + ipAddr, err := getIP(request) + if err != nil { + writer.WriteHeader(http.StatusInternalServerError) + + return + } + + writer.WriteHeader(http.StatusOK) + + writtenLen, err := writer.Write([]byte(ipAddr)) + if err != nil || writtenLen != len(ipAddr) { + writer.WriteHeader(http.StatusInternalServerError) + + return + } +} + +type Config struct { + Addr string + ContextTimeOut float64 + ReadHeaderTimeout float64 + ReadTimeout float64 + WriteTimeout float64 + IdleTimeout float64 +} + +func getConfigValue(envVarName string) (float64, error) { + defaultOptions := getDefaultOptions() + + paramEnv := os.Getenv(envVarName) + if paramEnv != "" { + param, err := strconv.ParseFloat(paramEnv, 64) + if err != nil { + return defaultOptions[envVarName], errBadFloatValue + } + + return param, nil + } + + return defaultOptions[envVarName], nil +} + +func getConfig() (Config, error) { + var config Config + + var err error + + appAddrEnv := os.Getenv("APP_ADDR") + if appAddrEnv != "" { + config.Addr = appAddrEnv + } else { + config.Addr = ":8080" + } + + config.ContextTimeOut, err = getConfigValue("APP_CONTEXT_TIMEOUT") + if err != nil { + log.Println(err.Error()) + } + + config.ReadHeaderTimeout, err = getConfigValue("APP_READ_HEADER_TIMEOUT") + if err != nil { + log.Println(err.Error()) + } + + config.ReadTimeout, err = getConfigValue("APP_READ_TIMEOUT") + if err != nil { + log.Println(err.Error()) + } + + config.WriteTimeout, err = getConfigValue("APP_WRITE_TIMEOUT") + if err != nil { + log.Println(err.Error()) + } + + config.IdleTimeout, err = getConfigValue("APP_IDLE_TIMEOUT") + if err != nil { + log.Println(err.Error()) + } + + return config, err +} + +func main() { + log.SetOutput(os.Stdout) + http.HandleFunc("/", ipHandler) + + config, err := getConfig() + if err != nil { + log.Fatal(errBadConfig) + } + + server := http.Server{ + Addr: config.Addr, + ReadHeaderTimeout: time.Duration(config.ReadHeaderTimeout) * time.Second, + ReadTimeout: time.Duration(config.ReadTimeout) * time.Second, + WriteTimeout: time.Duration(config.WriteTimeout) * time.Second, + IdleTimeout: time.Duration(config.IdleTimeout) * time.Second, + TLSNextProto: nil, + ErrorLog: nil, + Handler: nil, + } + + ln, err := net.Listen("tcp", config.Addr) + if err != nil { + log.Fatal(err) + } + + proxyListener := &proxyproto.Listener{ + Listener: ln, + ReadHeaderTimeout: time.Duration(config.ReadHeaderTimeout) * time.Second, + } + defer proxyListener.Close() + + log.Fatal(server.Serve(proxyListener)) +} diff --git a/nginx.conf b/nginx.conf index 5a9c8a3..b917535 100644 --- a/nginx.conf +++ b/nginx.conf @@ -26,6 +26,8 @@ http { add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "no-referrer"; fastcgi_hide_header X-Powered-By; + proxy_set_header X-Real-IP $proxy_protocol_addr; + proxy_set_header X-Forwarded-For $proxy_protocol_addr; error_page 401 403 404 /404.html; location / { -- cgit v1.2.3