From e0d291ab8a622f39d6fe8492abcb3224ed6730dd Mon Sep 17 00:00:00 2001 From: Maisem Ali Date: Wed, 29 Mar 2023 13:51:53 -0700 Subject: [PATCH] ipn/store: add support for stores to hook into a custom dialer For stores like k8s secrets we need to dial out to the k8s API as though Tailscale wasn't running. The issue currently only manifests when you try to use an exit node while running inside a k8s cluster and are trying to use Kubernetes secrets as the backing store. This doesn't address cmd/containerboot, which I'll do in a follow up. Updates #7695 Signed-off-by: Maisem Ali --- ipn/ipnlocal/local.go | 3 +++ ipn/store.go | 8 ++++++++ ipn/store/kubestore/store_kube.go | 5 +++++ kube/client.go | 7 +++++++ 4 files changed, 23 insertions(+) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 5be259af2..9a2429f6f 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -276,6 +276,9 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor if err != nil { return nil, err } + if sds, ok := store.(ipn.StateStoreDialerSetter); ok { + sds.SetDialer(dialer.SystemDial) + } hi := hostinfo.New() logf.JSON(1, "Hostinfo", hi) diff --git a/ipn/store.go b/ipn/store.go index 33bc391cf..ee5a238a7 100644 --- a/ipn/store.go +++ b/ipn/store.go @@ -4,8 +4,10 @@ package ipn import ( + "context" "errors" "fmt" + "net" "strconv" ) @@ -72,6 +74,12 @@ type StateStore interface { WriteState(id StateKey, bs []byte) error } +// StateStoreDialerSetter is an optional interface that StateStores +// can implement to allow the caller to set a custom dialer. +type StateStoreDialerSetter interface { + SetDialer(d func(ctx context.Context, network, address string) (net.Conn, error)) +} + // ReadStoreInt reads an integer from a StateStore. func ReadStoreInt(store StateStore, id StateKey) (int64, error) { v, err := store.ReadState(id) diff --git a/ipn/store/kubestore/store_kube.go b/ipn/store/kubestore/store_kube.go index b82d041ff..65c73c1bd 100644 --- a/ipn/store/kubestore/store_kube.go +++ b/ipn/store/kubestore/store_kube.go @@ -7,6 +7,7 @@ package kubestore import ( "context" + "net" "strings" "time" @@ -33,6 +34,10 @@ func New(_ logger.Logf, secretName string) (*Store, error) { }, nil } +func (s *Store) SetDialer(d func(ctx context.Context, network, address string) (net.Conn, error)) { + s.client.SetDialer(d) +} + func (s *Store) String() string { return "kube.Store" } // ReadState implements the StateStore interface. diff --git a/kube/client.go b/kube/client.go index 020a8776e..ef62b74ce 100644 --- a/kube/client.go +++ b/kube/client.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "log" + "net" "net/http" "net/url" "os" @@ -90,6 +91,12 @@ func (c *Client) SetURL(url string) { c.url = url } +// SetDialer sets the dialer to use when establishing a connection +// to the Kubernetes API server. +func (c *Client) SetDialer(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) { + c.client.Transport.(*http.Transport).DialContext = dialer +} + func (c *Client) expireToken() { c.mu.Lock() defer c.mu.Unlock()