chore(api): log X-Forwarded-For (#10035)
### Purpose Fix https://github.com/syncthing/syncthing/issues/9336 The `emitLoginAttempt` function now checks for the presence of an `X-Forwarded-For` header. The IP from this header is only used if the connecting host is either on loopback or on the same LAN. In the case of a host pretending to be a proxy, we'd still have both IPs in the logs, which should make this much less critical from a security standpoint. ### Testing 1. directly via localhost 2. via proxy an localhost #### Logs ``` [3JPXJ] 2025/04/11 15:00:40 INFO: Wrong credentials supplied during API authorization from 127.0.0.1 [3JPXJ] 2025/04/11 15:03:04 INFO: Wrong credentials supplied during API authorization from 192.168.178.5 proxied by 127.0.0.1 ``` #### Event API ``` { "id": 23, "globalID": 23, "time": "2025-04-11T15:00:40.578577402+02:00", "type": "LoginAttempt", "data": { "remoteAddress": "127.0.0.1", "success": false, "username": "sdfsd" } }, { "id": 24, "globalID": 24, "time": "2025-04-11T15:03:04.423403976+02:00", "type": "LoginAttempt", "data": { "proxy": "127.0.0.1", "remoteAddress": "192.168.178.5", "success": false, "username": "sdfsd" } } ``` ### Documentation https://github.com/syncthing/docs/pull/907 --------- Co-authored-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
@@ -8,6 +8,7 @@ package osutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetInterfaceAddrs returns the IP networks of all interfaces that are up.
|
||||
@@ -46,6 +47,17 @@ func GetInterfaceAddrs(includePtP bool) ([]*net.IPNet, error) {
|
||||
return nets, nil
|
||||
}
|
||||
|
||||
func IPFromString(addr string) net.IP {
|
||||
// strip the port
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
host = addr
|
||||
}
|
||||
// strip IPv6 zone identifier
|
||||
host, _, _ = strings.Cut(host, "%")
|
||||
return net.ParseIP(host)
|
||||
}
|
||||
|
||||
func IPFromAddr(addr net.Addr) (net.IP, error) {
|
||||
switch a := addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
|
||||
@@ -135,3 +135,35 @@ func TestRenameOrCopy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPFromString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"192.168.178.1", "192.168.178.1"},
|
||||
{"192.168.178.1:8384", "192.168.178.1"},
|
||||
{"fe80::20c:29ff:fe9a:46d2", "fe80::20c:29ff:fe9a:46d2"},
|
||||
{"[fe80::20c:29ff:fe9a:46d2]:8384", "fe80::20c:29ff:fe9a:46d2"},
|
||||
{"[fe80::20c:29ff:fe9a:46d2%eno1]:8384", "fe80::20c:29ff:fe9a:46d2"},
|
||||
{"google.com", ""},
|
||||
{"1.1.1.1.1", ""},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
ip := osutil.IPFromString(c.in)
|
||||
var address string
|
||||
if ip != nil {
|
||||
address = ip.String()
|
||||
} else {
|
||||
address = ""
|
||||
}
|
||||
|
||||
if c.out != address {
|
||||
t.Fatalf("result should be %s != %s", c.out, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user