From 70178b511a2284b343d9efe5a367de43779580fa Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:55:37 +0200 Subject: [PATCH 01/16] Add Trusted Proxies parameter Missing webui management --- client/src/pages/config/users/configman.jsx | 3 +++ src/proxy/shield.go | 5 +++-- src/utils/types.go | 1 + src/utils/utils.go | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index 4b532c19..186625ff 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -106,6 +106,7 @@ const ConfigManagement = () => { GenerateMissingAuthCert: config.HTTPConfig.GenerateMissingAuthCert, HTTPPort: config.HTTPConfig.HTTPPort, HTTPSPort: config.HTTPConfig.HTTPSPort, + TrustedProxies: config.TrustedProxies && config.TrustedProxies.join(', '), SSLEmail: config.HTTPConfig.SSLEmail, UseWildcardCertificate: config.HTTPConfig.UseWildcardCertificate, HTTPSCertificateMode: config.HTTPConfig.HTTPSCertificateMode, @@ -205,6 +206,8 @@ const ConfigManagement = () => { AllowSearchEngine: values.AllowSearchEngine, AllowHTTPLocalIPAccess: values.AllowHTTPLocalIPAccess, PublishMDNS: values.PublishMDNS, + TrustedProxies: (values.TrustedProxies && values.TrustedProxies != "") ? + values.TrustedProxies.split(',').map((x) => x.trim()) : [], }, EmailConfig: { ...config.EmailConfig, diff --git a/src/proxy/shield.go b/src/proxy/shield.go index c4bdfffa..5efaf209 100644 --- a/src/proxy/shield.go +++ b/src/proxy/shield.go @@ -7,6 +7,7 @@ import ( "fmt" "math" "strconv" + "strings" "github.com/azukaar/cosmos-server/src/utils" "github.com/azukaar/cosmos-server/src/metrics" @@ -288,9 +289,9 @@ func GetClientID(r *http.Request, route utils.ProxyRouteConfig) string { isConstIP := utils.IsConstellationIP(remoteAddr) isConstTokenValid := constellation.CheckConstellationToken(r) == nil - if (UseForwardedFor && r.Header.Get("x-forwarded-for") != "") || + if ((UseForwardedFor || utils.IsTrustedProxy(remoteAddr) ) && r.Header.Get("x-forwarded-for") != "") || (isTunneledIp && isConstIP && isConstTokenValid) { - ip, _ := utils.SplitIP(r.Header.Get("x-forwarded-for")) + ip := strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0] utils.Debug("SmartShield: Getting forwarded client ID " + ip) return ip } else { diff --git a/src/utils/types.go b/src/utils/types.go index 7c173b47..06e062da 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -175,6 +175,7 @@ type HTTPConfig struct { UseForwardedFor bool AllowSearchEngine bool PublishMDNS bool + TrustedProxies []string } const ( diff --git a/src/utils/utils.go b/src/utils/utils.go index d77f03cf..49e876e7 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -891,6 +891,25 @@ func IsConstellationIP(ip string) bool { return false } +func IsTrustedProxy(ip string) bool { + ipAddr := osnet.ParseIP(ip) + for _, trustedProxy := range GetMainConfig().HTTPConfig.TrustedProxies { + _, cidr, err := osnet.ParseCIDR(trustedProxy) + if err != nil { + // If it's not a CIDR, check for exact match + if ip == trustedProxy { + return true + } + } else { + // Check if the IP is in the CIDR range + if cidr.Contains(ipAddr) { + return true + } + } + } + return false +} + func SplitIP(ipPort string) (string, string) { host, port, err := osnet.SplitHostPort(ipPort) if err != nil { From 03b7db34abfed37da328e507f800a20ad3ddc555 Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:03:25 +0200 Subject: [PATCH 02/16] Add WebUI management --- client/src/pages/config/users/configman.jsx | 13 ++++++++++++- .../locales/en-FUNNYSHAKESPEARE/translation.json | 2 ++ client/src/utils/locales/fr/translation.json | 2 ++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index 186625ff..c7cbadf7 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -106,7 +106,7 @@ const ConfigManagement = () => { GenerateMissingAuthCert: config.HTTPConfig.GenerateMissingAuthCert, HTTPPort: config.HTTPConfig.HTTPPort, HTTPSPort: config.HTTPConfig.HTTPSPort, - TrustedProxies: config.TrustedProxies && config.TrustedProxies.join(', '), + TrustedProxies: config.HTTPConfig.TrustedProxies && config.HTTPConfig.TrustedProxies.join(', '), SSLEmail: config.HTTPConfig.SSLEmail, UseWildcardCertificate: config.HTTPConfig.UseWildcardCertificate, HTTPSCertificateMode: config.HTTPConfig.HTTPSCertificateMode, @@ -618,6 +618,17 @@ const ConfigManagement = () => { )} + + + + + + {t('mgmt.config.http.allowSearchIndexCheckbox')}
diff --git a/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json b/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json index 6e8463f9..87ff024d 100644 --- a/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json +++ b/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json @@ -174,6 +174,8 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Hostname: This shall be used to restrict access to thy Cosmos Server (Thy IP, or domain name)", "mgmt.config.http.hostnameInput.HostnameValidation": "Hostname is utterly required", "mgmt.config.http.publishMDNSCheckbox": "This shall allow thee to publish thy server on thine local network using mDNS. So all thy .local domains will be available upon thine local network with no extra config.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from an IP/IP range.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IP or IP range separated by commas", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notifyeth Users upon Successful Login", "mgmt.config.proxy.noRoutesConfiguredText": "No routes have been configured, alas.", "mgmt.config.proxy.originTitle": "Origin from whence thou comest", diff --git a/client/src/utils/locales/fr/translation.json b/client/src/utils/locales/fr/translation.json index 691859fb..d627e2d6 100644 --- a/client/src/utils/locales/fr/translation.json +++ b/client/src/utils/locales/fr/translation.json @@ -174,6 +174,8 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Nom d'hôte : Cela sera utilisé pour restreindre l'accès à votre serveur Cosmos (Votre IP, ou votre nom de domaine)", "mgmt.config.http.hostnameInput.HostnameValidation": "Le nom d'hôte est obligatoire", "mgmt.config.http.publishMDNSCheckbox": "Cela vous permet de publier votre serveur sur votre réseau local en utilisant mDNS. Cela signifie que tous vos domaines .local seront disponibles sur votre réseau local sans configuration supplémentaire.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Proxy de confiance pour l'utilisation de l'entête X-Forwarded-For.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Utilisez ce paramètre lorsque vous avez un serveur proxy en amont pour éviter le blocage de celui-ci par le Shield. IP ou plage IP séparées par des virgules", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notifier les utilisateurs en cas de connexion réussie", "mgmt.config.proxy.noRoutesConfiguredText": "Aucune route configurée.", "mgmt.config.proxy.originTitle": "Origine", From 345f803224210c80bce6a2f4520e38b76416203c Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:10:19 +0200 Subject: [PATCH 03/16] Update translation.json --- client/src/utils/locales/en/translation.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/utils/locales/en/translation.json b/client/src/utils/locales/en/translation.json index e7329f38..606d0e6d 100644 --- a/client/src/utils/locales/en/translation.json +++ b/client/src/utils/locales/en/translation.json @@ -176,6 +176,8 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Hostname: This will be used to restrict access to your Cosmos Server (Your IP, or your domain name)", "mgmt.config.http.hostnameInput.HostnameValidation": "Hostname is required", "mgmt.config.http.publishMDNSCheckbox": "This allows you to publish your server on your local network using mDNS. This means all your .local domains will be available on your local network with no additional config.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from an IP/IP range.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IP or IP range separated by commas", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notify Users upon Successful Login", "mgmt.config.proxy.noRoutesConfiguredText": "No routes configured.", "mgmt.config.proxy.originTitle": "Origin", From 903434f4510555ca67a035bba6de8a9cf0b032ed Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:12:17 +0200 Subject: [PATCH 04/16] revert en-FUNNYSHAKESPEARE --- client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json b/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json index 87ff024d..6e8463f9 100644 --- a/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json +++ b/client/src/utils/locales/en-FUNNYSHAKESPEARE/translation.json @@ -174,8 +174,6 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Hostname: This shall be used to restrict access to thy Cosmos Server (Thy IP, or domain name)", "mgmt.config.http.hostnameInput.HostnameValidation": "Hostname is utterly required", "mgmt.config.http.publishMDNSCheckbox": "This shall allow thee to publish thy server on thine local network using mDNS. So all thy .local domains will be available upon thine local network with no extra config.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from an IP/IP range.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IP or IP range separated by commas", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notifyeth Users upon Successful Login", "mgmt.config.proxy.noRoutesConfiguredText": "No routes have been configured, alas.", "mgmt.config.proxy.originTitle": "Origin from whence thou comest", From 7755afcebc66424a479275c8a1eca13bf8505de4 Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:21:52 +0200 Subject: [PATCH 05/16] Edit TrustesProxiesLabel --- client/src/utils/locales/en/translation.json | 2 +- client/src/utils/locales/fr/translation.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/utils/locales/en/translation.json b/client/src/utils/locales/en/translation.json index 606d0e6d..df6c58aa 100644 --- a/client/src/utils/locales/en/translation.json +++ b/client/src/utils/locales/en/translation.json @@ -176,7 +176,7 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Hostname: This will be used to restrict access to your Cosmos Server (Your IP, or your domain name)", "mgmt.config.http.hostnameInput.HostnameValidation": "Hostname is required", "mgmt.config.http.publishMDNSCheckbox": "This allows you to publish your server on your local network using mDNS. This means all your .local domains will be available on your local network with no additional config.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from an IP/IP range.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from IP/IP range (separated by comma).", "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IP or IP range separated by commas", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notify Users upon Successful Login", "mgmt.config.proxy.noRoutesConfiguredText": "No routes configured.", diff --git a/client/src/utils/locales/fr/translation.json b/client/src/utils/locales/fr/translation.json index d627e2d6..9cfaaf88 100644 --- a/client/src/utils/locales/fr/translation.json +++ b/client/src/utils/locales/fr/translation.json @@ -174,7 +174,7 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Nom d'hôte : Cela sera utilisé pour restreindre l'accès à votre serveur Cosmos (Votre IP, ou votre nom de domaine)", "mgmt.config.http.hostnameInput.HostnameValidation": "Le nom d'hôte est obligatoire", "mgmt.config.http.publishMDNSCheckbox": "Cela vous permet de publier votre serveur sur votre réseau local en utilisant mDNS. Cela signifie que tous vos domaines .local seront disponibles sur votre réseau local sans configuration supplémentaire.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Proxy de confiance pour l'utilisation de l'entête X-Forwarded-For.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "IP/Plage IP des proxys de confiance (séparé par des virgules) pour l'utilisation de X-Forwarded-For.", "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Utilisez ce paramètre lorsque vous avez un serveur proxy en amont pour éviter le blocage de celui-ci par le Shield. IP ou plage IP séparées par des virgules", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notifier les utilisateurs en cas de connexion réussie", "mgmt.config.proxy.noRoutesConfiguredText": "Aucune route configurée.", From 11d41ca0815fb861b0ee0aac5ebfa08d98296422 Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:38:30 +0200 Subject: [PATCH 06/16] Fix Helpertext message --- client/src/pages/config/users/configman.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index c7cbadf7..be9f84d3 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -621,11 +621,13 @@ const ConfigManagement = () => { + + {t('mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText')}
+
From 12e0dc6287604df67fde261d6f84f88c1fde82f7 Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:38:49 +0200 Subject: [PATCH 07/16] Fix translation --- client/src/utils/locales/en/translation.json | 4 ++-- client/src/utils/locales/fr/translation.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/utils/locales/en/translation.json b/client/src/utils/locales/en/translation.json index df6c58aa..6df4da74 100644 --- a/client/src/utils/locales/en/translation.json +++ b/client/src/utils/locales/en/translation.json @@ -176,8 +176,8 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Hostname: This will be used to restrict access to your Cosmos Server (Your IP, or your domain name)", "mgmt.config.http.hostnameInput.HostnameValidation": "Hostname is required", "mgmt.config.http.publishMDNSCheckbox": "This allows you to publish your server on your local network using mDNS. This means all your .local domains will be available on your local network with no additional config.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from IP/IP range (separated by comma).", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IP or IP range separated by commas", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "Trusted proxies allow X-Forwarded-For from IP/IP range.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Use this setting when you have an upstream proxy server to avoid it being blocked by Shield. IPs or IP ranges separated by commas.", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notify Users upon Successful Login", "mgmt.config.proxy.noRoutesConfiguredText": "No routes configured.", "mgmt.config.proxy.originTitle": "Origin", diff --git a/client/src/utils/locales/fr/translation.json b/client/src/utils/locales/fr/translation.json index 9cfaaf88..598063dc 100644 --- a/client/src/utils/locales/fr/translation.json +++ b/client/src/utils/locales/fr/translation.json @@ -174,8 +174,8 @@ "mgmt.config.http.hostnameInput.HostnameLabel": "Nom d'hôte : Cela sera utilisé pour restreindre l'accès à votre serveur Cosmos (Votre IP, ou votre nom de domaine)", "mgmt.config.http.hostnameInput.HostnameValidation": "Le nom d'hôte est obligatoire", "mgmt.config.http.publishMDNSCheckbox": "Cela vous permet de publier votre serveur sur votre réseau local en utilisant mDNS. Cela signifie que tous vos domaines .local seront disponibles sur votre réseau local sans configuration supplémentaire.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "IP/Plage IP des proxys de confiance (séparé par des virgules) pour l'utilisation de X-Forwarded-For.", - "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Utilisez ce paramètre lorsque vous avez un serveur proxy en amont pour éviter le blocage de celui-ci par le Shield. IP ou plage IP séparées par des virgules", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesLabel": "IPs/Plages IP des proxys de confiance pour l'utilisation de X-Forwarded-For.", + "mgmt.config.http.TrustedProxiesInput.TrustesProxiesHelperText": "Utilisez ce paramètre lorsque vous avez un serveur proxy en amont pour éviter le blocage de celui-ci par le Shield. IPs ou plages IP séparées par des virgules.", "mgmt.config.email.notifyLoginCheckbox.notifyLoginLabel": "Notifier les utilisateurs en cas de connexion réussie", "mgmt.config.proxy.noRoutesConfiguredText": "Aucune route configurée.", "mgmt.config.proxy.originTitle": "Origine", From 5c63102b4c03372595aaf853a9c428ebcecf0a6e Mon Sep 17 00:00:00 2001 From: Someone <10882916+InterN0te@users.noreply.github.com> Date: Mon, 14 Oct 2024 18:15:18 +0200 Subject: [PATCH 08/16] Apply Real IP in BannedIPs and Blockers Added the real client IP as ClientID in Context and used this for IP abuse count/block/ban --- src/httpServer.go | 2 ++ src/proxy/shield.go | 2 +- src/utils/middleware.go | 72 +++++++++++++++++++++++++---------------- src/utils/utils.go | 17 +++++++--- 4 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/httpServer.go b/src/httpServer.go index 22d21b96..a1901ce7 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -359,6 +359,8 @@ func InitServer() *mux.Router { router := mux.NewRouter().StrictSlash(true) + router.Use(utils.ClientRealIP) + router.Use(utils.BlockBannedIPs) router.Use(utils.Logger) diff --git a/src/proxy/shield.go b/src/proxy/shield.go index 81cb680b..1ab3489d 100644 --- a/src/proxy/shield.go +++ b/src/proxy/shield.go @@ -304,7 +304,7 @@ func GetClientID(r *http.Request, route utils.ProxyRouteConfig) string { if ((UseForwardedFor || utils.IsTrustedProxy(remoteAddr) ) && r.Header.Get("x-forwarded-for") != "") || (isTunneledIp && isConstIP && isConstTokenValid) { - ip := strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0] + ip := strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]) utils.Debug("SmartShield: Getting forwarded client ID " + ip) return ip } else { diff --git a/src/utils/middleware.go b/src/utils/middleware.go index 81c0c44c..ff8b8967 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -48,34 +48,51 @@ func getIPAbuseCounter(ip string) int64 { return atomic.LoadInt64(&counter.val) } +func ClientRealIP(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + clientID := GetClientIP(r) + if(clientID == ""){ + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } else { + Debug("Add ClientID in context : " + clientID) + } + + ctx := context.WithValue(r.Context(), "ClientID", clientID) + r = r.WithContext(ctx) + + next.ServeHTTP(w, r) + }) +} + func BlockBannedIPs(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - if hj, ok := w.(http.Hijacker); ok { - conn, _, err := hj.Hijack() - if err == nil { - conn.Close() - } - } - return + ip, ok := r.Context().Value("ClientID").(string) + if !ok { + if hj, ok := w.(http.Hijacker); ok { + conn, _, err := hj.Hijack() + if err == nil { + conn.Close() + } + } + return } - nbAbuse := getIPAbuseCounter(ip) + nbAbuse := getIPAbuseCounter(ip) if nbAbuse > 275 { - Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") - } + Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") + } if nbAbuse > 300 { - if hj, ok := w.(http.Hijacker); ok { - conn, _, err := hj.Hijack() - if err == nil { - conn.Close() - } - } - return + if hj, ok := w.(http.Hijacker); ok { + conn, _, err := hj.Hijack() + if err == nil { + conn.Close() } + } + return + } next.ServeHTTP(w, r) }) @@ -204,8 +221,8 @@ func GetIPLocation(ip string) (string, error) { func BlockByCountryMiddleware(blockedCountries []string, CountryBlacklistIsWhitelist bool) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { + ip, ok := r.Context().Value("ClientID").(string) + if !ok { http.Error(w, "Invalid request", http.StatusBadRequest) return } @@ -287,7 +304,7 @@ func BlockPostWithoutReferer(next http.Handler) http.Handler { Error("Blocked POST request without Referer header", nil) http.Error(w, "Bad Request: Invalid request.", http.StatusBadRequest) - ip, _, _ := net.SplitHostPort(r.RemoteAddr) + ip, _ := r.Context().Value("ClientID").(string) if ip != "" { TriggerEvent( "cosmos.proxy.shield.referer", @@ -346,7 +363,7 @@ func EnsureHostname(next http.Handler) http.Handler { w.WriteHeader(http.StatusBadRequest) http.Error(w, "Bad Request: Invalid hostname. Use your domain instead of your IP to access your server. Check logs if more details are needed.", http.StatusBadRequest) - ip, _, _ := net.SplitHostPort(r.RemoteAddr) + ip, _ := r.Context().Value("ClientID").(string) if ip != "" { TriggerEvent( "cosmos.proxy.shield.hostname", @@ -412,7 +429,7 @@ func EnsureHostnameCosmosAPI(next http.Handler) http.Handler { w.WriteHeader(http.StatusBadRequest) http.Error(w, "Bad Request: Invalid hostname. Use your domain instead of your IP to access your server. Check logs if more details are needed.", http.StatusBadRequest) - ip, _, _ := net.SplitHostPort(r.RemoteAddr) + ip, _ := r.Context().Value("ClientID").(string) if ip != "" { TriggerEvent( "cosmos.proxy.shield.hostname", @@ -483,8 +500,9 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { + remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr) + ip, ok := r.Context().Value("ClientID").(string) + if (err != nil) || !ok { http.Error(w, "Invalid request", http.StatusBadRequest) return } @@ -492,7 +510,7 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu isUsingWhiteList := len(WhitelistInboundIPs) > 0 isInWhitelist := false - isInConstellation := strings.HasPrefix(ip, "192.168.201.") || strings.HasPrefix(ip, "192.168.202.") + isInConstellation := strings.HasPrefix(remoteAddr, "192.168.201.") || strings.HasPrefix(remoteAddr, "192.168.202.") for _, ipRange := range WhitelistInboundIPs { Debug("Checking if " + ip + " is in " + ipRange) diff --git a/src/utils/utils.go b/src/utils/utils.go index 7c586d18..e1fcab59 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -779,11 +779,18 @@ func DownloadFile(url string) (string, error) { } func GetClientIP(req *http.Request) string { - /*ip := req.Header.Get("X-Forwarded-For") - if ip == "" { - ip = req.RemoteAddr - }*/ - return req.RemoteAddr + // when using Docker we need to get the real IP + remoteAddr, _ := SplitIP(req.RemoteAddr) + UseForwardedFor := GetMainConfig().HTTPConfig.UseForwardedFor + + if ((UseForwardedFor || IsTrustedProxy(remoteAddr)) && req.Header.Get("x-forwarded-for") != "") { + ip := strings.TrimSpace(strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0]) + Debug("Client IP : " + ip) + return ip + } else { + Debug("Client IP : " + remoteAddr) + return remoteAddr + } } func IsDomain(domain string) bool { From 594b5e4cda39a92093218fad020fd61c6d1d3167 Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Mon, 14 Oct 2024 21:43:01 +0200 Subject: [PATCH 09/16] Removed Debug line --- src/utils/middleware.go | 2 -- src/utils/utils.go | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index ff8b8967..cbd80988 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -54,8 +54,6 @@ func ClientRealIP(next http.Handler) http.Handler { if(clientID == ""){ http.Error(w, "Invalid request", http.StatusBadRequest) return - } else { - Debug("Add ClientID in context : " + clientID) } ctx := context.WithValue(r.Context(), "ClientID", clientID) diff --git a/src/utils/utils.go b/src/utils/utils.go index e1fcab59..2e3ebf0d 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -785,10 +785,8 @@ func GetClientIP(req *http.Request) string { if ((UseForwardedFor || IsTrustedProxy(remoteAddr)) && req.Header.Get("x-forwarded-for") != "") { ip := strings.TrimSpace(strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0]) - Debug("Client IP : " + ip) return ip } else { - Debug("Client IP : " + remoteAddr) return remoteAddr } } From 6fc014b1602354955922c8df19b469b3a656100d Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Tue, 15 Oct 2024 18:55:09 +0200 Subject: [PATCH 10/16] Remove duplicated code Using IPInRange in IsTrustedProxy SplitIP put back in shield (this is not a problem with a standard forwarded-for header) Modified IPInRange to allow comparison of 2 valid IPs --- src/proxy/shield.go | 21 +++++++++++++-------- src/utils/middleware.go | 13 ++++++++----- src/utils/utils.go | 25 ++++++------------------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/proxy/shield.go b/src/proxy/shield.go index 1ab3489d..46494af4 100644 --- a/src/proxy/shield.go +++ b/src/proxy/shield.go @@ -297,14 +297,19 @@ func calculateLowestExhaustedPercentage(policy utils.SmartShieldPolicy, userCons func GetClientID(r *http.Request, route utils.ProxyRouteConfig) string { // when using Docker we need to get the real IP remoteAddr, _ := utils.SplitIP(r.RemoteAddr) - UseForwardedFor := utils.GetMainConfig().HTTPConfig.UseForwardedFor - isTunneledIp := constellation.GetDeviceIp(route.TunnelVia) == remoteAddr - isConstIP := utils.IsConstellationIP(remoteAddr) - isConstTokenValid := constellation.CheckConstellationToken(r) == nil - - if ((UseForwardedFor || utils.IsTrustedProxy(remoteAddr) ) && r.Header.Get("x-forwarded-for") != "") || - (isTunneledIp && isConstIP && isConstTokenValid) { - ip := strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]) + useForwardedForHeader := false + if r.Header.Get("x-forwarded-for") != "" { + useForwardedForHeader = utils.IsTrustedProxy(remoteAddr) + if !useForwardedForHeader { + isTunneledIp := constellation.GetDeviceIp(route.TunnelVia) == remoteAddr + isConstIP := utils.IsConstellationIP(remoteAddr) + isConstTokenValid := constellation.CheckConstellationToken(r) == nil + useForwardedForHeader = isTunneledIp && isConstIP && isConstTokenValid + } + } + + if useForwardedForHeader { + ip, _ := utils.SplitIP(strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0])) utils.Debug("SmartShield: Getting forwarded client ID " + ip) return ip } else { diff --git a/src/utils/middleware.go b/src/utils/middleware.go index cbd80988..b0b0c383 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -481,16 +481,19 @@ func IsValidHostname(hostname string) bool { } func IPInRange(ipStr, cidrStr string) (bool, error) { - _, cidrNet, err := net.ParseCIDR(cidrStr) - if err != nil { - return false, fmt.Errorf("parse CIDR range: %w", err) - } - ip := net.ParseIP(ipStr) if ip == nil { return false, fmt.Errorf("parse IP: invalid IP address") } + _, cidrNet, err := net.ParseCIDR(cidrStr) + if err != nil { + if ipStr == cidrStr { + return true, nil + } + return false, fmt.Errorf("parse CIDR range: %w", err) + } + return cidrNet.Contains(ip), nil } diff --git a/src/utils/utils.go b/src/utils/utils.go index 2e3ebf0d..5c83f099 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -781,14 +781,11 @@ func DownloadFile(url string) (string, error) { func GetClientIP(req *http.Request) string { // when using Docker we need to get the real IP remoteAddr, _ := SplitIP(req.RemoteAddr) - UseForwardedFor := GetMainConfig().HTTPConfig.UseForwardedFor - if ((UseForwardedFor || IsTrustedProxy(remoteAddr)) && req.Header.Get("x-forwarded-for") != "") { - ip := strings.TrimSpace(strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0]) - return ip - } else { - return remoteAddr + if req.Header.Get("x-forwarded-for") != "" && IsTrustedProxy(remoteAddr) { + remoteAddr, _ = SplitIP(strings.TrimSpace(strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0])) } + return remoteAddr } func IsDomain(domain string) bool { @@ -905,22 +902,12 @@ func IsConstellationIP(ip string) bool { } func IsTrustedProxy(ip string) bool { - ipAddr := osnet.ParseIP(ip) for _, trustedProxy := range GetMainConfig().HTTPConfig.TrustedProxies { - _, cidr, err := osnet.ParseCIDR(trustedProxy) - if err != nil { - // If it's not a CIDR, check for exact match - if ip == trustedProxy { - return true - } - } else { - // Check if the IP is in the CIDR range - if cidr.Contains(ipAddr) { - return true - } + if isInRange, _ := IPInRange(ip, trustedProxy); isInRange { + return true } } - return false + return false } func SplitIP(ipPort string) (string, string) { From a22b849dc9d6c9b65d4fdece5d83fe2739deee83 Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Tue, 15 Oct 2024 20:22:51 +0200 Subject: [PATCH 11/16] Fix indentation --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index b0b0c383..4d404d27 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -66,7 +66,7 @@ func ClientRealIP(next http.Handler) http.Handler { func BlockBannedIPs(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip, ok := r.Context().Value("ClientID").(string) - if !ok { + if !ok { if hj, ok := w.(http.Hijacker); ok { conn, _, err := hj.Hijack() if err == nil { From de096b2e48fb285162f4454ea1b1c1d503c79f86 Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Thu, 31 Oct 2024 09:52:07 +0100 Subject: [PATCH 12/16] Update middleware.go Resolve conflict --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index 4d404d27..b640589c 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -82,7 +82,7 @@ func BlockBannedIPs(next http.Handler) http.Handler { Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") } - if nbAbuse > 300 { + if nbAbuse > 300 { if hj, ok := w.(http.Hijacker); ok { conn, _, err := hj.Hijack() if err == nil { From 969363ffd0e049089cc3ace76e32c769b2c4e8a4 Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Thu, 31 Oct 2024 09:55:02 +0100 Subject: [PATCH 13/16] Resolve conflict --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index b640589c..eecd74e4 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -82,7 +82,7 @@ func BlockBannedIPs(next http.Handler) http.Handler { Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") } - if nbAbuse > 300 { + nbAbuse := GetIPAbuseCounter(ip) if hj, ok := w.(http.Hijacker); ok { conn, _, err := hj.Hijack() if err == nil { From c77f94770882ca085125ed7aa131f2239d59dc1d Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Thu, 31 Oct 2024 09:57:22 +0100 Subject: [PATCH 14/16] Revert "Resolve conflict" This reverts commit 969363ffd0e049089cc3ace76e32c769b2c4e8a4. --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index eecd74e4..b640589c 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -82,7 +82,7 @@ func BlockBannedIPs(next http.Handler) http.Handler { Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") } - nbAbuse := GetIPAbuseCounter(ip) + if nbAbuse > 300 { if hj, ok := w.(http.Hijacker); ok { conn, _, err := hj.Hijack() if err == nil { From ba094bcea3e4b40edfb148d2b874120e5d1e0e42 Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Thu, 31 Oct 2024 09:57:30 +0100 Subject: [PATCH 15/16] Revert "Update middleware.go" This reverts commit de096b2e48fb285162f4454ea1b1c1d503c79f86. --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index b640589c..4d404d27 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -82,7 +82,7 @@ func BlockBannedIPs(next http.Handler) http.Handler { Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.") } - if nbAbuse > 300 { + if nbAbuse > 300 { if hj, ok := w.(http.Hijacker); ok { conn, _, err := hj.Hijack() if err == nil { From a716e0cec838aa709b55498b765dd460c1247dda Mon Sep 17 00:00:00 2001 From: Someone <7dn1yh5j@debauchez.fr> Date: Thu, 31 Oct 2024 09:58:11 +0100 Subject: [PATCH 16/16] Resolv conflict --- src/utils/middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/middleware.go b/src/utils/middleware.go index 4d404d27..d1a88dc3 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -76,7 +76,7 @@ func BlockBannedIPs(next http.Handler) http.Handler { return } - nbAbuse := getIPAbuseCounter(ip) + nbAbuse := getIPAbuseCounter(ip) if nbAbuse > 275 { Warn("IP " + ip + " has " + fmt.Sprintf("%d", nbAbuse) + " abuse(s) and will soon be banned.")