Compare commits
4 Commits
vault/deve
...
fix/EE-661
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d26ebf6f8e | ||
|
|
b569ea7ef3 | ||
|
|
f797d7f3dd | ||
|
|
c80b8ff440 |
@@ -8,6 +8,8 @@ linters:
|
||||
- govet
|
||||
- errorlint
|
||||
- exportloopref
|
||||
- staticcheck
|
||||
|
||||
linters-settings:
|
||||
depguard:
|
||||
rules:
|
||||
|
||||
@@ -3,11 +3,9 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/apikey"
|
||||
@@ -455,6 +453,9 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
|
||||
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
|
||||
kubernetesClientFactory, err := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, dataStore, instanceID, *flags.AddrHTTPS, settings.UserSessionTimeout)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing kubernetes client factory")
|
||||
}
|
||||
|
||||
authorizationService := authorization.NewService(dataStore)
|
||||
authorizationService.K8sClientFactory = kubernetesClientFactory
|
||||
@@ -631,8 +632,6 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
configureLogger()
|
||||
setLoggingMode("PRETTY")
|
||||
|
||||
|
||||
@@ -146,7 +146,9 @@ func (m *Migrator) updateEdgeStackStatusForDB100() error {
|
||||
edgeStack.Status[environmentID] = environmentStatus
|
||||
}
|
||||
|
||||
err = m.edgeStackService.UpdateEdgeStack(edgeStack.ID, &edgeStack)
|
||||
err = m.edgeStackService.UpdateEdgeStackFunc(edgeStack.ID, func(stack *portainer.EdgeStack) {
|
||||
stack.Status = edgeStack.Status
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
endpointAdministratorRole.Authorizations = authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole()
|
||||
|
||||
err = m.roleService.Update(endpointAdministratorRole.ID, endpointAdministratorRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
helpDeskRole, err := m.roleService.Read(portainer.RoleID(2))
|
||||
if err != nil {
|
||||
@@ -65,6 +68,9 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
helpDeskRole.Authorizations = authorization.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.Update(helpDeskRole.ID, helpDeskRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
standardUserRole, err := m.roleService.Read(portainer.RoleID(3))
|
||||
if err != nil {
|
||||
@@ -74,6 +80,9 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
standardUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.Update(standardUserRole.ID, standardUserRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
readOnlyUserRole, err := m.roleService.Read(portainer.RoleID(4))
|
||||
if err != nil {
|
||||
|
||||
@@ -93,7 +93,9 @@ func (m *Migrator) updateEdgeStackStatusForDB80() error {
|
||||
edgeStack.Status[endpointId] = status
|
||||
}
|
||||
|
||||
err = m.edgeStackService.UpdateEdgeStack(edgeStack.ID, &edgeStack)
|
||||
err = m.edgeStackService.UpdateEdgeStackFunc(edgeStack.ID, func(stack *portainer.EdgeStack) {
|
||||
stack.Status = edgeStack.Status
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func Test_ClonePublicRepository_Shallow(t *testing.T) {
|
||||
t.Logf("Cloning into %s", dir)
|
||||
err := service.CloneRepository(dir, repositoryURL, referenceName, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, getCommitHistoryLength(t, err, dir), "cloned repo has incorrect depth")
|
||||
assert.Equal(t, 1, getCommitHistoryLength(t, dir), "cloned repo has incorrect depth")
|
||||
}
|
||||
|
||||
func Test_ClonePublicRepository_NoGitDirectory(t *testing.T) {
|
||||
@@ -76,7 +76,7 @@ func Test_cloneRepository(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, getCommitHistoryLength(t, err, dir), "cloned repo has incorrect depth")
|
||||
assert.Equal(t, 4, getCommitHistoryLength(t, dir), "cloned repo has incorrect depth")
|
||||
}
|
||||
|
||||
func Test_latestCommitID(t *testing.T) {
|
||||
@@ -91,7 +91,7 @@ func Test_latestCommitID(t *testing.T) {
|
||||
assert.Equal(t, "68dcaa7bd452494043c64252ab90db0f98ecf8d2", id)
|
||||
}
|
||||
|
||||
func getCommitHistoryLength(t *testing.T, err error, dir string) int {
|
||||
func getCommitHistoryLength(t *testing.T, dir string) int {
|
||||
repo, err := git.PlainOpen(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("can't open a git repo at %s with error %v", dir, err)
|
||||
|
||||
@@ -200,7 +200,7 @@ func (handler *Handler) syncUserTeamsWithLDAPGroups(user *portainer.User, settin
|
||||
|
||||
func teamExists(teamName string, ldapGroups []string) bool {
|
||||
for _, group := range ldapGroups {
|
||||
if strings.ToLower(group) == strings.ToLower(teamName) {
|
||||
if strings.EqualFold(group, teamName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/rs/zerolog/log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
@@ -107,30 +105,17 @@ func hijackAttachStartOperation(
|
||||
attachID string,
|
||||
token string,
|
||||
) error {
|
||||
dial, err := initDial(endpoint)
|
||||
conn, err := initDial(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When we set up a TCP connection for hijack, there could be long periods
|
||||
// of inactivity (a long running command with no output) that in certain
|
||||
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||
if tcpConn, ok := dial.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
|
||||
httpConn := httputil.NewClientConn(dial, nil)
|
||||
defer httpConn.Close()
|
||||
|
||||
attachStartRequest, err := createAttachStartRequest(attachID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hijackRequest(websocketConn, httpConn, attachStartRequest, token)
|
||||
return hijackRequest(websocketConn, conn, attachStartRequest, token)
|
||||
}
|
||||
|
||||
func createAttachStartRequest(attachID string) (*http.Request, error) {
|
||||
|
||||
@@ -2,9 +2,7 @@ package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
@@ -102,7 +100,12 @@ func (handler *Handler) handleExecRequest(w http.ResponseWriter, r *http.Request
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer websocketConn.Close()
|
||||
|
||||
defer func() {
|
||||
// Wait 10s before firing the close event to give the user time to read the last message
|
||||
time.Sleep(10 * time.Second)
|
||||
websocketConn.Close()
|
||||
}()
|
||||
|
||||
return hijackExecStartOperation(websocketConn, params.endpoint, params.ID, tokenData.Token)
|
||||
}
|
||||
@@ -113,30 +116,17 @@ func hijackExecStartOperation(
|
||||
execID string,
|
||||
token string,
|
||||
) error {
|
||||
dial, err := initDial(endpoint)
|
||||
conn, err := initDial(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When we set up a TCP connection for hijack, there could be long periods
|
||||
// of inactivity (a long running command with no output) that in certain
|
||||
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||
if tcpConn, ok := dial.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
|
||||
httpConn := httputil.NewClientConn(dial, nil)
|
||||
defer httpConn.Close()
|
||||
|
||||
execStartRequest, err := createExecStartRequest(execID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hijackRequest(websocketConn, httpConn, execStartRequest, token)
|
||||
return hijackRequest(websocketConn, conn, execStartRequest, token)
|
||||
}
|
||||
|
||||
func createExecStartRequest(execID string) (*http.Request, error) {
|
||||
|
||||
@@ -1,50 +1,162 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/portainer/portainer/api/internal/logoutcontext"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// Time allowed to write a message to the peer
|
||||
writeWait = 10 * time.Second
|
||||
|
||||
// Send pings to peer with this period
|
||||
pingPeriod = 50 * time.Second
|
||||
)
|
||||
|
||||
func hijackRequest(
|
||||
websocketConn *websocket.Conn,
|
||||
httpConn *httputil.ClientConn,
|
||||
conn net.Conn,
|
||||
request *http.Request,
|
||||
token string,
|
||||
) error {
|
||||
// Server hijacks the connection, error 'connection closed' expected
|
||||
resp, err := httpConn.Do(request)
|
||||
if !errors.Is(err, httputil.ErrPersistEOF) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
resp.Body.Close()
|
||||
return fmt.Errorf("unable to upgrade to tcp, received %d", resp.StatusCode)
|
||||
}
|
||||
resp, err := sendHTTPRequest(conn, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tcpConn, brw := httpConn.Hijack()
|
||||
defer tcpConn.Close()
|
||||
// Check if the response status code indicates an upgrade (101 Switching Protocols)
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
return fmt.Errorf("unexpected response status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
errorChan := make(chan error, 1)
|
||||
go streamFromReaderToWebsocket(websocketConn, brw, errorChan)
|
||||
go streamFromWebsocketToWriter(websocketConn, tcpConn, errorChan)
|
||||
go readWebSocketToTCP(websocketConn, conn, errorChan)
|
||||
go writeTCPToWebSocket(websocketConn, conn, errorChan)
|
||||
|
||||
logoutCtx := logoutcontext.GetContext(token)
|
||||
|
||||
select {
|
||||
case <-logoutCtx.Done():
|
||||
return fmt.Errorf("Your session has been logged out.")
|
||||
case err = <-errorChan:
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) {
|
||||
return err
|
||||
}
|
||||
err = <-errorChan
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("session ended")
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendHTTPRequest sends an HTTP request over the provided net.Conn and parses the response.
|
||||
func sendHTTPRequest(conn net.Conn, req *http.Request) (*http.Response, error) {
|
||||
// Send the HTTP request to the server
|
||||
if err := req.Write(conn); err != nil {
|
||||
return nil, fmt.Errorf("error writing request: %w", err)
|
||||
}
|
||||
|
||||
// Read the response from the server
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading response: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func readWebSocketToTCP(websocketConn *websocket.Conn, tcpConn net.Conn, errorChan chan error) {
|
||||
for {
|
||||
messageType, p, err := websocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
log.Debug().Msgf("Unexpected close error: %v\n", err)
|
||||
}
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
if messageType == websocket.TextMessage || messageType == websocket.BinaryMessage {
|
||||
_, err := tcpConn.Write(p)
|
||||
if err != nil {
|
||||
log.Debug().Msgf("Error writing to TCP connection: %v\n", err)
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeTCPToWebSocket(websocketConn *websocket.Conn, tcpConn net.Conn, errorChan chan error) {
|
||||
var mu sync.Mutex
|
||||
out := make([]byte, readerBufferSize)
|
||||
input := make(chan string)
|
||||
pingTicker := time.NewTicker(pingPeriod)
|
||||
defer pingTicker.Stop()
|
||||
defer websocketConn.Close()
|
||||
|
||||
websocketConn.SetReadLimit(2048)
|
||||
websocketConn.SetPongHandler(func(string) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
websocketConn.SetPingHandler(func(data string) error {
|
||||
websocketConn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
return websocketConn.WriteMessage(websocket.PongMessage, []byte(data))
|
||||
})
|
||||
|
||||
reader := bufio.NewReader(tcpConn)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
n, err := reader.Read(out)
|
||||
if err != nil {
|
||||
errorChan <- err
|
||||
if !errors.Is(err, io.EOF) {
|
||||
log.Debug().Msgf("error reading from server: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
processedOutput := validString(string(out[:n]))
|
||||
input <- string(processedOutput)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-input:
|
||||
err := wswrite(websocketConn, &mu, msg)
|
||||
if err != nil {
|
||||
log.Debug().Msgf("error writing to websocket: %v", err)
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
case <-pingTicker.C:
|
||||
if err := wsping(websocketConn, &mu); err != nil {
|
||||
log.Debug().Msgf("error writing to websocket during pong response: %v", err)
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wswrite(websocketConn *websocket.Conn, mu *sync.Mutex, msg string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
websocketConn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
return websocketConn.WriteMessage(websocket.TextMessage, []byte(msg))
|
||||
}
|
||||
|
||||
func wsping(websocketConn *websocket.Conn, mu *sync.Mutex) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
websocketConn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
return websocketConn.WriteMessage(websocket.PingMessage, nil)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const contextEndpoint = "endpoint"
|
||||
// Note: context keys must be distinct types to prevent collisions. They are NOT key/value map's internally
|
||||
// See: https://go.dev/blog/context#TOC_3.2.
|
||||
type key int
|
||||
|
||||
const contextEndpoint key = 0
|
||||
|
||||
func WithEndpoint(endpointService dataservices.EndpointService, endpointIDParam string) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
|
||||
@@ -344,6 +344,7 @@ func Test_apiKeyLookup(t *testing.T) {
|
||||
req.Header.Add("x-api-key", rawAPIKey)
|
||||
|
||||
token, err := bouncer.apiKeyLookup(req)
|
||||
is.NoError(err)
|
||||
|
||||
expectedToken := &portainer.TokenData{ID: user.ID, Username: user.Username, Role: portainer.StandardUserRole}
|
||||
is.Equal(expectedToken, token)
|
||||
@@ -358,6 +359,7 @@ func Test_apiKeyLookup(t *testing.T) {
|
||||
req.Header.Add("x-api-key", rawAPIKey)
|
||||
|
||||
token, err := bouncer.apiKeyLookup(req)
|
||||
is.NoError(err)
|
||||
|
||||
expectedToken := &portainer.TokenData{ID: user.ID, Username: user.Username, Role: portainer.StandardUserRole}
|
||||
is.Equal(expectedToken, token)
|
||||
@@ -372,6 +374,7 @@ func Test_apiKeyLookup(t *testing.T) {
|
||||
req.Header.Add("x-api-key", rawAPIKey)
|
||||
|
||||
token, err := bouncer.apiKeyLookup(req)
|
||||
is.NoError(err)
|
||||
|
||||
expectedToken := &portainer.TokenData{ID: user.ID, Username: user.Username, Role: portainer.StandardUserRole}
|
||||
is.Equal(expectedToken, token)
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const year = time.Hour * 24 * 365
|
||||
|
||||
// scope represents JWT scopes that are supported in JWT claims.
|
||||
type scope string
|
||||
|
||||
@@ -29,7 +31,7 @@ type claims struct {
|
||||
Role int `json:"role"`
|
||||
Scope scope `json:"scope"`
|
||||
ForceChangePassword bool `json:"forceChangePassword"`
|
||||
jwt.StandardClaims
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -98,7 +100,7 @@ func (service *Service) defaultExpireAt() time.Time {
|
||||
// GenerateToken generates a new JWT token.
|
||||
func (service *Service) GenerateToken(data *portainer.TokenData) (string, time.Time, error) {
|
||||
expiryTime := service.defaultExpireAt()
|
||||
token, err := service.generateSignedToken(data, expiryTime.Unix(), defaultScope)
|
||||
token, err := service.generateSignedToken(data, expiryTime, defaultScope)
|
||||
return token, expiryTime, err
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ func (service *Service) ParseAndVerifyToken(token string) (*portainer.TokenData,
|
||||
if err != nil {
|
||||
return nil, errInvalidJWTToken
|
||||
}
|
||||
if user.TokenIssueAt > cl.StandardClaims.IssuedAt {
|
||||
if user.TokenIssueAt > cl.RegisteredClaims.ExpiresAt.Unix() {
|
||||
return nil, errInvalidJWTToken
|
||||
}
|
||||
|
||||
@@ -156,7 +158,7 @@ func (service *Service) SetUserSessionDuration(userSessionDuration time.Duration
|
||||
service.userSessionTimeout = userSessionDuration
|
||||
}
|
||||
|
||||
func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt int64, scope scope) (string, error) {
|
||||
func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt time.Time, scope scope) (string, error) {
|
||||
secret, found := service.secrets[scope]
|
||||
if !found {
|
||||
return "", fmt.Errorf("invalid scope: %v", scope)
|
||||
@@ -170,7 +172,7 @@ func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt
|
||||
if settings.IsDockerDesktopExtension {
|
||||
// Set expiration to 99 years for docker desktop extension.
|
||||
log.Info().Msg("detected docker desktop extension mode")
|
||||
expiresAt = time.Now().Add(time.Hour * 8760 * 99).Unix()
|
||||
expiresAt = time.Now().Add(year * 99)
|
||||
}
|
||||
|
||||
cl := claims{
|
||||
@@ -179,10 +181,12 @@ func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt
|
||||
Role: int(data.Role),
|
||||
Scope: scope,
|
||||
ForceChangePassword: data.ForceChangePassword,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: expiresAt,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
},
|
||||
}
|
||||
|
||||
if !expiresAt.IsZero() {
|
||||
cl.RegisteredClaims = jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
}
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, cl)
|
||||
|
||||
@@ -18,9 +18,9 @@ func (service *Service) GenerateTokenForKubeconfig(data *portainer.TokenData) (s
|
||||
return "", err
|
||||
}
|
||||
|
||||
expiryAt := time.Now().Add(expiryDuration).Unix()
|
||||
expiryAt := time.Now().Add(expiryDuration)
|
||||
if expiryDuration == time.Duration(0) {
|
||||
expiryAt = 0
|
||||
expiryAt = time.Time{}
|
||||
}
|
||||
|
||||
return service.generateSignedToken(data, expiryAt, kubeConfigScope)
|
||||
|
||||
@@ -43,14 +43,14 @@ func TestService_GenerateTokenForKubeconfig(t *testing.T) {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantExpiresAt int64
|
||||
wantExpiresAt *jwt.NumericDate
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "kubeconfig no expiry",
|
||||
fields: myFields,
|
||||
args: myArgs,
|
||||
wantExpiresAt: 0,
|
||||
wantExpiresAt: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestGenerateSignedToken(t *testing.T) {
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
}
|
||||
expiresAt := time.Now().Add(1 * time.Hour).Unix()
|
||||
expiresAt := time.Now().Add(1 * time.Hour)
|
||||
|
||||
generatedToken, err := svc.generateSignedToken(token, expiresAt, defaultScope)
|
||||
assert.NoError(t, err, "failed to generate a signed token")
|
||||
@@ -36,7 +36,7 @@ func TestGenerateSignedToken(t *testing.T) {
|
||||
assert.Equal(t, token.Username, tokenClaims.Username)
|
||||
assert.Equal(t, int(token.ID), tokenClaims.UserID)
|
||||
assert.Equal(t, int(token.Role), tokenClaims.Role)
|
||||
assert.Equal(t, expiresAt, tokenClaims.ExpiresAt)
|
||||
assert.Equal(t, jwt.NewNumericDate(expiresAt), tokenClaims.ExpiresAt)
|
||||
}
|
||||
|
||||
func TestGenerateSignedToken_InvalidScope(t *testing.T) {
|
||||
@@ -49,7 +49,7 @@ func TestGenerateSignedToken_InvalidScope(t *testing.T) {
|
||||
ID: 1,
|
||||
Role: 1,
|
||||
}
|
||||
expiresAt := time.Now().Add(1 * time.Hour).Unix()
|
||||
expiresAt := time.Now().Add(1 * time.Hour)
|
||||
|
||||
_, err = svc.generateSignedToken(token, expiresAt, "testing")
|
||||
assert.Error(t, err)
|
||||
|
||||
@@ -242,6 +242,10 @@ func (factory *ClientFactory) buildEdgeConfig(endpoint *portainer.Endpoint) (*re
|
||||
}
|
||||
|
||||
signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Insecure = true
|
||||
config.QPS = DefaultKubeClientQPS
|
||||
config.Burst = DefaultKubeClientBurst
|
||||
@@ -257,32 +261,6 @@ func (factory *ClientFactory) buildEdgeConfig(endpoint *portainer.Endpoint) (*re
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (factory *ClientFactory) createRemoteClient(endpointURL string) (*kubernetes.Clientset, error) {
|
||||
signature, err := factory.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := clientcmd.BuildConfigFromFlags(endpointURL, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Insecure = true
|
||||
config.QPS = DefaultKubeClientQPS
|
||||
config.Burst = DefaultKubeClientBurst
|
||||
|
||||
config.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||
return &agentHeaderRoundTripper{
|
||||
signatureHeader: signature,
|
||||
publicKeyHeader: factory.signatureService.EncodedPublicKey(),
|
||||
roundTripper: rt,
|
||||
}
|
||||
})
|
||||
|
||||
return kubernetes.NewForConfig(config)
|
||||
}
|
||||
|
||||
func (factory *ClientFactory) CreateRemoteMetricsClient(endpoint *portainer.Endpoint) (*metricsv.Clientset, error) {
|
||||
config, err := factory.CreateConfig(endpoint)
|
||||
if err != nil {
|
||||
|
||||
@@ -198,7 +198,6 @@ func (d *stackDeployer) remoteStack(stack *portainer.Stack, endpoint *portainer.
|
||||
Str("cmd", strings.Join(cmd, " ")).
|
||||
Msg("running unpacker")
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
unpackerContainer, err := cli.ContainerCreate(ctx, &container.Config{
|
||||
Image: image,
|
||||
Cmd: cmd,
|
||||
|
||||
Reference in New Issue
Block a user