Fixes #24292: The source of tenant and plugin status must be use to check node update
https://issues.rudder.io/issues/24292
This PR add mainly two things:
- a control on node modification based on the tenant of the node and the actor, and a new specific method to change tenants on nodes.
- API token logic to understand tenats (but no UI for that for now)
During tests, some inconsistencies where surfaced regarding the behavior of saving nodes and resulting change event:
- when machine were absent in LDAP (which should not happen anymore, but is demonstrated in tests), then there was a non-idempotent get/save/get scenario
- the resulting
SelectFactsobject of the composition of storage event from the cache ofCoreNodeFactand the cold storage ofNodeFactwas incorrect, leading to fewer or more changes reported than needed.
The biggest (in line numbers) part of the PR is adding tests (and log for understand why test are failing). That last part if done throught the facilities on NodeFact: debugLog and debugDiff which are now able to produce informed detailled log of a node fact:
Updating nodes and node's tenant (NodeSecurityContext)
Since the security context is part of the NodeFact, we must tightly control how it can be changed. For that, we chose to have:
- the save method isn't allowed to change security context,
- there is a dedicated method for that.
Both use the same implementation of internalSave because there is a lot of infrastructure logic in it (change events, etc) but with a set of parameter that dictates how to handle change in SecurityContext (allowed or not).
We also added a check to be sure that only user with the relevant permissions are changing the node.
:star2: NOTE: as long as an user is allowed to change a node security context based on tenant logic, the setSecurityTag method will work.
That means that we don't bind a role to the permission of updating a role. It's only "if the node perms matches, then the action can be performed". But we check the consistency of the current state of the node security tag and the future node security tag, so that the action can only change a node tenant to something that is compatible with the user permission.
Behavior when plugin is disable: always keep existing tenant
When the plugin is disabled, we ignore change to node security tag.
Node deletion
The same kind of check is done for node deletion, so that only user with the correct tenant access can delete a node.
Tenants for API account
We want to limit an API account in what nodes it can see in the same fashion than we do for users.
For that, we add a apiTenant LDAP attribute in our schema.
The serialized value is expected to be the same format as for user, with the same interpretation (missing == compat mode with possibility to see any nodes, etc).
Warning: for now, there is no UI to set that attribute value in an API account, it needs to be done latter.
Log facilities
Viewing creation
23:49:36.821 [zio-default-blocking-2] TRACE nodes.details.write - Saved node event: [updatedAccepted] node 'node0.normation.com' (node0) id:node0
* description:None
* fqdn:node0.normation.com
* os:Linux(Linux,,[nothing],None,[nothing])
* machine:MachineInfo(MachineUuid(machine-for-node0),unknownMachineType,None,None)
* rudderSettings:
-RudderSettings(UndefinedKey,ReportingConfiguration(None,None,None),Node,AcceptedInventory,Enabled,None,NodeId(root-policy-server),Some(SecurityTag(Chunk(TenantId(zoneA)))))
+RudderSettings(UndefinedKey,ReportingConfiguration(None,None,None),Node,AcceptedInventory,Enabled,None,NodeId(root-policy-server),Some(SecurityTag(Chunk(TenantId(zoneB)))))
* rudderAgent:RudderAgent(cfengine-nova,root,AgentVersion(8.0.0),Certificate(-----BEGIN CERTIFICATE-----
...-----END CERTIFICATE-----),Chunk(AgentCapability(jq),AgentCapability(yaml),AgentCapability(cfengine),AgentCapability(curl),AgentCapability(acl),AgentCapability(xml),AgentCapability(http_reporting)))
* properties:List()
* creationDate:2007-01-01T01:00:00.000+01:00
* factProcessedDate:2013-05-15T14:34:56.948+02:00
* lastInventoryDate:Some(2013-05-15T14:34:56.948+02:00)
* ipAddresses:Chunk(IpAddress(192.168.56.100))
* timezone:None
* archDescription:None
* ram:None
* ignored: swap,accounts,bios,controllers,environment,file,inputs,local,local,logical,memories,networks,physical,ports,processes,processors,slots,software,software,sounds,storages,videos,vms
Wieving diff
23:49:37.159 [zio-default-blocking-1] TRACE nodes.details.write - Saved node event: [updatedAccepted] node 'node7.normation.com' (node7) id:node7
* description:None
* fqdn:node7.normation.com
* os:Linux(Linux,,[nothing],None,[nothing])
* machine:MachineInfo(MachineUuid(machine2),physicalMachine,None,None)
* rudderSettings:RudderSettings(UndefinedKey,ReportingConfiguration(None,None,None),Node,AcceptedInventory,Initializing,None,NodeId(root-policy-server),None)
* rudderAgent:RudderAgent(cfengine-community,root,AgentVersion(8.0.0),Certificate(-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
),Chunk(AgentCapability(jq),AgentCapability(yaml),AgentCapability(cfengine),AgentCapability(curl),AgentCapability(acl),AgentCapability(xml),AgentCapability(http_reporting)))
* properties:List()
* creationDate:2007-01-01T01:00:00.000+01:00
* factProcessedDate:2020-05-15T14:34:56.948+02:00
* lastInventoryDate:Some(2020-05-15T14:34:56.948+02:00)
* ipAddresses:Chunk()
* timezone:None
* archDescription:None
* ram:None
* swap: None
* accounts: Chunk()
* bios: Chunk(Bios(bios1,None,Some([6.00]),Some(SoftwareEditor(Phoenix Technologies LTD)),None,1))
* controllers: Chunk()
* environment:
* - Chunk()
* + Chunk((envVAR,envVALUE))
* file: Chunk(FileSystem(/,Some(ext3),None,None,Some(10 B),Some(803838361699 B)))
* inputs: Chunk()
* local: Chunk()
* local: Chunk()
* logical: Chunk()
* memories: Chunk()
* networks:
* - Chunk()
* + Chunk(Network(eth0,None,List(),None,List(),List(),List(),None,None,None,None,None))
* physical: Chunk()
* ports: Chunk()
* processes: Chunk()
* processors: Chunk()
* slots:
* - Chunk()
* + Chunk(Slot(slot0,None,None,1))
* software:
* - Chunk(SoftwareFact(Software 0,[1.0.0],None,None,None,None,None,None,None,None,None,None,None,None,None))
* + Chunk(SoftwareFact(Software 0,[1.0.0],None,None,None,None,None,None,None,None,None,None,None,None,None),SoftwareFact(s2,[1.2],None,None,None,None,None,None,None,None,None,None,None,None,None))
* software: Chunk()
* sounds: Chunk()
* storages: Chunk()
* videos: Chunk()
* vms: Chunk()
* ignored:
PR updated with a new commit
PR updated with a new commit
PR updated with a new commit
PR updated with a new commit
Missing API doc !
PR updated with a new commit
PR updated with a new commit
This PR is not mergeable to upper versions.
Since it is "Ready for merge" you must merge it by yourself using the following command:
rudder-dev merge https://github.com/Normation/rudder/pull/5429
-- Your faithful QA
Kant merge: "Happiness is not an ideal of reason, but of imagination."
(https://ci.normation.com/jenkins/job/merge-accepted-pr/81452/console)
OK, squash merging this PR