Puppet Enterprise RBAC API, or how to manage access to tasks

See more posts about: Tips & How To

RBAC is great, but I’m tired of clicking!

Puppet Enterprise (PE) role-based access control (RBAC) is great because it allows you to manage access to tasks with groups or individual users and nodes. But what if your organization is growing, or your use of Puppet is expanding? What if you want to take advantage of agentless tasks and there are just too many users and systems? What if different teams are managing permissions, lists of users and nodes, and writing tasks? The PE RBAC API can help.

Freeing you to do what robots can’t

PE has a new API to enable you to control access to Puppet Tasks. You can interact with this API through your scripts or code, or integrate with other systems, enabling you to make changes without having to interact with the PE console. Maybe you want to divide up task development and role management. Perhaps you want to include RBAC access to systems that are created and destroyed frequently in containers or the cloud. Now you can! Follow me down the rabbit hole and see.

To pull our example together we will be enabling web devs to restart web servers on web server nodes in our infrastructure. Roles and permissions for users to call these APIs are outside the scope of this article.

Thus begins a series of steps (divide and conquer!)

Homework! In order to use our API, you will need to locate the required services in your infrastructure1, have a tool for web requests (and maybe working with JSON)2, and you will need an authentication token:

1
2
3
curl -X POST https://$PE_HOST:$RBAC_PORT/rbac-api/v1/auth/token \
  -d '{"login":"azurediamond","password":"hunter2","lifetime":"12h"}' \
  -k -H "Content-Type: application/json"

Results:

1
2
3
4
5
6
7
8
9
10
11
12
% export PE_HOST=puppet-enterprise-instance
% export RBAC_PORT=4433
% curl -X POST -H "Content-Type: application/json" -k \
-d '{"login":"azurediamond","password": "hunter2"}' \
https://$PE_HOST:$RBAC_PORT/rbac-api/v1/auth/token
{"token":"ANw7SC8OmDmgC22U0DX_kp90i0C-S-DAUUQnP7ySyYnA"}%                                                                                                                                                   % export TOKEN=ANw7SC8OmDmgC22U0DX_kp90i0C-S-DAUUQnP7ySyYnA
% #Set up some more convenience variables
% export CLASSIFIER_PORT=4433
% export ORCH_PORT=8143
% export INVENTORY_PORT=8143
% export PE_USER=8c537a9f-9d91-418b-8f30-623fbf856cce
% export PARENT_NODE_GROUP=00000000-0000-4000-8000-000000000000

Docs: Puppet RBAC POST /auth/token endpoint

First things first: User groups and node groups need to exist before the rest.

User groups come from LDAP or whatever external directory you are using. We will need the name of this group, e.g. ‘Web Devs’. Node groups (we will call ours ‘Web Servers’) can be created with this API call1:

1
2
3
curl -X POST https://$PE_HOST:$CLASSIFIER_PORT/classifier-api/v1/groups \
  -d '{"name":"Web Servers","parent":"00000000-0000-4000-8000-000000000000","classes":{}}' \
  -H "Content-Type: application/json" -k -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
5
6
7
8
9
10
11
% curl -X POST https://$PE_HOST:$CLASSIFIER_PORT/classifier-api/v1/groups \
  -d '{"name":"Web Servers","parent":"00000000-0000-4000-8000-000000000000","classes":{}}' \
  -H "Content-Type: application/json" -k -H "X-Authentication:$TOKEN"
 % export WEB_SERVER_GROUP=$(curl https://$PE_HOST:$RBAC_PORT/classifier-api/v1/groups \
  -H "Content-Type: application/json" -k -H "X-Authentication:$TOKEN" \
  | jq -r '.[] | select(.name == "Web Servers") | .id')
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  6161  100  6161    0     0  20981      0 --:--:-- --:--:-- --:--:-- 21027
% echo $WEB_SERVER_GROUP
a7cb4911-21b4-4ee7-a5b6-c47fe968264c

Docs: Classifier POST groups endpoint

Subtasks FTW! Hand these out to your team like party favors!

These steps can all be done separately with a few ordering requirements. The core of this is the task-target. This is a data structure that enables you to connect nodes or node groups and tasks. Then you can create roles to allow users or user group members to execute these tasks on the targeted nodes.

Now separately (task-target must exist before roles are created, and nodes must have a name before they are added to a group, but they don’t have to exist yet), you can:

  1. Find or write a module that has tasks and include it in your PE installation3 (e.g. https://forge.puppet.com/puppetlabs/apache/tasks#task_init). We will be using the built in ‘service’ task.

  2. Create or update a ‘task-target’ (this is our unit of permissions in our RBAC API):

1
2
3
curl -X POST https://$PE_HOST:$ORCH_PORT/orchestrator/v1/command/task_target \
  -d '{"display_name":"web-dev-service-on-web-server","tasks":["service"],"node_groups":["a7cb4911-21b4-4ee7-a5b6-c47fe968264c"]}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
5
6
7
8
9
10
% curl -X POST https://$PE_HOST:$ORCH_PORT/orchestrator/v1/command/task_target \
  -d '{"display_name":"web-dev-service-on-web-server","tasks":["service"],"node_groups":["a7cb4911-21b4-4ee7-a5b6-c47fe968264c"]}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

{
  "task_target" : {
    "id" : "https://puppet-enterprise-instance:8143/orchestrator/v1/scopes/task_targets/1",
    "name" : "1"
  }
}%

Docs: Puppet orchestrator POST /command/task_target endpoint

  1. Create a role that controls permission to execute the task-target:
1
2
3
curl -X POST https://$PE_HOST:$RBAC_PORT/rbac-api/v1/roles \
  -d '{"permissions":[{"object_type":"tasks","action":"run_with_constraints","instance":"1"}],"group_ids":[],"display_name":"Web Dev Servers","user_ids":["8c537a9f-9d91-418b-8f30-623fbf856cce"],"description":"Enable Web Devs to service Web Servers"}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
% curl -X POST https://$PE_HOST:$RBAC_PORT/rbac-api/v1/roles \
  -d '{"permissions":[{"object_type":"tasks","action":"run_with_constraints","instance":"1"}],"group_ids":[],"display_name":"Web Dev Servers","user_ids":["8c537a9f-9d91-418b-8f30-623fbf856cce"],"description":"Enable Web Devs to service Web Servers"}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"
%

Docs: Puppet RBAC POST /roles endpoint

Note: %CAUTION% Permissions to create this role require ultimate permissions (think “root”) on your PE console, so this operation should only be performed by your PE Administrator. It does have to be done after your task-target is created. Currently there is no way to modify or delete a task-target, but you can remove its associated roles. Also, this needs the task-target ID, so that has to be created first.

In this simplified example, the permissions are set for a user id for testing. In a more realistic environment, the UUID of one or more user groups would be in the currently empty argument: “group_ids”:[]

  1. Add a node to our inventory:
1
2
3
curl -X POST https://$PE_HOST:$INVENTORY_PORT/inventory/v1/command/create-connection \
  -d '{"certnames":["rabid-chairman.delivery.puppetlabs.net"],"type":"ssh","parameters":{},"sensitive_parameters":{"user":"root","password":"*******"},"duplicates":"replace"}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
5
6
7
% curl -X POST https://$PE_HOST:$INVENTORY_PORT/inventory/v1/command/create-connection \
  -d '{"certnames":["rabid-chairman.delivery.puppetlabs.net"],"type":"ssh","parameters":{},"sensitive_parameters":{"user":"root","password":"*******"},"duplicates":"replace"}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

{
  "connection_id" : "16e470af-d6da-4097-8d4c-95bb9e08a490"
}%

Docs: #post_command_create-connection

Note: most API calls will refer to this system by its certname: rabid-chairman.delivery.puppetlabs.net

  1. Add (“pin”) a node to a group:
1
2
curl -X POST https://$PE_HOST:$CLASSIFIER_PORT/classifier-api/v1/groups/$WEB_SERVER_GROUP/pin \
  -k -d '{"nodes":["rabid-chairman.delivery.puppetlabs.net"]}' -H 'Content-Type: application/json' -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
% curl -X POST https://$PE_HOST:$CLASSIFIER_PORT/classifier-api/v1/groups/$WEB_SERVER_GROUP/pin \
  -k -d '{"nodes":["rabid-chairman.delivery.puppetlabs.net"]}' -H 'Content-Type: application/json' -H "X-Authentication:$TOKEN"

%

Docs: Puppet Classifier POST /groups/pin endpoint

Note: The node does not have to be in inventory before it is pinned to the group, but its certname has to be known. Also, there are many other ways the classifier can include a node in a group3.

Everything (should be) ready!

Permissions can be checked with this API:

1
2
curl https://$PE_HOST:$RBAC_PORT/rbac-api/v1/permitted/tasks/run_with_constraints/$PE_USER \
  -k -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
  % curl https://$PE_HOST:$RBAC_PORT/rbac-api/v1/permitted/tasks/run_with_constraints/$PE_USER \
  -k -H "X-Authentication:$TOKEN"

["1"]%

Docs: #get-permittedobject-typeactionuuid

And finally, users can even use an API to execute the task:

1
2
3
curl -X POST https://$PE_HOST:$ORCH_PORT/orchestrator/v1/command/task \
  -d '{"task":"service","params":{"action":"restart","service":"nginx"},"scope":{"nodes":["rabid-chairman.delivery.puppetlabs.net"]}}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

Results:

1
2
3
4
5
6
7
8
9
10
% curl -X POST https://$PE_HOST:$ORCH_PORT/orchestrator/v1/command/task \
  -d '{"task":"service","params":{"action":"restart","name":"nginx"},"scope":{"nodes":["rabid-chairman.delivery.puppetlabs.net"]}}' \
  -k -H "Content-Type: application/json" -H "X-Authentication:$TOKEN"

{
  "job" : {
    "id" : "https://puppet-enterprise-instance:8143/orchestrator/v1/jobs/2",
    "name" : "2"
  }
}%

Docs: Puppet Orchestrator POST /command/task endpoint

Tying everything up!

Congratulations! You have the tools to start automating the management of your RBAC in detail. You can divide this effort into teams as you find appropriate, and there is flexibility in the order of these tasks, reducing bottlenecks, scheduling, and handoffs. Instead of clicking, you can create automation that works with your flows and teams.

For more info

API documentation and examples can be found in your PE documentation, e.g. https://puppet.com/docs/pe/latest/pe_user_guide.html For example, you can find RBAC API details here: https://puppet.com/docs/pe/latest/rbac_api_v1.html

1 Your Puppet administrator will be able to point you to your PE host. If you need help finding port numbers, typically a configuration file here will have values, or ask your admin: /etc/puppetlabs/console-services/conf.d/console.conf

2 Tools used:

3 https://puppet.com/docs/pe/latest/grouping_and_classifying_nodes.html

Troubleshooting

  • Get more information about what curl sends and receives with ‘-v’
  • See the body of what you post with curl with ‘--trace-ascii /dev/stdout’
Puppet sites use proprietary and third-party cookies. By using our sites, you agree to our cookie policy.