From 8972cd64904d445b27bfb7efd083f1d3bc5bbf29 Mon Sep 17 00:00:00 2001 From: alalamav <2132122+alalamav@users.noreply.github.com> Date: Thu, 31 Jan 2019 15:03:05 -0500 Subject: [PATCH] Restrict access to the server's private networks (#14) --- net/private_net.go | 43 ++++++++++++++++++++++++++++++++++++++ net/private_net_test.go | 46 +++++++++++++++++++++++++++++++++++++++++ shadowsocks/tcp.go | 3 +++ shadowsocks/udp.go | 3 +++ 4 files changed, 95 insertions(+) create mode 100644 net/private_net.go create mode 100644 net/private_net_test.go diff --git a/net/private_net.go b/net/private_net.go new file mode 100644 index 00000000..e8c21a71 --- /dev/null +++ b/net/private_net.go @@ -0,0 +1,43 @@ +// Copyright 2019 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package net + +import "net" + +var privateNetworks []*net.IPNet + +func init() { + for _, cidr := range []string{ + // RFC 1918: private IPv4 networks + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + // RFC 4193: IPv6 ULAs + "fc00::/7", + } { + _, subnet, _ := net.ParseCIDR(cidr) + privateNetworks = append(privateNetworks, subnet) + } +} + +// IsPrivateAddress returns whether an IP address belongs to the LAN. +func IsPrivateAddress(ip net.IP) bool { + for _, network := range privateNetworks { + if network.Contains(ip) { + return true + } + } + return false +} diff --git a/net/private_net_test.go b/net/private_net_test.go new file mode 100644 index 00000000..795e91a1 --- /dev/null +++ b/net/private_net_test.go @@ -0,0 +1,46 @@ +// Copyright 2019 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package net + +import ( + "net" + "testing" +) + +var privateAddressTests = []struct { + address string + expected bool +}{ + {"10.0.2.11", true}, + {"172.16.1.2", true}, + {"172.32.0.0", false}, + {"192.168.0.23", true}, + {"192.169.1.1", false}, + {"127.0.0.1", false}, + {"8.8.8.8", false}, + {"::", false}, + {"fd66:f83a:c650::1", true}, + {"fde4:8dba:82e1::", true}, + {"fe::123", false}, +} + +func TestIsLanAddress(t *testing.T) { + for _, tt := range privateAddressTests { + actual := IsPrivateAddress(net.ParseIP(tt.address)) + if actual != tt.expected { + t.Errorf("IsLanAddress(%s): expected %t, actual %t", tt.address, tt.expected, actual) + } + } +} diff --git a/shadowsocks/tcp.go b/shadowsocks/tcp.go index 9e1d6415..fe07a5ce 100644 --- a/shadowsocks/tcp.go +++ b/shadowsocks/tcp.go @@ -128,6 +128,9 @@ func proxyConnection(clientConn onet.DuplexConn, proxyMetrics *metrics.ProxyMetr if !tgtTCPAddr.IP.IsGlobalUnicast() { return onet.NewConnectionError("ERR_ADDRESS_INVALID", fmt.Sprintf("Target address is not global unicast: %v", tgtAddr.String()), err) } + if onet.IsPrivateAddress(tgtTCPAddr.IP) { + return onet.NewConnectionError("ERR_ADDRESS_PRIVATE", fmt.Sprintf("Target address is a private address: %v", tgtAddr.String()), nil) + } tgtTCPConn, err := net.DialTCP("tcp", nil, tgtTCPAddr) if err != nil { diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index fe120761..fa491bb8 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -140,6 +140,9 @@ func (s *udpService) Start() { if !tgtUDPAddr.IP.IsGlobalUnicast() { return onet.NewConnectionError("ERR_ADDRESS_INVALID", fmt.Sprintf("Target address is not global unicast: %v", tgtAddr.String()), err) } + if onet.IsPrivateAddress(tgtUDPAddr.IP) { + return onet.NewConnectionError("ERR_ADDRESS_PRIVATE", fmt.Sprintf("Target address is a private address: %v", tgtAddr.String()), nil) + } payload := buf[len(tgtAddr):]