mirror of https://github.com/portainer/portainer
fix(kube): Use KubeClusterAccessService for Helm operations [EE-2500] (#6559)
parent
cf7746082b
commit
c486130a9f
|
@ -599,7 +599,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||||
|
|
||||||
kubernetesTokenCacheManager := kubeproxy.NewTokenCacheManager()
|
kubernetesTokenCacheManager := kubeproxy.NewTokenCacheManager()
|
||||||
|
|
||||||
kubeConfigService := kubernetes.NewKubeConfigCAService(*flags.AddrHTTPS, sslSettings.CertPath)
|
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService(*flags.BaseURL, *flags.AddrHTTPS, sslSettings.CertPath)
|
||||||
|
|
||||||
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
|
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
|
||||||
|
|
||||||
|
@ -706,7 +706,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||||
OpenAMTService: openAMTService,
|
OpenAMTService: openAMTService,
|
||||||
ProxyManager: proxyManager,
|
ProxyManager: proxyManager,
|
||||||
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
|
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
|
||||||
KubeConfigService: kubeConfigService,
|
KubeClusterAccessService: kubeClusterAccessService,
|
||||||
SignatureService: digitalSignatureService,
|
SignatureService: digitalSignatureService,
|
||||||
SnapshotService: snapshotService,
|
SnapshotService: snapshotService,
|
||||||
SSLService: sslService,
|
SSLService: sslService,
|
||||||
|
@ -716,7 +716,6 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||||
ShutdownCtx: shutdownCtx,
|
ShutdownCtx: shutdownCtx,
|
||||||
ShutdownTrigger: shutdownTrigger,
|
ShutdownTrigger: shutdownTrigger,
|
||||||
StackDeployer: stackDeployer,
|
StackDeployer: stackDeployer,
|
||||||
BaseURL: *flags.BaseURL,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package helm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/portainer/libhelm"
|
"github.com/portainer/libhelm"
|
||||||
|
@ -14,10 +15,6 @@ import (
|
||||||
"github.com/portainer/portainer/api/kubernetes"
|
"github.com/portainer/portainer/api/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
handlerActivityContext = "Kubernetes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type requestBouncer interface {
|
type requestBouncer interface {
|
||||||
AuthenticatedAccess(h http.Handler) http.Handler
|
AuthenticatedAccess(h http.Handler) http.Handler
|
||||||
}
|
}
|
||||||
|
@ -28,13 +25,13 @@ type Handler struct {
|
||||||
requestBouncer requestBouncer
|
requestBouncer requestBouncer
|
||||||
dataStore dataservices.DataStore
|
dataStore dataservices.DataStore
|
||||||
jwtService dataservices.JWTService
|
jwtService dataservices.JWTService
|
||||||
kubeConfigService kubernetes.KubeConfigService
|
kubeClusterAccessService kubernetes.KubeClusterAccessService
|
||||||
kubernetesDeployer portainer.KubernetesDeployer
|
kubernetesDeployer portainer.KubernetesDeployer
|
||||||
helmPackageManager libhelm.HelmPackageManager
|
helmPackageManager libhelm.HelmPackageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHandler creates a handler to manage endpoint group operations.
|
// NewHandler creates a handler to manage endpoint group operations.
|
||||||
func NewHandler(bouncer requestBouncer, dataStore dataservices.DataStore, jwtService dataservices.JWTService, kubernetesDeployer portainer.KubernetesDeployer, helmPackageManager libhelm.HelmPackageManager, kubeConfigService kubernetes.KubeConfigService) *Handler {
|
func NewHandler(bouncer requestBouncer, dataStore dataservices.DataStore, jwtService dataservices.JWTService, kubernetesDeployer portainer.KubernetesDeployer, helmPackageManager libhelm.HelmPackageManager, kubeClusterAccessService kubernetes.KubeClusterAccessService) *Handler {
|
||||||
h := &Handler{
|
h := &Handler{
|
||||||
Router: mux.NewRouter(),
|
Router: mux.NewRouter(),
|
||||||
requestBouncer: bouncer,
|
requestBouncer: bouncer,
|
||||||
|
@ -42,7 +39,7 @@ func NewHandler(bouncer requestBouncer, dataStore dataservices.DataStore, jwtSer
|
||||||
jwtService: jwtService,
|
jwtService: jwtService,
|
||||||
kubernetesDeployer: kubernetesDeployer,
|
kubernetesDeployer: kubernetesDeployer,
|
||||||
helmPackageManager: helmPackageManager,
|
helmPackageManager: helmPackageManager,
|
||||||
kubeConfigService: kubeConfigService,
|
kubeClusterAccessService: kubeClusterAccessService,
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Use(middlewares.WithEndpoint(dataStore.Endpoint(), "id"))
|
h.Use(middlewares.WithEndpoint(dataStore.Endpoint(), "id"))
|
||||||
|
@ -104,10 +101,20 @@ func (handler *Handler) getHelmClusterAccess(r *http.Request) (*options.Kubernet
|
||||||
return nil, &httperror.HandlerError{http.StatusUnauthorized, "Unauthorized", err}
|
return nil, &httperror.HandlerError{http.StatusUnauthorized, "Unauthorized", err}
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeConfigInternal := handler.kubeConfigService.GetKubeConfigInternal(endpoint.ID, bearerToken)
|
sslSettings, err := handler.dataStore.SSLSettings().Settings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
|
||||||
|
}
|
||||||
|
|
||||||
|
hostURL := "localhost"
|
||||||
|
if !sslSettings.SelfSigned {
|
||||||
|
hostURL = strings.Split(r.Host, ":")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeConfigInternal := handler.kubeClusterAccessService.GetData(hostURL, endpoint.ID)
|
||||||
return &options.KubernetesClusterAccess{
|
return &options.KubernetesClusterAccess{
|
||||||
ClusterServerURL: kubeConfigInternal.ClusterServerURL,
|
ClusterServerURL: kubeConfigInternal.ClusterServerURL,
|
||||||
CertificateAuthorityFile: kubeConfigInternal.CertificateAuthorityFile,
|
CertificateAuthorityFile: kubeConfigInternal.CertificateAuthorityFile,
|
||||||
AuthToken: kubeConfigInternal.AuthToken,
|
AuthToken: bearerToken,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ func Test_helmDelete(t *testing.T) {
|
||||||
|
|
||||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||||
kubeConfigService := kubernetes.NewKubeConfigCAService("", "")
|
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeConfigService)
|
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||||
|
|
||||||
is.NotNil(h, "Handler should not fail")
|
is.NotNil(h, "Handler should not fail")
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ func Test_helmInstall(t *testing.T) {
|
||||||
|
|
||||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||||
kubeConfigService := kubernetes.NewKubeConfigCAService("", "")
|
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeConfigService)
|
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||||
|
|
||||||
is.NotNil(h, "Handler should not fail")
|
is.NotNil(h, "Handler should not fail")
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ func Test_helmList(t *testing.T) {
|
||||||
|
|
||||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||||
kubeConfigService := kubernetes.NewKubeConfigCAService("", "")
|
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeConfigService)
|
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||||
|
|
||||||
// Install a single chart. We expect to get these values back
|
// Install a single chart. We expect to get these values back
|
||||||
options := options.InstallOptions{Name: "nginx-1", Chart: "nginx", Namespace: "default"}
|
options := options.InstallOptions{Name: "nginx-1", Chart: "nginx", Namespace: "default"}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/portainer/portainer/api/kubernetes"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -17,21 +18,22 @@ import (
|
||||||
// Handler is the HTTP handler which will natively deal with to external environments(endpoints).
|
// Handler is the HTTP handler which will natively deal with to external environments(endpoints).
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
*mux.Router
|
*mux.Router
|
||||||
dataStore dataservices.DataStore
|
|
||||||
kubernetesClientFactory *cli.ClientFactory
|
|
||||||
authorizationService *authorization.Service
|
authorizationService *authorization.Service
|
||||||
JwtService dataservices.JWTService
|
dataStore dataservices.DataStore
|
||||||
BaseURL string
|
jwtService dataservices.JWTService
|
||||||
|
kubernetesClientFactory *cli.ClientFactory
|
||||||
|
kubeClusterAccessService kubernetes.KubeClusterAccessService
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHandler creates a handler to process pre-proxied requests to external APIs.
|
// NewHandler creates a handler to process pre-proxied requests to external APIs.
|
||||||
func NewHandler(bouncer *security.RequestBouncer, authorizationService *authorization.Service, dataStore dataservices.DataStore, kubernetesClientFactory *cli.ClientFactory, baseURL string) *Handler {
|
func NewHandler(bouncer *security.RequestBouncer, authorizationService *authorization.Service, dataStore dataservices.DataStore, jwtService dataservices.JWTService, kubeClusterAccessService kubernetes.KubeClusterAccessService, kubernetesClientFactory *cli.ClientFactory) *Handler {
|
||||||
h := &Handler{
|
h := &Handler{
|
||||||
Router: mux.NewRouter(),
|
Router: mux.NewRouter(),
|
||||||
dataStore: dataStore,
|
|
||||||
kubernetesClientFactory: kubernetesClientFactory,
|
|
||||||
authorizationService: authorizationService,
|
authorizationService: authorizationService,
|
||||||
BaseURL: baseURL,
|
dataStore: dataStore,
|
||||||
|
jwtService: jwtService,
|
||||||
|
kubeClusterAccessService: kubeClusterAccessService,
|
||||||
|
kubernetesClientFactory: kubernetesClientFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeRouter := h.PathPrefix("/kubernetes").Subrouter()
|
kubeRouter := h.PathPrefix("/kubernetes").Subrouter()
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (handler *Handler) getKubernetesConfig(w http.ResponseWriter, r *http.Reque
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access environment", err}
|
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access environment", err}
|
||||||
}
|
}
|
||||||
bearerToken, err := handler.JwtService.GenerateTokenForKubeconfig(tokenData)
|
bearerToken, err := handler.jwtService.GenerateTokenForKubeconfig(tokenData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to generate JWT token", err}
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to generate JWT token", err}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD
|
||||||
instanceID := handler.kubernetesClientFactory.GetInstanceID()
|
instanceID := handler.kubernetesClientFactory.GetInstanceID()
|
||||||
serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID)
|
serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID)
|
||||||
|
|
||||||
configClusters[idx] = buildCluster(r, handler.BaseURL, endpoint)
|
configClusters[idx] = handler.buildCluster(r, endpoint)
|
||||||
configContexts[idx] = buildContext(serviceAccountName, endpoint)
|
configContexts[idx] = buildContext(serviceAccountName, endpoint)
|
||||||
if !authInfosSet[serviceAccountName] {
|
if !authInfosSet[serviceAccountName] {
|
||||||
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccountName, bearerToken))
|
configAuthInfos = append(configAuthInfos, buildAuthInfo(serviceAccountName, bearerToken))
|
||||||
|
@ -144,15 +144,13 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildCluster(r *http.Request, baseURL string, endpoint portainer.Endpoint) clientV1.NamedCluster {
|
func (handler *Handler) buildCluster(r *http.Request, endpoint portainer.Endpoint) clientV1.NamedCluster {
|
||||||
if baseURL != "/" {
|
hostURL := strings.Split(r.Host, ":")[0]
|
||||||
baseURL = fmt.Sprintf("/%s/", strings.Trim(baseURL, "/"))
|
kubeConfigInternal := handler.kubeClusterAccessService.GetData(hostURL, endpoint.ID)
|
||||||
}
|
|
||||||
proxyURL := fmt.Sprintf("https://%s%sapi/endpoints/%d/kubernetes", r.Host, baseURL, endpoint.ID)
|
|
||||||
return clientV1.NamedCluster{
|
return clientV1.NamedCluster{
|
||||||
Name: buildClusterName(endpoint.Name),
|
Name: buildClusterName(endpoint.Name),
|
||||||
Cluster: clientV1.Cluster{
|
Cluster: clientV1.Cluster{
|
||||||
Server: proxyURL,
|
Server: kubeConfigInternal.ClusterServerURL,
|
||||||
InsecureSkipTLSVerify: true,
|
InsecureSkipTLSVerify: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ type Server struct {
|
||||||
SwarmStackManager portainer.SwarmStackManager
|
SwarmStackManager portainer.SwarmStackManager
|
||||||
ProxyManager *proxy.Manager
|
ProxyManager *proxy.Manager
|
||||||
KubernetesTokenCacheManager *kubernetes.TokenCacheManager
|
KubernetesTokenCacheManager *kubernetes.TokenCacheManager
|
||||||
KubeConfigService k8s.KubeConfigService
|
KubeClusterAccessService k8s.KubeClusterAccessService
|
||||||
Handler *handler.Handler
|
Handler *handler.Handler
|
||||||
SSLService *ssl.Service
|
SSLService *ssl.Service
|
||||||
DockerClientFactory *docker.ClientFactory
|
DockerClientFactory *docker.ClientFactory
|
||||||
|
@ -98,7 +98,6 @@ type Server struct {
|
||||||
ShutdownCtx context.Context
|
ShutdownCtx context.Context
|
||||||
ShutdownTrigger context.CancelFunc
|
ShutdownTrigger context.CancelFunc
|
||||||
StackDeployer stackdeployer.StackDeployer
|
StackDeployer stackdeployer.StackDeployer
|
||||||
BaseURL string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the HTTP server
|
// Start starts the HTTP server
|
||||||
|
@ -175,12 +174,11 @@ func (server *Server) Start() error {
|
||||||
endpointProxyHandler.ProxyManager = server.ProxyManager
|
endpointProxyHandler.ProxyManager = server.ProxyManager
|
||||||
endpointProxyHandler.ReverseTunnelService = server.ReverseTunnelService
|
endpointProxyHandler.ReverseTunnelService = server.ReverseTunnelService
|
||||||
|
|
||||||
var kubernetesHandler = kubehandler.NewHandler(requestBouncer, server.AuthorizationService, server.DataStore, server.KubernetesClientFactory, server.BaseURL)
|
var kubernetesHandler = kubehandler.NewHandler(requestBouncer, server.AuthorizationService, server.DataStore, server.JWTService, server.KubeClusterAccessService, server.KubernetesClientFactory)
|
||||||
kubernetesHandler.JwtService = server.JWTService
|
|
||||||
|
|
||||||
var fileHandler = file.NewHandler(filepath.Join(server.AssetsPath, "public"))
|
var fileHandler = file.NewHandler(filepath.Join(server.AssetsPath, "public"))
|
||||||
|
|
||||||
var endpointHelmHandler = helm.NewHandler(requestBouncer, server.DataStore, server.JWTService, server.KubernetesDeployer, server.HelmPackageManager, server.KubeConfigService)
|
var endpointHelmHandler = helm.NewHandler(requestBouncer, server.DataStore, server.JWTService, server.KubernetesDeployer, server.HelmPackageManager, server.KubeClusterAccessService)
|
||||||
|
|
||||||
var helmTemplatesHandler = helm.NewTemplateHandler(requestBouncer, server.HelmPackageManager)
|
var helmTemplatesHandler = helm.NewTemplateHandler(requestBouncer, server.HelmPackageManager)
|
||||||
|
|
||||||
|
|
|
@ -7,26 +7,27 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KubeConfigService represents a service that is responsible for handling kubeconfig operations
|
// KubeClusterAccessService represents a service that is responsible for centralizing kube cluster access data
|
||||||
type KubeConfigService interface {
|
type KubeClusterAccessService interface {
|
||||||
IsSecure() bool
|
IsSecure() bool
|
||||||
GetKubeConfigInternal(endpointId portainer.EndpointID, authToken string) kubernetesClusterAccess
|
GetData(hostURL string, endpointId portainer.EndpointID) kubernetesClusterAccessData
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
||||||
type kubernetesClusterAccess struct {
|
type kubernetesClusterAccessData struct {
|
||||||
ClusterServerURL string `example:"https://mycompany.k8s.com"`
|
ClusterServerURL string `example:"https://mycompany.k8s.com"`
|
||||||
CertificateAuthorityFile string `example:"/data/tls/localhost.crt"`
|
CertificateAuthorityFile string `example:"/data/tls/localhost.crt"`
|
||||||
CertificateAuthorityData string `example:"MIIC5TCCAc2gAwIBAgIJAJ+...+xuhOaFXwQ=="`
|
CertificateAuthorityData string `example:"MIIC5TCCAc2gAwIBAgIJAJ+...+xuhOaFXwQ=="`
|
||||||
AuthToken string `example:"ey..."`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type kubeConfigCAService struct {
|
type kubeClusterAccessService struct {
|
||||||
|
baseURL string
|
||||||
httpsBindAddr string
|
httpsBindAddr string
|
||||||
certificateAuthorityFile string
|
certificateAuthorityFile string
|
||||||
certificateAuthorityData string
|
certificateAuthorityData string
|
||||||
|
@ -39,14 +40,15 @@ var (
|
||||||
errTLSCertValidation = errors.New("failed to parse tls certificate")
|
errTLSCertValidation = errors.New("failed to parse tls certificate")
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewKubeConfigCAService encapsulates generation of core KubeConfig data
|
// NewKubeClusterAccessService creates a new instance of a KubeClusterAccessService
|
||||||
func NewKubeConfigCAService(httpsBindAddr string, tlsCertPath string) KubeConfigService {
|
func NewKubeClusterAccessService(baseURL, httpsBindAddr, tlsCertPath string) KubeClusterAccessService {
|
||||||
certificateAuthorityData, err := getCertificateAuthorityData(tlsCertPath)
|
certificateAuthorityData, err := getCertificateAuthorityData(tlsCertPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[DEBUG] [internal,kubeconfig] [message: %s, generated KubeConfig will be insecure]", err.Error())
|
log.Printf("[DEBUG] [internal,kubeconfig] [message: %s, generated KubeConfig will be insecure]", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &kubeConfigCAService{
|
return &kubeClusterAccessService{
|
||||||
|
baseURL: baseURL,
|
||||||
httpsBindAddr: httpsBindAddr,
|
httpsBindAddr: httpsBindAddr,
|
||||||
certificateAuthorityFile: tlsCertPath,
|
certificateAuthorityFile: tlsCertPath,
|
||||||
certificateAuthorityData: certificateAuthorityData,
|
certificateAuthorityData: certificateAuthorityData,
|
||||||
|
@ -82,23 +84,27 @@ func getCertificateAuthorityData(tlsCertPath string) (string, error) {
|
||||||
// this is based on the fact that we can successfully extract `certificateAuthorityData` from
|
// this is based on the fact that we can successfully extract `certificateAuthorityData` from
|
||||||
// certificate file at `tlsCertPath`. If we can successfully extract `certificateAuthorityData`,
|
// certificate file at `tlsCertPath`. If we can successfully extract `certificateAuthorityData`,
|
||||||
// then this will be used as `certificate-authority-data` attribute in a generated KubeConfig.
|
// then this will be used as `certificate-authority-data` attribute in a generated KubeConfig.
|
||||||
func (kccas *kubeConfigCAService) IsSecure() bool {
|
func (service *kubeClusterAccessService) IsSecure() bool {
|
||||||
return kccas.certificateAuthorityData != ""
|
return service.certificateAuthorityData != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKubeConfigInternal returns K8s cluster access details for the specified environment(endpoint).
|
// GetData returns K8s cluster access details for the specified environment(endpoint).
|
||||||
// On startup, portainer generates a certificate against localhost at specified `httpsBindAddr` port, hence
|
|
||||||
// the kubeconfig generated should only be utilised by internal portainer binaries as the `ClusterServerURL`
|
|
||||||
// points to the internally accessible `https` based `localhost` address.
|
|
||||||
// The struct can be used to:
|
// The struct can be used to:
|
||||||
// - generate a kubeconfig file
|
// - generate a kubeconfig file
|
||||||
// - pass down params to binaries
|
// - pass down params to binaries
|
||||||
func (kccas *kubeConfigCAService) GetKubeConfigInternal(endpointId portainer.EndpointID, authToken string) kubernetesClusterAccess {
|
func (service *kubeClusterAccessService) GetData(hostURL string, endpointID portainer.EndpointID) kubernetesClusterAccessData {
|
||||||
clusterServerUrl := fmt.Sprintf("https://localhost%s/api/endpoints/%s/kubernetes", kccas.httpsBindAddr, fmt.Sprint(endpointId))
|
baseURL := service.baseURL
|
||||||
return kubernetesClusterAccess{
|
if baseURL != "/" {
|
||||||
ClusterServerURL: clusterServerUrl,
|
baseURL = fmt.Sprintf("/%s/", strings.Trim(baseURL, "/"))
|
||||||
CertificateAuthorityFile: kccas.certificateAuthorityFile,
|
}
|
||||||
CertificateAuthorityData: kccas.certificateAuthorityData,
|
|
||||||
AuthToken: authToken,
|
clusterURL := hostURL + service.httpsBindAddr + baseURL
|
||||||
|
|
||||||
|
clusterServerURL := fmt.Sprintf("https://%sapi/endpoints/%d/kubernetes", clusterURL, endpointID)
|
||||||
|
|
||||||
|
return kubernetesClusterAccessData{
|
||||||
|
ClusterServerURL: clusterServerURL,
|
||||||
|
CertificateAuthorityFile: service.certificateAuthorityFile,
|
||||||
|
CertificateAuthorityData: service.certificateAuthorityData,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -78,11 +78,11 @@ func Test_getCertificateAuthorityData(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeConfigService_IsSecure(t *testing.T) {
|
func TestKubeClusterAccessService_IsSecure(t *testing.T) {
|
||||||
is := assert.New(t)
|
is := assert.New(t)
|
||||||
|
|
||||||
t.Run("IsSecure should be false", func(t *testing.T) {
|
t.Run("IsSecure should be false", func(t *testing.T) {
|
||||||
kcs := NewKubeConfigCAService("", "")
|
kcs := NewKubeClusterAccessService("", "", "")
|
||||||
is.False(kcs.IsSecure(), "should be false if TLS cert not provided")
|
is.False(kcs.IsSecure(), "should be false if TLS cert not provided")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -90,39 +90,32 @@ func TestKubeConfigService_IsSecure(t *testing.T) {
|
||||||
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
|
||||||
kcs := NewKubeConfigCAService("", filePath)
|
kcs := NewKubeClusterAccessService("", "", filePath)
|
||||||
is.True(kcs.IsSecure(), "should be true if valid TLS cert (path and content) provided")
|
is.True(kcs.IsSecure(), "should be true if valid TLS cert (path and content) provided")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeConfigService_GetKubeConfigInternal(t *testing.T) {
|
func TestKubeClusterAccessService_GetKubeConfigInternal(t *testing.T) {
|
||||||
is := assert.New(t)
|
is := assert.New(t)
|
||||||
|
|
||||||
t.Run("GetKubeConfigInternal returns localhost address", func(t *testing.T) {
|
t.Run("GetData contains host address", func(t *testing.T) {
|
||||||
kcs := NewKubeConfigCAService("", "")
|
kcs := NewKubeClusterAccessService("/", "", "")
|
||||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
||||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://localhost"), "should contain localhost address")
|
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "https://mysite.com"), "should contain host address")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetKubeConfigInternal contains https bind address port", func(t *testing.T) {
|
t.Run("GetData contains environment proxy url", func(t *testing.T) {
|
||||||
kcs := NewKubeConfigCAService(":1010", "")
|
kcs := NewKubeClusterAccessService("/", "", "")
|
||||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
clusterAccessDetails := kcs.GetData("mysite.com", 100)
|
||||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, ":1010"), "should contain bind address port")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("GetKubeConfigInternal contains environment proxy url", func(t *testing.T) {
|
|
||||||
kcs := NewKubeConfigCAService("", "")
|
|
||||||
clusterAccessDetails := kcs.GetKubeConfigInternal(100, "some-token")
|
|
||||||
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "api/endpoints/100/kubernetes"), "should contain environment proxy url")
|
is.True(strings.Contains(clusterAccessDetails.ClusterServerURL, "api/endpoints/100/kubernetes"), "should contain environment proxy url")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetKubeConfigInternal returns insecure cluster access config", func(t *testing.T) {
|
t.Run("GetData returns insecure cluster access config", func(t *testing.T) {
|
||||||
kcs := NewKubeConfigCAService("", "")
|
kcs := NewKubeClusterAccessService("/", ":9443", "")
|
||||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
clusterAccessDetails := kcs.GetData("mysite.com", 1)
|
||||||
|
|
||||||
wantClusterAccessDetails := kubernetesClusterAccess{
|
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||||
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
ClusterServerURL: "https://mysite.com:9443/api/endpoints/1/kubernetes",
|
||||||
AuthToken: "some-token",
|
|
||||||
CertificateAuthorityFile: "",
|
CertificateAuthorityFile: "",
|
||||||
CertificateAuthorityData: "",
|
CertificateAuthorityData: "",
|
||||||
}
|
}
|
||||||
|
@ -130,16 +123,15 @@ func TestKubeConfigService_GetKubeConfigInternal(t *testing.T) {
|
||||||
is.Equal(clusterAccessDetails, wantClusterAccessDetails)
|
is.Equal(clusterAccessDetails, wantClusterAccessDetails)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetKubeConfigInternal returns secure cluster access config", func(t *testing.T) {
|
t.Run("GetData returns secure cluster access config", func(t *testing.T) {
|
||||||
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
filePath, teardown := createTempFile("valid-cert.crt", certData)
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
|
||||||
kcs := NewKubeConfigCAService("", filePath)
|
kcs := NewKubeClusterAccessService("/", "", filePath)
|
||||||
clusterAccessDetails := kcs.GetKubeConfigInternal(1, "some-token")
|
clusterAccessDetails := kcs.GetData("localhost", 1)
|
||||||
|
|
||||||
wantClusterAccessDetails := kubernetesClusterAccess{
|
wantClusterAccessDetails := kubernetesClusterAccessData{
|
||||||
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
ClusterServerURL: "https://localhost/api/endpoints/1/kubernetes",
|
||||||
AuthToken: "some-token",
|
|
||||||
CertificateAuthorityFile: filePath,
|
CertificateAuthorityFile: filePath,
|
||||||
CertificateAuthorityData: certDataString,
|
CertificateAuthorityData: certDataString,
|
||||||
}
|
}
|
Loading…
Reference in New Issue