rudder icon indicating copy to clipboard operation
rudder copied to clipboard

Fixes #24292: The source of tenant and plugin status must be use to check node update

Open fanf opened this issue 1 year ago • 2 comments

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 SelectFacts object of the composition of storage event from the cache of CoreNodeFact and the cold storage of NodeFact was 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: 

fanf avatar Mar 02 '24 22:03 fanf

PR updated with a new commit

fanf avatar Mar 03 '24 08:03 fanf

PR updated with a new commit

fanf avatar Mar 03 '24 08:03 fanf

PR updated with a new commit

fanf avatar Mar 08 '24 11:03 fanf

PR updated with a new commit

fanf avatar Mar 12 '24 21:03 fanf

Missing API doc !

fanf avatar Mar 13 '24 10:03 fanf

PR updated with a new commit

VinceMacBuche avatar Mar 13 '24 13:03 VinceMacBuche

PR updated with a new commit

fanf avatar Mar 13 '24 21:03 fanf

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

fanf avatar Mar 13 '24 22:03 fanf