fix(environment): reject TLS config for Edge Agent environment creation and update [BE-12700] (#2609)

This commit is contained in:
Oscar Zhou
2026-05-12 08:50:41 +12:00
committed by GitHub
parent 080d75acae
commit 9ecd8d3efb
4 changed files with 132 additions and 1 deletions
@@ -90,6 +90,10 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
useTLS, _ := request.RetrieveBooleanMultiPartFormValue(r, "TLS", true)
payload.TLS = useTLS
if payload.TLS && payload.EndpointCreationType == edgeAgentEnvironment {
return errors.New("TLS is not supported for Edge Agent environments")
}
if payload.TLS {
skipTLSServerVerification, _ := request.RetrieveBooleanMultiPartFormValue(r, "TLSSkipVerify", true)
payload.TLSSkipVerify = skipTLSServerVerification
@@ -116,6 +116,61 @@ func TestCreateEndpointFailure(t *testing.T) {
require.Nil(t, endpoint)
}
func TestValidateEndpointCreatePayload_TLS(t *testing.T) {
t.Parallel()
fips.InitFIPS(false)
testCases := []struct {
name string
formValues map[string][]string
expectedError string
}{
{
name: "edge agent env with TLS rejected",
formValues: map[string][]string{
"Name": {"Test Endpoint"},
"EndpointCreationType": {"4"},
"TLS": {"true"},
},
expectedError: "TLS is not supported for Edge Agent environments",
},
{
name: "edge agent env without TLS succeeds",
formValues: map[string][]string{
"Name": {"Test Endpoint"},
"EndpointCreationType": {"4"},
"URL": {"https://portainer.example:9443"},
},
expectedError: "",
},
{
name: "non-edge agent env with TLS allowed",
formValues: map[string][]string{
"Name": {"Test Endpoint"},
"EndpointCreationType": {"2"},
"TLS": {"true"},
"TLSSkipVerify": {"true"},
"TLSSkipClientVerify": {"true"},
},
expectedError: "",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := http.Request{Form: tc.formValues}
p := &endpointCreatePayload{}
err := p.Validate(&r)
if tc.expectedError == "" {
require.NoError(t, err)
} else {
require.EqualError(t, err, tc.expectedError)
}
})
}
}
func TestCreateEdgeAgentEndpoint_ContainerEngineMapping(t *testing.T) {
t.Parallel()
fips.InitFIPS(false)
@@ -94,6 +94,10 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
return httperror.InternalServerError("Unable to find an environment with the specified identifier inside the database", err)
}
if payload.TLS != nil && *payload.TLS && endpointutils.IsEdgeEndpoint(endpoint) {
return httperror.BadRequest("TLS is not supported for Edge Agent environments", nil)
}
updateEndpointProxy := shouldReloadTLSConfiguration(endpoint, &payload)
if payload.Name != nil {
@@ -247,7 +251,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
}
}
if !endpointutils.IsLocalEndpoint(endpoint) && endpointutils.IsKubernetesEndpoint(endpoint) {
isStandardKubeAgent := !endpointutils.IsLocalEndpoint(endpoint) && endpointutils.IsKubernetesEndpoint(endpoint) && !endpointutils.IsEdgeEndpoint(endpoint)
if isStandardKubeAgent {
endpoint.TLSConfig.TLS = true
endpoint.TLSConfig.TLSSkipVerify = true
}
@@ -0,0 +1,67 @@
package endpoints
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_endpointPut_TLSRejectedForEdgeEndpoint(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, true, true)
h := NewHandler(testhelpers.NewTestRequestBouncer())
h.DataStore = store
testCases := []struct {
name string
endpointType portainer.EndpointType
}{
{
name: "edge agent on docker rejects TLS",
endpointType: portainer.EdgeAgentOnDockerEnvironment,
},
{
name: "edge agent on kubernetes rejects TLS",
endpointType: portainer.EdgeAgentOnKubernetesEnvironment,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
endpointID := portainer.EndpointID(store.Endpoint().GetNextIdentifier())
err := store.Endpoint().Create(&portainer.Endpoint{
ID: endpointID,
Type: tc.endpointType,
})
require.NoError(t, err)
payload := &endpointUpdatePayload{TLS: new(true)}
bodyJSON, err := json.Marshal(payload)
require.NoError(t, err)
url := fmt.Sprintf("/endpoints/%d", endpointID)
req := httptest.NewRequest(http.MethodPut, url, bytes.NewBuffer(bodyJSON))
rctx := security.StoreTokenData(req, &portainer.TokenData{ID: 1, Username: "admin", Role: portainer.AdministratorRole})
req = req.WithContext(rctx)
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
})
}
}