Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filter intercepted clients by source ip #1403

Draft
wants to merge 1 commit into
base: release-next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion controller/persistence/migration_initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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",
Expand Down
8 changes: 6 additions & 2 deletions controller/persistence/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions tunnel/entities/intercept.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
15 changes: 8 additions & 7 deletions tunnel/entities/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
122 changes: 80 additions & 42 deletions tunnel/intercept/tproxy/tproxy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -511,48 +511,54 @@ 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 {
return errors.Wrap(err, "failed to insert rule")
}

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")
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down