1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-29 18:29:44 +02:00

fix(transport): portainer generated kubeconfig causes kubectl exec fail [R8S-430] (#929)

This commit is contained in:
Steven Kang 2025-07-24 13:11:13 +12:00 committed by GitHub
parent bba3751268
commit bdb2e2f417
12 changed files with 417 additions and 25 deletions

View file

@ -451,7 +451,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
snapshotService.Start() snapshotService.Start()
proxyManager.NewProxyFactory(dataStore, signatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService) proxyManager.NewProxyFactory(dataStore, signatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService, jwtService)
helmPackageManager, err := initHelmPackageManager() helmPackageManager, err := initHelmPackageManager()
if err != nil { if err != nil {

View file

@ -22,7 +22,7 @@ func TestEndpointDeleteEdgeGroupsConcurrently(t *testing.T) {
handler := NewHandler(testhelpers.NewTestRequestBouncer()) handler := NewHandler(testhelpers.NewTestRequestBouncer())
handler.DataStore = store handler.DataStore = store
handler.ProxyManager = proxy.NewManager(nil) handler.ProxyManager = proxy.NewManager(nil)
handler.ProxyManager.NewProxyFactory(nil, nil, nil, nil, nil, nil, nil, nil) handler.ProxyManager.NewProxyFactory(nil, nil, nil, nil, nil, nil, nil, nil, nil)
// Create all the environments and add them to the same edge group // Create all the environments and add them to the same edge group

View file

@ -24,11 +24,12 @@ type (
kubernetesTokenCacheManager *kubernetes.TokenCacheManager kubernetesTokenCacheManager *kubernetes.TokenCacheManager
gitService portainer.GitService gitService portainer.GitService
snapshotService portainer.SnapshotService snapshotService portainer.SnapshotService
jwtService portainer.JWTService
} }
) )
// NewProxyFactory returns a pointer to a new instance of a ProxyFactory // NewProxyFactory returns a pointer to a new instance of a ProxyFactory
func NewProxyFactory(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, tunnelService portainer.ReverseTunnelService, clientFactory *dockerclient.ClientFactory, kubernetesClientFactory *cli.ClientFactory, kubernetesTokenCacheManager *kubernetes.TokenCacheManager, gitService portainer.GitService, snapshotService portainer.SnapshotService) *ProxyFactory { func NewProxyFactory(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, tunnelService portainer.ReverseTunnelService, clientFactory *dockerclient.ClientFactory, kubernetesClientFactory *cli.ClientFactory, kubernetesTokenCacheManager *kubernetes.TokenCacheManager, gitService portainer.GitService, snapshotService portainer.SnapshotService, jwtService portainer.JWTService) *ProxyFactory {
return &ProxyFactory{ return &ProxyFactory{
dataStore: dataStore, dataStore: dataStore,
signatureService: signatureService, signatureService: signatureService,
@ -38,6 +39,7 @@ func NewProxyFactory(dataStore dataservices.DataStore, signatureService portaine
kubernetesTokenCacheManager: kubernetesTokenCacheManager, kubernetesTokenCacheManager: kubernetesTokenCacheManager,
gitService: gitService, gitService: gitService,
snapshotService: snapshotService, snapshotService: snapshotService,
jwtService: jwtService,
} }
} }

View file

@ -38,7 +38,7 @@ func (factory *ProxyFactory) newKubernetesLocalProxy(endpoint *portainer.Endpoin
return nil, err return nil, err
} }
transport, err := kubernetes.NewLocalTransport(tokenManager, endpoint, factory.kubernetesClientFactory, factory.dataStore) transport, err := kubernetes.NewLocalTransport(tokenManager, endpoint, factory.kubernetesClientFactory, factory.dataStore, factory.jwtService)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,7 +74,7 @@ func (factory *ProxyFactory) newKubernetesEdgeHTTPProxy(endpoint *portainer.Endp
endpointURL.Scheme = "http" endpointURL.Scheme = "http"
proxy := NewSingleHostReverseProxyWithHostHeader(endpointURL) proxy := NewSingleHostReverseProxyWithHostHeader(endpointURL)
proxy.Transport = kubernetes.NewEdgeTransport(factory.dataStore, factory.signatureService, factory.reverseTunnelService, endpoint, tokenManager, factory.kubernetesClientFactory) proxy.Transport = kubernetes.NewEdgeTransport(factory.dataStore, factory.signatureService, factory.reverseTunnelService, endpoint, tokenManager, factory.kubernetesClientFactory, factory.jwtService)
return proxy, nil return proxy, nil
} }
@ -105,7 +105,7 @@ func (factory *ProxyFactory) newKubernetesAgentHTTPSProxy(endpoint *portainer.En
} }
proxy := NewSingleHostReverseProxyWithHostHeader(remoteURL) proxy := NewSingleHostReverseProxyWithHostHeader(remoteURL)
proxy.Transport = kubernetes.NewAgentTransport(factory.signatureService, tlsConfig, tokenManager, endpoint, factory.kubernetesClientFactory, factory.dataStore) proxy.Transport = kubernetes.NewAgentTransport(factory.signatureService, tlsConfig, tokenManager, endpoint, factory.kubernetesClientFactory, factory.dataStore, factory.jwtService)
return proxy, nil return proxy, nil
} }

View file

@ -16,7 +16,7 @@ type agentTransport struct {
} }
// NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer agent // NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer agent
func NewAgentTransport(signatureService portainer.DigitalSignatureService, tlsConfig *tls.Config, tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore) *agentTransport { func NewAgentTransport(signatureService portainer.DigitalSignatureService, tlsConfig *tls.Config, tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore, jwtService portainer.JWTService) *agentTransport {
transport := &agentTransport{ transport := &agentTransport{
baseTransport: newBaseTransport( baseTransport: newBaseTransport(
&http.Transport{ &http.Transport{
@ -26,6 +26,7 @@ func NewAgentTransport(signatureService portainer.DigitalSignatureService, tlsCo
endpoint, endpoint,
k8sClientFactory, k8sClientFactory,
dataStore, dataStore,
jwtService,
), ),
signatureService: signatureService, signatureService: signatureService,
} }

View file

@ -16,7 +16,7 @@ type edgeTransport struct {
} }
// NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer Edge agent // NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer Edge agent
func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, endpoint *portainer.Endpoint, tokenManager *tokenManager, k8sClientFactory *cli.ClientFactory) *edgeTransport { func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, endpoint *portainer.Endpoint, tokenManager *tokenManager, k8sClientFactory *cli.ClientFactory, jwtService portainer.JWTService) *edgeTransport {
transport := &edgeTransport{ transport := &edgeTransport{
reverseTunnelService: reverseTunnelService, reverseTunnelService: reverseTunnelService,
signatureService: signatureService, signatureService: signatureService,
@ -26,6 +26,7 @@ func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portain
endpoint, endpoint,
k8sClientFactory, k8sClientFactory,
dataStore, dataStore,
jwtService,
), ),
} }

View file

@ -14,7 +14,7 @@ type localTransport struct {
} }
// NewLocalTransport returns a new transport that can be used to send requests to the local Kubernetes API // NewLocalTransport returns a new transport that can be used to send requests to the local Kubernetes API
func NewLocalTransport(tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore) (*localTransport, error) { func NewLocalTransport(tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore, jwtService portainer.JWTService) (*localTransport, error) {
config, err := crypto.CreateTLSConfigurationFromBytes(nil, nil, nil, true, true) config, err := crypto.CreateTLSConfigurationFromBytes(nil, nil, nil, true, true)
if err != nil { if err != nil {
return nil, err return nil, err
@ -29,6 +29,7 @@ func NewLocalTransport(tokenManager *tokenManager, endpoint *portainer.Endpoint,
endpoint, endpoint,
k8sClientFactory, k8sClientFactory,
dataStore, dataStore,
jwtService,
), ),
} }

View file

@ -2,12 +2,18 @@ package kubernetes
import ( import (
"net/http" "net/http"
"strings"
) )
func (transport *baseTransport) proxyPodsRequest(request *http.Request, namespace, requestPath string) (*http.Response, error) { func (transport *baseTransport) proxyPodsRequest(request *http.Request, namespace string) (*http.Response, error) {
if request.Method == http.MethodDelete { if request.Method == http.MethodDelete {
transport.refreshRegistry(request, namespace) transport.refreshRegistry(request, namespace)
} }
if request.Method == http.MethodPost && strings.Contains(request.URL.Path, "/exec") {
if err := transport.addTokenForExec(request); err != nil {
return nil, err
}
}
return transport.executeKubernetesRequest(request) return transport.executeKubernetesRequest(request)
} }

View file

@ -26,15 +26,17 @@ type baseTransport struct {
endpoint *portainer.Endpoint endpoint *portainer.Endpoint
k8sClientFactory *cli.ClientFactory k8sClientFactory *cli.ClientFactory
dataStore dataservices.DataStore dataStore dataservices.DataStore
jwtService portainer.JWTService
} }
func newBaseTransport(httpTransport *http.Transport, tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore) *baseTransport { func newBaseTransport(httpTransport *http.Transport, tokenManager *tokenManager, endpoint *portainer.Endpoint, k8sClientFactory *cli.ClientFactory, dataStore dataservices.DataStore, jwtService portainer.JWTService) *baseTransport {
return &baseTransport{ return &baseTransport{
httpTransport: httpTransport, httpTransport: httpTransport,
tokenManager: tokenManager, tokenManager: tokenManager,
endpoint: endpoint, endpoint: endpoint,
k8sClientFactory: k8sClientFactory, k8sClientFactory: k8sClientFactory,
dataStore: dataStore, dataStore: dataStore,
jwtService: jwtService,
} }
} }
@ -82,7 +84,7 @@ func (transport *baseTransport) proxyNamespacedRequest(request *http.Request, fu
switch { switch {
case strings.HasPrefix(requestPath, "pods"): case strings.HasPrefix(requestPath, "pods"):
return transport.proxyPodsRequest(request, namespace, requestPath) return transport.proxyPodsRequest(request, namespace)
case strings.HasPrefix(requestPath, "deployments"): case strings.HasPrefix(requestPath, "deployments"):
return transport.proxyDeploymentsRequest(request, namespace, requestPath) return transport.proxyDeploymentsRequest(request, namespace, requestPath)
case requestPath == "" && request.Method == "DELETE": case requestPath == "" && request.Method == "DELETE":
@ -92,6 +94,23 @@ func (transport *baseTransport) proxyNamespacedRequest(request *http.Request, fu
} }
} }
// addTokenForExec injects a kubeconfig token into the request header
// this is only used with kubeconfig for kubectl exec requests
func (transport *baseTransport) addTokenForExec(request *http.Request) error {
tokenData, err := security.RetrieveTokenData(request)
if err != nil {
return err
}
token, err := transport.jwtService.GenerateTokenForKubeconfig(tokenData)
if err != nil {
return err
}
request.Header.Set("Authorization", "Bearer "+token)
return nil
}
func (transport *baseTransport) executeKubernetesRequest(request *http.Request) (*http.Response, error) { func (transport *baseTransport) executeKubernetesRequest(request *http.Request) (*http.Response, error) {
resp, err := transport.httpTransport.RoundTrip(request) resp, err := transport.httpTransport.RoundTrip(request)

View file

@ -0,0 +1,359 @@
package kubernetes
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/jwt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// MockJWTService implements portainer.JWTService for testing
type MockJWTService struct {
generateTokenFunc func(data *portainer.TokenData) (string, error)
}
func (m *MockJWTService) GenerateToken(data *portainer.TokenData) (string, time.Time, error) {
if m.generateTokenFunc != nil {
token, err := m.generateTokenFunc(data)
return token, time.Now().Add(24 * time.Hour), err
}
return "mock-token", time.Now().Add(24 * time.Hour), nil
}
func (m *MockJWTService) GenerateTokenForKubeconfig(data *portainer.TokenData) (string, error) {
if m.generateTokenFunc != nil {
return m.generateTokenFunc(data)
}
return "mock-kubeconfig-token", nil
}
func (m *MockJWTService) ParseAndVerifyToken(token string) (*portainer.TokenData, string, time.Time, error) {
return &portainer.TokenData{ID: 1, Username: "mock", Role: portainer.AdministratorRole}, "mock-id", time.Now().Add(24 * time.Hour), nil
}
func (m *MockJWTService) SetUserSessionDuration(userSessionDuration time.Duration) {
// Mock implementation - not used in tests
}
func TestBaseTransport_AddTokenForExec(t *testing.T) {
// Setup test store and JWT service
_, store := datastore.MustNewTestStore(t, true, false)
// Create test users
adminUser := &portainer.User{
ID: 1,
Username: "admin",
Role: portainer.AdministratorRole,
}
err := store.User().Create(adminUser)
require.NoError(t, err)
standardUser := &portainer.User{
ID: 2,
Username: "standard",
Role: portainer.StandardUserRole,
}
err = store.User().Create(standardUser)
require.NoError(t, err)
// Create JWT service
jwtService, err := jwt.NewService("24h", store)
require.NoError(t, err)
// Create base transport
transport := &baseTransport{
jwtService: jwtService,
}
tests := []struct {
name string
tokenData *portainer.TokenData
setupRequest func(*http.Request) *http.Request
expectError bool
errorMsg string
expectPanic bool
verifyResponse func(*testing.T, *http.Request, *portainer.TokenData)
}{
{
name: "admin user - successful token generation",
tokenData: &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
},
setupRequest: func(req *http.Request) *http.Request {
return req.WithContext(security.StoreTokenData(req, &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
}))
},
expectError: false,
verifyResponse: func(t *testing.T, req *http.Request, tokenData *portainer.TokenData) {
authHeader := req.Header.Get("Authorization")
assert.NotEmpty(t, authHeader)
assert.True(t, strings.HasPrefix(authHeader, "Bearer "))
token := authHeader[7:] // Remove "Bearer " prefix
parsedTokenData, _, _, err := jwtService.ParseAndVerifyToken(token)
assert.NoError(t, err)
assert.Equal(t, tokenData.ID, parsedTokenData.ID)
assert.Equal(t, tokenData.Username, parsedTokenData.Username)
assert.Equal(t, tokenData.Role, parsedTokenData.Role)
},
},
{
name: "standard user - successful token generation",
tokenData: &portainer.TokenData{
ID: standardUser.ID,
Username: standardUser.Username,
Role: standardUser.Role,
},
setupRequest: func(req *http.Request) *http.Request {
return req.WithContext(security.StoreTokenData(req, &portainer.TokenData{
ID: standardUser.ID,
Username: standardUser.Username,
Role: standardUser.Role,
}))
},
expectError: false,
verifyResponse: func(t *testing.T, req *http.Request, tokenData *portainer.TokenData) {
authHeader := req.Header.Get("Authorization")
assert.NotEmpty(t, authHeader)
assert.True(t, strings.HasPrefix(authHeader, "Bearer "))
token := authHeader[7:] // Remove "Bearer " prefix
parsedTokenData, _, _, err := jwtService.ParseAndVerifyToken(token)
assert.NoError(t, err)
assert.Equal(t, tokenData.ID, parsedTokenData.ID)
assert.Equal(t, tokenData.Username, parsedTokenData.Username)
assert.Equal(t, tokenData.Role, parsedTokenData.Role)
},
},
{
name: "request without token data in context",
tokenData: nil,
setupRequest: func(req *http.Request) *http.Request {
return req // Don't add token data to context
},
expectError: true,
errorMsg: "Unable to find JWT data in request context",
},
{
name: "request with nil token data",
tokenData: nil,
setupRequest: func(req *http.Request) *http.Request {
return req.WithContext(security.StoreTokenData(req, nil))
},
expectPanic: true,
},
{
name: "JWT service failure",
tokenData: &portainer.TokenData{
ID: 1,
Username: "test",
Role: portainer.AdministratorRole,
},
setupRequest: func(req *http.Request) *http.Request {
return req.WithContext(security.StoreTokenData(req, &portainer.TokenData{
ID: 1,
Username: "test",
Role: portainer.AdministratorRole,
}))
},
expectPanic: true,
},
{
name: "verify authorization header format",
tokenData: &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
},
setupRequest: func(req *http.Request) *http.Request {
return req.WithContext(security.StoreTokenData(req, &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
}))
},
expectError: false,
verifyResponse: func(t *testing.T, req *http.Request, tokenData *portainer.TokenData) {
authHeader := req.Header.Get("Authorization")
assert.NotEmpty(t, authHeader)
assert.True(t, strings.HasPrefix(authHeader, "Bearer "))
token := authHeader[7:] // Remove "Bearer " prefix
assert.NotEmpty(t, token)
assert.Greater(t, len(token), 0, "Token should not be empty")
},
},
{
name: "verify header is overwritten on subsequent calls",
tokenData: &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
},
setupRequest: func(req *http.Request) *http.Request {
req = req.WithContext(security.StoreTokenData(req, &portainer.TokenData{
ID: adminUser.ID,
Username: adminUser.Username,
Role: adminUser.Role,
}))
// Set an existing Authorization header
req.Header.Set("Authorization", "Bearer old-token")
return req
},
expectError: false,
verifyResponse: func(t *testing.T, req *http.Request, tokenData *portainer.TokenData) {
authHeader := req.Header.Get("Authorization")
assert.NotEqual(t, "Bearer old-token", authHeader)
assert.True(t, strings.HasPrefix(authHeader, "Bearer "))
token := authHeader[7:] // Remove "Bearer " prefix
parsedTokenData, _, _, err := jwtService.ParseAndVerifyToken(token)
assert.NoError(t, err)
assert.Equal(t, tokenData.ID, parsedTokenData.ID)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create request
request := httptest.NewRequest("GET", "/", nil)
request = tt.setupRequest(request)
// Determine which transport to use based on test case
var testTransport *baseTransport
if tt.name == "JWT service failure" {
testTransport = &baseTransport{
jwtService: nil,
}
} else {
testTransport = transport
}
// Call the function
if tt.expectPanic {
assert.Panics(t, func() {
_ = testTransport.addTokenForExec(request)
})
return
}
err := testTransport.addTokenForExec(request)
// Check results
if tt.expectError {
assert.Error(t, err)
if tt.errorMsg != "" {
assert.Contains(t, err.Error(), tt.errorMsg)
}
} else {
assert.NoError(t, err)
if tt.verifyResponse != nil {
tt.verifyResponse(t, request, tt.tokenData)
}
}
})
}
}
func TestBaseTransport_AddTokenForExec_Integration(t *testing.T) {
// Create a test HTTP server to capture requests
var capturedRequest *http.Request
var capturedHeaders http.Header
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
capturedRequest = r
capturedHeaders = r.Header.Clone()
w.WriteHeader(http.StatusOK)
w.Write([]byte("success"))
}))
defer testServer.Close()
// Create mock JWT service
mockJWTService := &MockJWTService{
generateTokenFunc: func(data *portainer.TokenData) (string, error) {
return "mock-token-" + data.Username, nil
},
}
// Create base transport
transport := &baseTransport{
httpTransport: &http.Transport{},
jwtService: mockJWTService,
}
tests := []struct {
name string
tokenData *portainer.TokenData
requestPath string
expectedToken string
}{
{
name: "admin user exec request",
tokenData: &portainer.TokenData{
ID: 1,
Username: "admin",
Role: portainer.AdministratorRole,
},
requestPath: "/api/endpoints/1/kubernetes/api/v1/namespaces/default/pods/test-pod/exec",
expectedToken: "mock-token-admin",
},
{
name: "standard user exec request",
tokenData: &portainer.TokenData{
ID: 2,
Username: "standard",
Role: portainer.StandardUserRole,
},
requestPath: "/api/endpoints/1/kubernetes/api/v1/namespaces/default/pods/test-pod/exec",
expectedToken: "mock-token-standard",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Reset captured data
capturedRequest = nil
capturedHeaders = nil
// Create request to the test server
request, err := http.NewRequest("POST", testServer.URL+tt.requestPath, strings.NewReader(""))
require.NoError(t, err)
// Add token data to request context
request = request.WithContext(security.StoreTokenData(request, tt.tokenData))
// Call proxyPodsRequest which triggers addTokenForExec for POST /exec requests
resp, err := transport.proxyPodsRequest(request, "default")
require.NoError(t, err)
defer resp.Body.Close()
// Verify the response
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Verify the request was captured
assert.NotNil(t, capturedRequest)
assert.Equal(t, "POST", capturedRequest.Method)
assert.Equal(t, tt.requestPath, capturedRequest.URL.Path)
// Verify the authorization header was set correctly
capturedAuthHeader := capturedHeaders.Get("Authorization")
assert.NotEmpty(t, capturedAuthHeader)
assert.True(t, strings.HasPrefix(capturedAuthHeader, "Bearer "))
assert.Equal(t, "Bearer "+tt.expectedToken, capturedAuthHeader)
})
}
}

View file

@ -9,17 +9,20 @@ import (
// Note that we discard any non-canonical headers by design // Note that we discard any non-canonical headers by design
var allowedHeaders = map[string]struct{}{ var allowedHeaders = map[string]struct{}{
"Accept": {}, "Accept": {},
"Accept-Encoding": {}, "Accept-Encoding": {},
"Accept-Language": {}, "Accept-Language": {},
"Cache-Control": {}, "Cache-Control": {},
"Content-Length": {}, "Connection": {},
"Content-Type": {}, "Content-Length": {},
"Private-Token": {}, "Content-Type": {},
"User-Agent": {}, "Private-Token": {},
"X-Portaineragent-Target": {}, "Upgrade": {},
"X-Portainer-Volumename": {}, "User-Agent": {},
"X-Registry-Auth": {}, "X-Portaineragent-Target": {},
"X-Portainer-Volumename": {},
"X-Registry-Auth": {},
"X-Stream-Protocol-Version": {},
} }
// newSingleHostReverseProxyWithHostHeader is based on NewSingleHostReverseProxy // newSingleHostReverseProxyWithHostHeader is based on NewSingleHostReverseProxy

View file

@ -32,8 +32,8 @@ func NewManager(kubernetesClientFactory *cli.ClientFactory) *Manager {
} }
} }
func (manager *Manager) NewProxyFactory(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, tunnelService portainer.ReverseTunnelService, clientFactory *dockerclient.ClientFactory, kubernetesClientFactory *cli.ClientFactory, kubernetesTokenCacheManager *kubernetes.TokenCacheManager, gitService portainer.GitService, snapshotService portainer.SnapshotService) { func (manager *Manager) NewProxyFactory(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, tunnelService portainer.ReverseTunnelService, clientFactory *dockerclient.ClientFactory, kubernetesClientFactory *cli.ClientFactory, kubernetesTokenCacheManager *kubernetes.TokenCacheManager, gitService portainer.GitService, snapshotService portainer.SnapshotService, jwtService portainer.JWTService) {
manager.proxyFactory = factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService) manager.proxyFactory = factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService, jwtService)
} }
// CreateAndRegisterEndpointProxy creates a new HTTP reverse proxy based on environment(endpoint) properties and adds it to the registered proxies. // CreateAndRegisterEndpointProxy creates a new HTTP reverse proxy based on environment(endpoint) properties and adds it to the registered proxies.