From 4dfdabff8ce19621ff7fab05ab510524305548e5 Mon Sep 17 00:00:00 2001 From: Shawn Carey Date: Tue, 9 Apr 2024 18:34:15 +0000 Subject: [PATCH] respect service policies for admin identities (#1919) respect service policies for admin identities --- CHANGELOG.md | 20 +++++++++++ controller/model/edge_service_manager.go | 45 ++++++++---------------- tests/service_test.go | 20 +++++------ 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1fbc05a..9cb81d31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# Release 1.0.0 + +## What's New + +* Bugfixes + +## DEFAULT Bind/Dial SERVICE PERMISSIONS FOR Admin IDENTITIES HAVE CHANGED + +Admin identities were able to Dial and Bind all services regardless of the effective service policies +prior to this release. This could lead to a confusing situation where a tunneler that was assuming an Admin +identity would put itself into an infinite connect-loop when a service's host.v1 address overlapped with +any addresses in its intercept configuration. + +Please create service policies to grant Bind or Dial permissions to Admin identities as needed. + +## Component Updates and Bug Fixes + +* github.com/openziti/ziti: [v0.34.2 -> v1.0.0](https://github.com/openziti/ziti/compare/v0.34.2...v1.0.0) + * [Issue #1781](https://github.com/openziti/ziti/issues/1781) - Admin identities have bind and dial permissions to services + # Release 0.34.2 ## What's New diff --git a/controller/model/edge_service_manager.go b/controller/model/edge_service_manager.go index 4c16b76d7..2acd96590 100644 --- a/controller/model/edge_service_manager.go +++ b/controller/model/edge_service_manager.go @@ -119,34 +119,15 @@ func (self *EdgeServiceManager) ReadForIdentity(id string, identityId string, co } func (self *EdgeServiceManager) ReadForIdentityInTx(tx *bbolt.Tx, id string, identityId string, configTypes map[string]struct{}) (*ServiceDetail, error) { + edgeServiceStore := self.env.GetStores().EdgeService identity, err := self.GetEnv().GetManagers().Identity.readInTx(tx, identityId) if err != nil { return nil, err } - - var service *ServiceDetail - - if identity.IsAdmin { - service, err = self.readInTx(tx, id) - if err == nil && service != nil { - service.Permissions = []string{db.PolicyTypeBindName, db.PolicyTypeDialName} - } - } else { - service, err = self.ReadForNonAdminIdentityInTx(tx, id, identityId) - } - if err == nil && len(configTypes) > 0 { - identityServiceConfigs := self.env.GetStores().Identity.LoadServiceConfigsByServiceAndType(tx, identityId, configTypes) - self.mergeConfigs(tx, configTypes, service, identityServiceConfigs) - } - return service, err -} - -func (self *EdgeServiceManager) ReadForNonAdminIdentityInTx(tx *bbolt.Tx, id string, identityId string) (*ServiceDetail, error) { - edgeServiceStore := self.env.GetStores().EdgeService isBindable := edgeServiceStore.IsBindableByIdentity(tx, id, identityId) isDialable := edgeServiceStore.IsDialableByIdentity(tx, id, identityId) - if !isBindable && !isDialable { + if !isBindable && !isDialable && !identity.IsAdmin { // admin can view services even if policies don't permit bind/dial { return nil, boltz.NewNotFoundError(self.GetStore().GetSingularEntityType(), "id", id) } @@ -163,7 +144,17 @@ func (self *EdgeServiceManager) ReadForNonAdminIdentityInTx(tx *bbolt.Tx, id str if isDialable { result.Permissions = append(result.Permissions, db.PolicyTypeDialName) } - return result, nil + if result.Permissions == nil { + // don't return results with no permissions, since some SDKs assume non-nil permissions + result.Permissions = []string{db.PolicyTypeInvalidName} + } + + if len(configTypes) > 0 { + identityServiceConfigs := self.env.GetStores().Identity.LoadServiceConfigsByServiceAndType(tx, identityId, configTypes) + self.mergeConfigs(tx, configTypes, result, identityServiceConfigs) + } + + return result, err } func (self *EdgeServiceManager) PublicQueryForIdentity(sessionIdentity *Identity, configTypes map[string]struct{}, query ast.Query) (*ServiceListResult, error) { @@ -259,14 +250,8 @@ func (result *ServiceListResult) collect(tx *bbolt.Tx, ids []string, queryMetaDa identityServiceConfigs := result.manager.env.GetStores().Identity.LoadServiceConfigsByServiceAndType(tx, result.identityId, result.configTypes) for _, key := range ids { - if !result.isAdmin && result.identityId != "" { - service, err = result.manager.ReadForNonAdminIdentityInTx(tx, key, result.identityId) - } else { - service, err = result.manager.readInTx(tx, key) - if service != nil && result.isAdmin { - service.Permissions = []string{db.PolicyTypeBindName, db.PolicyTypeDialName} - } - } + // service permissions for admin & non-admin identities will be set according to policies + service, err = result.manager.ReadForIdentityInTx(tx, key, result.identityId, result.configTypes) if err != nil { return err } diff --git a/tests/service_test.go b/tests/service_test.go index c7e5c84b8..e84efdd5a 100644 --- a/tests/service_test.go +++ b/tests/service_test.go @@ -20,9 +20,9 @@ package tests import ( "fmt" - "github.com/openziti/ziti/common/eid" "github.com/openziti/foundation/v2/errorz" "github.com/openziti/foundation/v2/stringz" + "github.com/openziti/ziti/common/eid" "net/url" "sort" "testing" @@ -50,7 +50,7 @@ func Test_Services(t *testing.T) { ctx.testContextChanged(t) now := time.Now() service := ctx.AdminManagementSession.requireNewService(nil, nil) - service.permissions = []string{"Dial", "Bind"} + service.permissions = []string{"Invalid"} entityJson := ctx.AdminManagementSession.validateEntityWithQuery(service) ctx.validateDateFieldsForCreate(now, entityJson) }) @@ -58,11 +58,11 @@ func Test_Services(t *testing.T) { t.Run("list as admin should return 3 services", func(t *testing.T) { ctx.testContextChanged(t) service1 := ctx.AdminManagementSession.requireNewService(nil, nil) - service1.permissions = []string{"Dial", "Bind"} + service1.permissions = []string{"Invalid"} service2 := ctx.AdminManagementSession.requireNewService(nil, nil) - service2.permissions = []string{"Dial", "Bind"} + service2.permissions = []string{"Invalid"} service3 := ctx.AdminManagementSession.requireNewService(nil, nil) - service3.permissions = []string{"Dial", "Bind"} + service3.permissions = []string{"Invalid"} ctx.AdminManagementSession.validateEntityWithLookup(service1) ctx.AdminManagementSession.validateEntityWithQuery(service1) @@ -108,7 +108,7 @@ func Test_Services(t *testing.T) { t.Run("lookup as admin should pass", func(t *testing.T) { ctx.testContextChanged(t) service := ctx.AdminManagementSession.requireNewService(nil, nil) - service.permissions = []string{"Dial", "Bind"} + service.permissions = []string{"Invalid"} ctx.AdminManagementSession.validateEntityWithLookup(service) }) @@ -163,7 +163,7 @@ func Test_Services(t *testing.T) { ctx.testContextChanged(t) now := time.Now() service := ctx.AdminManagementSession.requireNewService(nil, nil) - service.permissions = []string{"Bind", "Dial"} + service.permissions = []string{"Invalid"} entityJson := ctx.AdminManagementSession.validateEntityWithQuery(service) createdAt := ctx.validateDateFieldsForCreate(now, entityJson) @@ -364,7 +364,7 @@ func Test_ServiceRoleAttributes(t *testing.T) { role1 := eid.New() role2 := eid.New() service := ctx.AdminManagementSession.requireNewService(s(role1, role2), nil) - service.permissions = []string{"Dial", "Bind"} + service.permissions = []string{"Invalid"} ctx.AdminManagementSession.validateEntityWithQuery(service) ctx.AdminManagementSession.validateEntityWithLookup(service) @@ -375,7 +375,7 @@ func Test_ServiceRoleAttributes(t *testing.T) { role1 := eid.New() role2 := eid.New() service := ctx.AdminManagementSession.requireNewService(s(role1, role2), nil) - service.permissions = []string{"Dial", "Bind"} + service.permissions = []string{"Invalid"} role3 := eid.New() service.roleAttributes = []string{role2, role3} @@ -423,7 +423,7 @@ func Test_ServiceRoleAttributes(t *testing.T) { now := time.Now() newService := ctx.AdminManagementSession.requireNewService(nil, nil) - newService.permissions = []string{"Dial", "Bind"} + newService.permissions = []string{"Invalid"} entityJson := ctx.AdminManagementSession.validateEntityWithQuery(newService)