diff --git a/client/proxy.go b/client/proxy.go new file mode 100644 index 000000000..d3072012e --- /dev/null +++ b/client/proxy.go @@ -0,0 +1,58 @@ +package client + +import ( + "crypto/tls" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/canonical/lxd/shared" + "github.com/canonical/microcluster/client" + + "github.com/canonical/microcloud/microcloud/api/types" +) + +// UseAuthProxy takes the given microcluster client and secret and proxies requests to other services through the MicroCloud API. +// The secret will be set in the authentication header in lieu of TLS authentication, if present. +func UseAuthProxy(c *client.Client, secret string, serviceType types.ServiceType) (*client.Client, error) { + tp, ok := c.Transport.(*http.Transport) + if !ok { + return nil, fmt.Errorf("Invalid client transport type") + } + + // If the client is a unix client, it may not have any TLS config. + if tp.TLSClientConfig == nil { + tp.TLSClientConfig = &tls.Config{} + } + + // Only set InsecureSkipVerify if the secret is non-empty, so we will fallback to regular TLS authentication. + if secret != "" { + tp.TLSClientConfig.InsecureSkipVerify = true + } + + tp.Proxy = AuthProxy(secret, serviceType) + + c.Transport = tp + + return c, nil +} + +// AuthProxy takes a request to a service and sends it to MicroCloud instead, +// to be then forwarded to the unix socket of the corresponding service. +// The secret is set in the request header to use in lieu of TLS authentication. +func AuthProxy(secret string, serviceType types.ServiceType) func(r *http.Request) (*url.URL, error) { + return func(r *http.Request) (*url.URL, error) { + r.Header.Set("X-MicroCloud-Auth", secret) + + // MicroCloud itself doesn't need to use the proxy other than to set the auth secret. + if serviceType != types.MicroCloud { + path := fmt.Sprintf("/1.0/services/%s", strings.ToLower(string(serviceType))) + if !strings.HasPrefix(r.URL.Path, path) { + r.URL.Path = path + r.URL.Path + } + } + + return shared.ProxyFromEnvironment(r) + } +} diff --git a/service/lxd.go b/service/lxd.go index ae43bccbe..9c34b0b72 100644 --- a/service/lxd.go +++ b/service/lxd.go @@ -5,8 +5,6 @@ import ( "context" "fmt" "net" - "net/http" - "net/url" "os" "strings" "time" @@ -20,6 +18,7 @@ import ( "golang.org/x/mod/semver" "github.com/canonical/microcloud/microcloud/api/types" + cloudClient "github.com/canonical/microcloud/microcloud/client" "github.com/canonical/microcloud/microcloud/mdns" ) @@ -60,14 +59,7 @@ func (s LXDService) Client(ctx context.Context, secret string) (lxd.InstanceServ return lxd.ConnectLXDUnixWithContext(ctx, s.m.FileSystem.ControlSocket().URL.Host, &lxd.ConnectionArgs{ HTTPClient: c.Client.Client, SkipGetServer: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/lxd") { - r.URL.Path = "/1.0/services/lxd" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - }, + Proxy: cloudClient.AuthProxy(secret, types.LXD), }) } @@ -83,14 +75,7 @@ func (s LXDService) remoteClient(secret string, address string, port int64) (lxd HTTPClient: c.Client.Client, InsecureSkipVerify: true, SkipGetServer: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/lxd") { - r.URL.Path = "/1.0/services/lxd" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - }, + Proxy: cloudClient.AuthProxy(secret, types.LXD), }) if err != nil { return nil, err diff --git a/service/microceph.go b/service/microceph.go index 1a330de8b..e939ab77d 100644 --- a/service/microceph.go +++ b/service/microceph.go @@ -2,7 +2,6 @@ package service import ( "context" - "crypto/tls" "fmt" "net/http" "net/url" @@ -17,6 +16,7 @@ import ( "github.com/canonical/microcluster/microcluster" "github.com/canonical/microcloud/microcloud/api/types" + cloudClient "github.com/canonical/microcloud/microcloud/client" ) // CephService is a MicroCeph service. @@ -65,15 +65,9 @@ func (s CephService) Client(target string, secret string) (*client.Client, error c = c.UseTarget(target) } - if secret != "" { - c.Client.Client.Transport.(*http.Transport).Proxy = func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/microceph") { - r.URL.Path = "/1.0/services/microceph" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - } + c, err = cloudClient.UseAuthProxy(c, secret, types.MicroCeph) + if err != nil { + return nil, err } return c, nil @@ -141,17 +135,9 @@ func (s CephService) RemoteClusterMembers(ctx context.Context, secret string, ad return nil, err } - client.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/microceph") { - r.URL.Path = "/1.0/services/microceph" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - }, + client, err = cloudClient.UseAuthProxy(client, secret, types.MicroCeph) + if err != nil { + return nil, err } return clusterMembers(ctx, client) @@ -182,17 +168,9 @@ func (s CephService) ClusterConfig(ctx context.Context, targetAddress string, ta return nil, err } - c.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", targetSecret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/microceph") { - r.URL.Path = "/1.0/services/microceph" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - }, + c, err = cloudClient.UseAuthProxy(c, targetSecret, types.MicroCeph) + if err != nil { + return nil, err } } diff --git a/service/microcloud.go b/service/microcloud.go index 270f356f8..40af0ef10 100644 --- a/service/microcloud.go +++ b/service/microcloud.go @@ -2,15 +2,11 @@ package service import ( "context" - "crypto/tls" "fmt" - "net/http" - "net/url" "strconv" "time" "github.com/canonical/lxd/lxd/util" - "github.com/canonical/lxd/shared" "github.com/canonical/lxd/shared/api" cephTypes "github.com/canonical/microceph/microceph/api/types" microClient "github.com/canonical/microcluster/client" @@ -21,6 +17,7 @@ import ( "github.com/canonical/microcloud/microcloud/api/types" "github.com/canonical/microcloud/microcloud/client" + cloudClient "github.com/canonical/microcloud/microcloud/client" ) // CloudService is a MicroCloud service. @@ -105,14 +102,9 @@ func (s CloudService) RemoteIssueToken(ctx context.Context, clusterAddress strin return "", err } - c.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - - return shared.ProxyFromEnvironment(r) - }, + c, err = cloudClient.UseAuthProxy(c, secret, types.MicroCloud) + if err != nil { + return "", err } return client.RemoteIssueToken(ctx, c, serviceType, types.ServiceTokensPost{ClusterAddress: c.URL().URL.Host, JoinerName: peer}) @@ -141,14 +133,9 @@ func (s CloudService) RequestJoin(ctx context.Context, secret string, name strin return err } - c.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - - return shared.ProxyFromEnvironment(r) - }, + c, err = cloudClient.UseAuthProxy(c, secret, types.MicroCloud) + if err != nil { + return err } } @@ -162,14 +149,9 @@ func (s CloudService) RemoteClusterMembers(ctx context.Context, secret string, a return nil, err } - client.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - - return shared.ProxyFromEnvironment(r) - }, + client, err = cloudClient.UseAuthProxy(client, secret, types.MicroCloud) + if err != nil { + return nil, err } return clusterMembers(ctx, client) diff --git a/service/microovn.go b/service/microovn.go index ce30d4586..1131efba4 100644 --- a/service/microovn.go +++ b/service/microovn.go @@ -2,7 +2,6 @@ package service import ( "context" - "crypto/tls" "fmt" "net/http" "net/url" @@ -15,6 +14,7 @@ import ( "github.com/canonical/microcluster/microcluster" "github.com/canonical/microcloud/microcloud/api/types" + cloudClient "github.com/canonical/microcloud/microcloud/client" ) // OVNService is a MicroOVN service. @@ -101,17 +101,9 @@ func (s OVNService) RemoteClusterMembers(ctx context.Context, secret string, add return nil, err } - client.Client.Client.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives: true, - Proxy: func(r *http.Request) (*url.URL, error) { - r.Header.Set("X-MicroCloud-Auth", secret) - if !strings.HasPrefix(r.URL.Path, "/1.0/services/microovn") { - r.URL.Path = "/1.0/services/microovn" + r.URL.Path - } - - return shared.ProxyFromEnvironment(r) - }, + client, err = cloudClient.UseAuthProxy(client, secret, types.MicroOVN) + if err != nil { + return nil, err } return clusterMembers(ctx, client)