diff --git a/controller/persistence/migration_initialize.go b/controller/persistence/migration_initialize.go index ca480db57..ae3152ab1 100644 --- a/controller/persistence/migration_initialize.go +++ b/controller/persistence/migration_initialize.go @@ -18,8 +18,8 @@ package persistence import ( "fmt" - "github.com/openziti/ziti/controller/db" "github.com/openziti/storage/boltz" + "github.com/openziti/ziti/controller/db" "math" "time" ) @@ -509,6 +509,13 @@ var interceptV1ConfigType = &ConfigType{ "type": "string", "description": "The source IP (and optional :port) to spoof when the connection is egressed from the hosting tunneler. '$tunneler_id.name' resolves to the name of the client tunneler's identity. '$tunneler_id.tag[tagName]' resolves to the value of the 'tagName' tag on the client tunneler's identity. '$src_ip' and '$src_port' resolve to the source IP / port of the originating client. '$dst_port' resolves to the port that the client is trying to connect.", }, + "allowedSourceAddresses": map[string]interface{}{ + "allOf": []interface{}{ + map[string]interface{}{"$ref": "#/definitions/inhabitedSet"}, + map[string]interface{}{"items": map[string]interface{}{"$ref": "#/definitions/listenAddress"}}, + }, + "description": "white list of source ips/cidrs that can be intercepted. all ips can be intercepted if this is not set.", + }, }, "required": []interface{}{ "protocols", diff --git a/controller/persistence/migrations.go b/controller/persistence/migrations.go index d85fd7afa..ec40da4d5 100644 --- a/controller/persistence/migrations.go +++ b/controller/persistence/migrations.go @@ -18,13 +18,13 @@ package persistence import ( "github.com/michaelquigley/pfxlog" - "github.com/openziti/ziti/controller/db" "github.com/openziti/storage/boltz" + "github.com/openziti/ziti/controller/db" "github.com/pkg/errors" ) const ( - CurrentDbVersion = 34 + CurrentDbVersion = 35 FieldVersion = "version" ) @@ -156,6 +156,10 @@ func (m *Migrations) migrate(step *boltz.MigrationStep) int { step.SetError(m.stores.ConfigType.Update(step.Ctx, hostV2ConfigType, nil)) } + if step.CurrentVersion < 35 { + step.SetError(m.stores.ConfigType.Update(step.Ctx, interceptV1ConfigType, nil)) + } + // current version if step.CurrentVersion <= CurrentDbVersion { return CurrentDbVersion diff --git a/tunnel/entities/intercept.v1.json b/tunnel/entities/intercept.v1.json index d27e87d49..838c56f12 100644 --- a/tunnel/entities/intercept.v1.json +++ b/tunnel/entities/intercept.v1.json @@ -69,6 +69,19 @@ } ] }, + "allowedSourceAddresses": { + "description": "white list of source ips/cidrs that can be intercepted. all ips can be intercepted if this is not set.", + "allOf": [ + { + "$ref": "#/definitions/inhabitedSet" + }, + { + "items": { + "$ref": "#/definitions/listenAddress" + } + } + ] + }, "dialOptions": { "additionalProperties": false, "properties": { diff --git a/tunnel/entities/service.go b/tunnel/entities/service.go index b79e821ec..40b4efb0f 100644 --- a/tunnel/entities/service.go +++ b/tunnel/entities/service.go @@ -5,11 +5,11 @@ import ( "github.com/michaelquigley/pfxlog" "github.com/mitchellh/mapstructure" "github.com/openziti/edge-api/rest_model" + "github.com/openziti/foundation/v2/genext" + "github.com/openziti/foundation/v2/stringz" "github.com/openziti/ziti/tunnel" "github.com/openziti/ziti/tunnel/health" "github.com/openziti/ziti/tunnel/utils" - "github.com/openziti/foundation/v2/genext" - "github.com/openziti/foundation/v2/stringz" "github.com/pkg/errors" "net" "reflect" @@ -352,11 +352,12 @@ type PortRange struct { } type InterceptV1Config struct { - Addresses []string - PortRanges []*PortRange - Protocols []string - SourceIp *string - DialOptions *DialOptions + Addresses []string + PortRanges []*PortRange + Protocols []string + SourceIp *string // source ip[:port] to bind on outbound connections from hosting tunneler/ERT + AllowedSourceAddresses []string // white list for source IPs/CIDRs that will be intercepted + DialOptions *DialOptions } type TemplateFunc func(sourceAddr net.Addr, destAddr net.Addr) string diff --git a/tunnel/intercept/tproxy/tproxy_linux.go b/tunnel/intercept/tproxy/tproxy_linux.go index 7d6654b7a..a94c34f05 100644 --- a/tunnel/intercept/tproxy/tproxy_linux.go +++ b/tunnel/intercept/tproxy/tproxy_linux.go @@ -21,16 +21,16 @@ import ( "fmt" "github.com/coreos/go-iptables/iptables" "github.com/michaelquigley/pfxlog" + "github.com/openziti/foundation/v2/info" + "github.com/openziti/foundation/v2/mempool" + "github.com/openziti/foundation/v2/stringz" + "github.com/openziti/sdk-golang/ziti/edge/network" "github.com/openziti/ziti/tunnel" "github.com/openziti/ziti/tunnel/dns" "github.com/openziti/ziti/tunnel/entities" "github.com/openziti/ziti/tunnel/intercept" "github.com/openziti/ziti/tunnel/router" "github.com/openziti/ziti/tunnel/udp_vconn" - "github.com/openziti/foundation/v2/info" - "github.com/openziti/foundation/v2/mempool" - "github.com/openziti/foundation/v2/stringz" - "github.com/openziti/sdk-golang/ziti/edge/network" cmap "github.com/orcaman/concurrent-map/v2" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -511,33 +511,43 @@ func (self *tProxy) addInterceptAddr(interceptAddr *intercept.InterceptAddress, self.addresses = append(self.addresses, interceptAddr) if self.interceptor.diverter != "" { - cidr := strings.Split(ipNet.String(), "/") - if len(cidr) != 2 { - return errors.Errorf("failed parsing '%s' as cidr", ipNet.String()) - } - cmd := exec.Command(self.interceptor.diverter, "-I", - "-c", cidr[0], "-m", cidr[1], "-p", interceptAddr.Proto(), - "-l", fmt.Sprintf("%d", interceptAddr.LowPort()), "-h", fmt.Sprintf("%d", interceptAddr.HighPort()), - "-t", fmt.Sprintf("%d", port.GetPort())) - cmdLogger := pfxlog.Logger().WithField("command", cmd.String()) - cmdLogger.Debug("running external diverter") - out, err := cmd.CombinedOutput() - if err != nil { - return errors.Errorf("diverter command failed. output: %s", out) - } else { - cmdLogger.Infof("diverter command succeeded. output: %s", out) + for _, srcCidr := range self.getSourceAddressesAsCidrs() { + cidr := strings.Split(ipNet.String(), "/") + if len(cidr) != 2 { + return errors.Errorf("failed parsing '%s' as cidr", ipNet.String()) + } + cmd := exec.Command(self.interceptor.diverter, "-I", + "-c", cidr[0], "-m", cidr[1], "-p", interceptAddr.Proto(), + "-o", srcCidr.ip, "-n", srcCidr.prefixLen, + "-l", fmt.Sprintf("%d", interceptAddr.LowPort()), "-h", fmt.Sprintf("%d", interceptAddr.HighPort()), + "-t", fmt.Sprintf("%d", port.GetPort())) + cmdLogger := pfxlog.Logger().WithField("command", cmd.String()) + cmdLogger.Debug("running external diverter") + out, err := cmd.CombinedOutput() + if err != nil { + return errors.Errorf("diverter command failed. output: %s", out) + } else { + cmdLogger.Infof("diverter command succeeded. output: %s", out) + } } } else { - interceptAddr.TproxySpec = []string{ + baseSpec := []string{ "-m", "comment", "--comment", *service.Name, "-d", ipNet.String(), "-p", interceptAddr.Proto(), "--dport", fmt.Sprintf("%v:%v", interceptAddr.LowPort(), interceptAddr.HighPort()), + } + + if service.InterceptV1Config.AllowedSourceAddresses != nil { + baseSpec = append(baseSpec, "-s", strings.Join(service.InterceptV1Config.AllowedSourceAddresses, ",")) + } + + interceptAddr.TproxySpec = append(baseSpec, "-j", "TPROXY", "--tproxy-mark", "0x1/0x1", fmt.Sprintf("--on-ip=%s", port.GetIP().String()), fmt.Sprintf("--on-port=%d", port.GetPort()), - } + ) pfxlog.Logger().Infof("Adding rule iptables -t %v -A %v %v", mangleTable, dstChain, interceptAddr.TproxySpec) if err := self.interceptor.ipt.Insert(mangleTable, dstChain, 1, interceptAddr.TproxySpec...); err != nil { @@ -545,14 +555,10 @@ func (self *tProxy) addInterceptAddr(interceptAddr *intercept.InterceptAddress, } if self.interceptor.lanIf != "" { - interceptAddr.AcceptSpec = []string{ + interceptAddr.AcceptSpec = append(baseSpec, "-i", self.interceptor.lanIf, - "-m", "comment", "--comment", *service.Name, - "-d", ipNet.String(), - "-p", interceptAddr.Proto(), - "--dport", fmt.Sprintf("%v:%v", interceptAddr.LowPort(), interceptAddr.HighPort()), "-j", "ACCEPT", - } + ) pfxlog.Logger().Infof("Adding rule iptables -t %v -A %v %v", filterTable, dstChain, interceptAddr.AcceptSpec) if err := self.interceptor.ipt.Insert(filterTable, dstChain, 1, interceptAddr.AcceptSpec...); err != nil { return errors.Wrap(err, "failed to insert rule") @@ -573,21 +579,24 @@ func (self *tProxy) StopIntercepting(tracker intercept.AddressTracker) error { log.Infof("removing intercepted low-port: %v, high-port: %v", addr.LowPort(), addr.HighPort()) if self.interceptor.diverter != "" { - cidr := strings.Split(addr.IpNet().String(), "/") - if len(cidr) != 2 { - return errors.Errorf("failed parsing '%s' as cidr", addr.IpNet().String()) - } - cmd := exec.Command(self.interceptor.diverter, "-D", - "-c", cidr[0], "-m", cidr[1], "-p", addr.Proto(), - "-l", fmt.Sprintf("%d", addr.LowPort()), "-h", fmt.Sprintf("%d", addr.HighPort())) - cmdLogger := pfxlog.Logger().WithField("command", cmd.String()) - cmdLogger.Debug("running external diverter") - out, err := cmd.CombinedOutput() - if err != nil { - errorList = append(errorList, err) - cmdLogger.Errorf("diverter command failed. output: %s", out) - } else { - cmdLogger.Infof("diverter command succeeded. output: %s", out) + for _, srcCidr := range self.getSourceAddressesAsCidrs() { + cidr := strings.Split(addr.IpNet().String(), "/") + if len(cidr) != 2 { + return errors.Errorf("failed parsing '%s' as cidr", addr.IpNet().String()) + } + cmd := exec.Command(self.interceptor.diverter, "-D", + "-c", cidr[0], "-m", cidr[1], "-p", addr.Proto(), + "-o", srcCidr.ip, "-n", srcCidr.prefixLen, + "-l", fmt.Sprintf("%d", addr.LowPort()), "-h", fmt.Sprintf("%d", addr.HighPort())) + cmdLogger := pfxlog.Logger().WithField("command", cmd.String()) + cmdLogger.Debug("running external diverter") + out, err := cmd.CombinedOutput() + if err != nil { + errorList = append(errorList, err) + cmdLogger.Errorf("diverter command failed. output: %s", out) + } else { + cmdLogger.Infof("diverter command succeeded. output: %s", out) + } } } else { log.Infof("Removing rule iptables -t %v -A %v %v", mangleTable, dstChain, addr.TproxySpec) @@ -625,6 +634,35 @@ func (self *tProxy) StopIntercepting(tracker intercept.AddressTracker) error { return network.MultipleErrors(errorList) } +type cidrString = struct { + ip string + prefixLen string +} + +func (self *tProxy) getSourceAddressesAsCidrs() []cidrString { + var srcAddrs []string + if self.service.InterceptV1Config.AllowedSourceAddresses != nil { + srcAddrs = self.service.InterceptV1Config.Addresses + } else { + srcAddrs = []string{"0.0.0.0/0"} + } + + var cidrStrings []cidrString + + for _, srcAddr := range srcAddrs { + srcCidr := strings.Split(srcAddr, "/") + ip := srcCidr[0] + prefixLen := "0" + if len(srcCidr) == 2 { + prefixLen = srcCidr[1] + } + cidrStrings = append(cidrStrings, cidrString{ip: ip, prefixLen: prefixLen}) + + } + + return cidrStrings +} + type IPPortAddr interface { GetIP() net.IP GetPort() int