-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
ipn/ipnlocal: Support TCP and Web VIP services #14508
base: main
Are you sure you want to change the base?
Conversation
7cbfe27
to
36c0813
Compare
This commit intend to provide support for TCP and Web VIP services and also allow user to use Tun for VIP services if they want to. The commit includes: 1.Setting TCP intercept function for VIP Services. 2.Update netstack to send packet written from WG to netStack handler for VIP service. 3.Return correct TCP hander for VIP services when netstack acceptTCP. This commit also includes unit tests for if the local backend setServeConfig would set correct TCP intercept function and test if a hander gets returned when getting TCPHandlerForDst. The shouldProcessInbound check is not unit tested since the test result just depends on mocked functions. There should be an integration test to cover shouldProcessInbound and if the returned TCP handler actually does what the serveConfig says. Updates tailscale/corp#24604 Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
45fa3ba
to
579d75d
Compare
ipn/serve.go
Outdated
@@ -662,6 +682,17 @@ func (v ServeConfigView) HasFunnelForTarget(target HostPort) bool { | |||
return false | |||
} | |||
|
|||
// HasValidServicesConfig reports whether if ServeConfig has at least |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just "whether" not "whether if"
but "whether" wording is normally used only for bool results. This returns an error.
So also the "at least one" wording is weird with an error result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about
"CheckValidServicesConfig reports whether the ServeConfig has invalid service configurations."
// Services maps from service name to a ServiceConfig. Which describes the | ||
// L3, L4, and L7 forwarding information for the service. | ||
// Services maps from service name (in the form "svc:dns_label") to a ServiceConfig. | ||
// Which describes the L3, L4, and L7 forwarding information for the service. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What L3 stuff do we do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't do anything. If Tun is set to true, we just let the packets pass to Tun and user can do their own customization to route then to their wanted destination.
Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
if vipServiceIPMap == nil { | ||
b.logf("can't set intercept function for Service TCP Ports, VIPServiceAddrInfo is nil") | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if vipServiceIPMap == nil { | |
b.logf("can't set intercept function for Service TCP Ports, VIPServiceAddrInfo is nil") | |
return | |
} | |
if len(vipServiceIPMap) == 0 { | |
// No approved VIP Services | |
return | |
} |
return | ||
} | ||
|
||
svcAddrPorts := map[netip.Addr]func(uint16) bool{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the convention is usually to make a map so that it has some empty space allocated.
svcAddrPorts := map[netip.Addr]func(uint16) bool{} | |
svcAddrPorts := make(map[netip.Addr]func(uint16) bool) |
|
||
svcAddrPorts := map[netip.Addr]func(uint16) bool{} | ||
// Only set the intercept function if the service has been assigned a VIP. | ||
for svcName, ports := range svcPorts { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, but I feel like it might make more sense to range on vipServiceIPMap
since I'd expect that to be the smaller of the two maps.
for svcName, ports := range svcPorts { | ||
if addrs, ok := vipServiceIPMap[svcName]; ok { | ||
for _, addr := range addrs { | ||
svcAddrPorts[addr] = generateInterceptTCPPortFunc(ports) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend it generate the func once outside the loop
@@ -6013,10 +6095,11 @@ func (b *LocalBackend) setTCPPortsInterceptedFromNetmapAndPrefsLocked(prefs ipn. | |||
if wire := b.wantIngressLocked(); b.hostinfo != nil && b.hostinfo.WireIngress != wire { | |||
b.logf("Hostinfo.WireIngress changed to %v", wire) | |||
b.hostinfo.WireIngress = wire | |||
b.goTracker.Go(b.doSetHostinfoFilterServices) | |||
go b.doSetHostinfoFilterServices() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch this back to using goTracker
func (sc *ServeConfig) CheckValidServicesConfig() error { | ||
for svcName, service := range sc.Services { | ||
if err := service.checkValidConfig(); err != nil { | ||
return fmt.Errorf("invalid service configuration for \"%q\": %w", svcName, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%q
already puts quotes around the contents, you don't need to add them manually
return nil | ||
} | ||
|
||
ipMaps, err := tailcfg.UnmarshalNodeCapJSON[tailcfg.ServiceIPMappings](nm.SelfNode.AsStruct().CapMap, tailcfg.NodeAttrServiceHost) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a way to avoid running this through AsStruct
since that makes an expensive deep copy.
// GetVIPServiceIPMap returns a map of service names to the slice of | ||
// VIP addresses that correspond to the service. The service names are | ||
// with the prefix "svc:". | ||
func (nm *NetworkMap) GetVIPServiceIPMap() tailcfg.ServiceIPMappings { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like it would be good for this to cache the unmarshaled details rather than JSON decoding multiple times when a new netmap is received.
ns.atomicIsVIPServiceIPFunc.Store(func(ip netip.Addr) bool { | ||
_, ok := serviceAddrSet[ip] | ||
return ok | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work?
ns.atomicIsVIPServiceIPFunc.Store(func(ip netip.Addr) bool { | |
_, ok := serviceAddrSet[ip] | |
return ok | |
}) | |
ns.atomicIsVIPServiceIPFunc.Store(serviceAddrSet.Contains) |
@@ -990,6 +1011,13 @@ func (ns *Impl) shouldProcessInbound(p *packet.Parsed, t *tstun.Wrapper) bool { | |||
return true | |||
} | |||
} | |||
if ns.lb != nil && p.IPProto == ipproto.TCP && isService { | |||
// An assumption holds for this to work: when tun mode is on for a service, | |||
// it's tcp and web are not set. It's enforced in b.setServeConfigLocked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// it's tcp and web are not set. It's enforced in b.setServeConfigLocked. | |
// its tcp and web are not set. This is enforced in b.setServeConfigLocked. |
This PR intend to provide support for TCP and Web VIP services and also allow user to use Tun for VIP services if they want to.
The first commit includes:
1.Setting TCP intercept function for VIP Services.
2.Update netstack to send packet written from WG to netStack handler for VIP service.
3.Return correct TCP hander for VIP services when netstack acceptTCP.
This commit also includes unit tests for if the local backend setServeConfig would set correct TCP intercept
function and test if a hander gets returned when getting TCPHandlerForDst. The shouldProcessInbound
check is not unit tested since the test result just depends on mocked functions. There should be an integration
test to cover shouldProcessInbound and if the returned TCP handler actually does what the serveConfig says.
Updates https://github.com/tailscale/corp/issues/24604
Signed-off-by: KevinLiang10 37811973+KevinLiang10@users.noreply.github.com