fix(auth): skip security checks with --no-auth flag (#4513)
* fix(stacks): skip security checks if no-auth * fix(containers): skip security check when auth is disabled * fix(volumes): show browse if auth is disabled
This commit is contained in:
@@ -302,9 +302,12 @@ func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portai
|
||||
}
|
||||
filteredRegistries := security.FilterRegistries(registries, securityContext)
|
||||
|
||||
user, err := handler.UserService.User(securityContext.UserID)
|
||||
if err != nil {
|
||||
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
|
||||
var user *portainer.User
|
||||
if !handler.authDisabled {
|
||||
user, err = handler.UserService.User(securityContext.UserID)
|
||||
if err != nil {
|
||||
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
|
||||
}
|
||||
}
|
||||
|
||||
config := &composeStackDeploymentConfig{
|
||||
@@ -330,28 +333,29 @@ func (handler *Handler) deployComposeStack(config *composeStackDeploymentConfig)
|
||||
return err
|
||||
}
|
||||
|
||||
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (!settings.AllowBindMountsForRegularUsers ||
|
||||
!settings.AllowPrivilegedModeForRegularUsers ||
|
||||
!settings.AllowHostNamespaceForRegularUsers ||
|
||||
!settings.AllowDeviceMappingForRegularUsers ||
|
||||
!settings.AllowContainerCapabilitiesForRegularUsers) &&
|
||||
!isAdminOrEndpointAdmin {
|
||||
|
||||
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
|
||||
|
||||
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
|
||||
if !handler.authDisabled {
|
||||
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = handler.isValidStackFile(stackContent, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
if (!settings.AllowBindMountsForRegularUsers ||
|
||||
!settings.AllowPrivilegedModeForRegularUsers ||
|
||||
!settings.AllowHostNamespaceForRegularUsers ||
|
||||
!settings.AllowDeviceMappingForRegularUsers ||
|
||||
!settings.AllowContainerCapabilitiesForRegularUsers) && !isAdminOrEndpointAdmin {
|
||||
|
||||
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
|
||||
|
||||
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = handler.isValidStackFile(stackContent, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -311,9 +311,12 @@ func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portaine
|
||||
}
|
||||
filteredRegistries := security.FilterRegistries(registries, securityContext)
|
||||
|
||||
user, err := handler.UserService.User(securityContext.UserID)
|
||||
if err != nil {
|
||||
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
|
||||
var user *portainer.User
|
||||
if !handler.authDisabled {
|
||||
user, err = handler.UserService.User(securityContext.UserID)
|
||||
if err != nil {
|
||||
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to load user information from the database", err}
|
||||
}
|
||||
}
|
||||
|
||||
config := &swarmStackDeploymentConfig{
|
||||
@@ -335,23 +338,25 @@ func (handler *Handler) deploySwarmStack(config *swarmStackDeploymentConfig) err
|
||||
return err
|
||||
}
|
||||
|
||||
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !settings.AllowBindMountsForRegularUsers && !isAdminOrEndpointAdmin {
|
||||
|
||||
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
|
||||
|
||||
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
|
||||
if !handler.authDisabled {
|
||||
isAdminOrEndpointAdmin, err := handler.userIsAdminOrEndpointAdmin(config.user, config.endpoint.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = handler.isValidStackFile(stackContent, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
if !settings.AllowBindMountsForRegularUsers && !isAdminOrEndpointAdmin {
|
||||
|
||||
composeFilePath := path.Join(config.stack.ProjectPath, config.stack.EntryPoint)
|
||||
|
||||
stackContent, err := handler.FileService.GetFileContent(composeFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = handler.isValidStackFile(stackContent, settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ type Handler struct {
|
||||
stackCreationMutex *sync.Mutex
|
||||
stackDeletionMutex *sync.Mutex
|
||||
requestBouncer *security.RequestBouncer
|
||||
authDisabled bool
|
||||
|
||||
*mux.Router
|
||||
FileService portainer.FileService
|
||||
GitService portainer.GitService
|
||||
@@ -32,9 +34,10 @@ type Handler struct {
|
||||
}
|
||||
|
||||
// NewHandler creates a handler to manage stack operations.
|
||||
func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
||||
func NewHandler(bouncer *security.RequestBouncer, authDisabled bool) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
authDisabled: authDisabled,
|
||||
stackCreationMutex: &sync.Mutex{},
|
||||
stackDeletionMutex: &sync.Mutex{},
|
||||
requestBouncer: bouncer,
|
||||
@@ -57,7 +60,7 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
||||
}
|
||||
|
||||
func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID, resourceControl *portainer.ResourceControl) (bool, error) {
|
||||
if securityContext.IsAdmin {
|
||||
if securityContext.IsAdmin || handler.authDisabled {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -90,7 +93,7 @@ func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedR
|
||||
}
|
||||
|
||||
func (handler *Handler) userCanCreateStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID) (bool, error) {
|
||||
if securityContext.IsAdmin {
|
||||
if securityContext.IsAdmin || handler.authDisabled {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ func (factory *ProxyFactory) newDockerHTTPProxy(endpoint *portainer.Endpoint) (h
|
||||
ExtensionService: factory.extensionService,
|
||||
SignatureService: factory.signatureService,
|
||||
DockerClientFactory: factory.dockerClientFactory,
|
||||
AuthDisabled: factory.authDisabled,
|
||||
}
|
||||
|
||||
dockerTransport, err := docker.NewTransport(transportParameters, httpTransport)
|
||||
|
||||
@@ -173,63 +173,65 @@ func (transport *Transport) decorateContainerCreationOperation(request *http.Req
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := transport.userService.User(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbacExtension, err := transport.extensionService.Extension(portainer.RBACExtension)
|
||||
if err != nil && err != portainer.ErrObjectNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpointResourceAccess := false
|
||||
_, ok := user.EndpointAuthorizations[portainer.EndpointID(transport.endpoint.ID)][portainer.EndpointResourcesAccess]
|
||||
if ok {
|
||||
endpointResourceAccess = true
|
||||
}
|
||||
|
||||
isAdmin := (rbacExtension != nil && endpointResourceAccess) || tokenData.Role == portainer.AdministratorRole
|
||||
|
||||
if !isAdmin {
|
||||
settings, err := transport.settingsService.Settings()
|
||||
if !transport.authDisabled {
|
||||
user, err := transport.userService.User(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !settings.AllowPrivilegedModeForRegularUsers ||
|
||||
!settings.AllowHostNamespaceForRegularUsers ||
|
||||
!settings.AllowDeviceMappingForRegularUsers ||
|
||||
!settings.AllowContainerCapabilitiesForRegularUsers {
|
||||
rbacExtension, err := transport.extensionService.Extension(portainer.RBACExtension)
|
||||
if err != nil && err != portainer.ErrObjectNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
endpointResourceAccess := false
|
||||
_, ok := user.EndpointAuthorizations[portainer.EndpointID(transport.endpoint.ID)][portainer.EndpointResourcesAccess]
|
||||
if ok {
|
||||
endpointResourceAccess = true
|
||||
}
|
||||
|
||||
isAdmin := (rbacExtension != nil && endpointResourceAccess) || tokenData.Role == portainer.AdministratorRole
|
||||
|
||||
if !isAdmin {
|
||||
settings, err := transport.settingsService.Settings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partialContainer := &PartialContainer{}
|
||||
err = json.Unmarshal(body, partialContainer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !settings.AllowPrivilegedModeForRegularUsers ||
|
||||
!settings.AllowHostNamespaceForRegularUsers ||
|
||||
!settings.AllowDeviceMappingForRegularUsers ||
|
||||
!settings.AllowContainerCapabilitiesForRegularUsers {
|
||||
|
||||
if !settings.AllowPrivilegedModeForRegularUsers && partialContainer.HostConfig.Privileged {
|
||||
return forbiddenResponse, errors.New("forbidden to use privileged mode")
|
||||
}
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !settings.AllowHostNamespaceForRegularUsers && partialContainer.HostConfig.PidMode == "host" {
|
||||
return forbiddenResponse, errors.New("forbidden to use pid host namespace")
|
||||
}
|
||||
partialContainer := &PartialContainer{}
|
||||
err = json.Unmarshal(body, partialContainer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !settings.AllowDeviceMappingForRegularUsers && len(partialContainer.HostConfig.Devices) > 0 {
|
||||
return nil, errors.New("forbidden to use device mapping")
|
||||
}
|
||||
if !settings.AllowPrivilegedModeForRegularUsers && partialContainer.HostConfig.Privileged {
|
||||
return forbiddenResponse, errors.New("forbidden to use privileged mode")
|
||||
}
|
||||
|
||||
if !settings.AllowContainerCapabilitiesForRegularUsers && (len(partialContainer.HostConfig.CapAdd) > 0 || len(partialContainer.HostConfig.CapDrop) > 0) {
|
||||
return nil, errors.New("forbidden to use container capabilities")
|
||||
}
|
||||
if !settings.AllowHostNamespaceForRegularUsers && partialContainer.HostConfig.PidMode == "host" {
|
||||
return forbiddenResponse, errors.New("forbidden to use pid host namespace")
|
||||
}
|
||||
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
||||
if !settings.AllowDeviceMappingForRegularUsers && len(partialContainer.HostConfig.Devices) > 0 {
|
||||
return nil, errors.New("forbidden to use device mapping")
|
||||
}
|
||||
|
||||
if !settings.AllowContainerCapabilitiesForRegularUsers && (len(partialContainer.HostConfig.CapAdd) > 0 || len(partialContainer.HostConfig.CapDrop) > 0) {
|
||||
return nil, errors.New("forbidden to use container capabilities")
|
||||
}
|
||||
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ type (
|
||||
extensionService portainer.ExtensionService
|
||||
dockerClient *client.Client
|
||||
dockerClientFactory *docker.ClientFactory
|
||||
authDisabled bool
|
||||
}
|
||||
|
||||
// TransportParameters is used to create a new Transport
|
||||
@@ -54,6 +55,7 @@ type (
|
||||
ReverseTunnelService portainer.ReverseTunnelService
|
||||
ExtensionService portainer.ExtensionService
|
||||
DockerClientFactory *docker.ClientFactory
|
||||
AuthDisabled bool
|
||||
}
|
||||
|
||||
restrictedDockerOperationContext struct {
|
||||
@@ -94,6 +96,7 @@ func NewTransport(parameters *TransportParameters, httpTransport *http.Transport
|
||||
dockerClientFactory: parameters.DockerClientFactory,
|
||||
HTTPTransport: httpTransport,
|
||||
dockerClient: dockerClient,
|
||||
authDisabled: parameters.AuthDisabled,
|
||||
}
|
||||
|
||||
return transport, nil
|
||||
@@ -656,7 +659,6 @@ func (transport *Transport) createRegistryAccessContext(request *http.Request) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
accessContext := ®istryAccessContext{
|
||||
isAdmin: true,
|
||||
userID: tokenData.ID,
|
||||
|
||||
@@ -24,6 +24,7 @@ func (factory ProxyFactory) newOSBasedLocalProxy(path string, endpoint *portaine
|
||||
ExtensionService: factory.extensionService,
|
||||
SignatureService: factory.signatureService,
|
||||
DockerClientFactory: factory.dockerClientFactory,
|
||||
AuthDisabled: factory.authDisabled,
|
||||
}
|
||||
|
||||
proxy := &dockerLocalProxy{}
|
||||
|
||||
@@ -25,6 +25,7 @@ func (factory ProxyFactory) newOSBasedLocalProxy(path string, endpoint *portaine
|
||||
ExtensionService: factory.extensionService,
|
||||
SignatureService: factory.signatureService,
|
||||
DockerClientFactory: factory.dockerClientFactory,
|
||||
AuthDisabled: factory.authDisabled,
|
||||
}
|
||||
|
||||
proxy := &dockerLocalProxy{}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/portainer/portainer/api"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
)
|
||||
|
||||
@@ -32,6 +32,7 @@ type (
|
||||
reverseTunnelService portainer.ReverseTunnelService
|
||||
extensionService portainer.ExtensionService
|
||||
dockerClientFactory *docker.ClientFactory
|
||||
authDisabled bool
|
||||
}
|
||||
|
||||
// ProxyFactoryParameters is used to create a new ProxyFactory
|
||||
@@ -47,6 +48,7 @@ type (
|
||||
ReverseTunnelService portainer.ReverseTunnelService
|
||||
ExtensionService portainer.ExtensionService
|
||||
DockerClientFactory *docker.ClientFactory
|
||||
AuthDisabled bool
|
||||
}
|
||||
)
|
||||
|
||||
@@ -64,6 +66,7 @@ func NewProxyFactory(parameters *ProxyFactoryParameters) *ProxyFactory {
|
||||
reverseTunnelService: parameters.ReverseTunnelService,
|
||||
extensionService: parameters.ExtensionService,
|
||||
dockerClientFactory: parameters.DockerClientFactory,
|
||||
authDisabled: parameters.AuthDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/orcaman/concurrent-map"
|
||||
"github.com/portainer/portainer/api"
|
||||
cmap "github.com/orcaman/concurrent-map"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
)
|
||||
@@ -34,6 +34,7 @@ type (
|
||||
ReverseTunnelService portainer.ReverseTunnelService
|
||||
ExtensionService portainer.ExtensionService
|
||||
DockerClientFactory *docker.ClientFactory
|
||||
AuthDisabled bool
|
||||
}
|
||||
)
|
||||
|
||||
@@ -51,6 +52,7 @@ func NewManager(parameters *ManagerParams) *Manager {
|
||||
ReverseTunnelService: parameters.ReverseTunnelService,
|
||||
ExtensionService: parameters.ExtensionService,
|
||||
DockerClientFactory: parameters.DockerClientFactory,
|
||||
AuthDisabled: parameters.AuthDisabled,
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
|
||||
@@ -104,6 +104,7 @@ func (server *Server) Start() error {
|
||||
ReverseTunnelService: server.ReverseTunnelService,
|
||||
ExtensionService: server.ExtensionService,
|
||||
DockerClientFactory: server.DockerClientFactory,
|
||||
AuthDisabled: server.AuthDisabled,
|
||||
}
|
||||
proxyManager := proxy.NewManager(proxyManagerParameters)
|
||||
|
||||
@@ -247,7 +248,7 @@ func (server *Server) Start() error {
|
||||
settingsHandler.ExtensionService = server.ExtensionService
|
||||
settingsHandler.AuthorizationService = authorizationService
|
||||
|
||||
var stackHandler = stacks.NewHandler(requestBouncer)
|
||||
var stackHandler = stacks.NewHandler(requestBouncer, server.AuthDisabled)
|
||||
stackHandler.FileService = server.FileService
|
||||
stackHandler.StackService = server.StackService
|
||||
stackHandler.EndpointService = server.EndpointService
|
||||
|
||||
Reference in New Issue
Block a user