The manager is a standard application software and does not require privileged access to operating system features. The manager service can operate fully when running under a non-privileged operating system user.
To work with the repository, the manager needs a separate database where service information is stored. Also, the DBMS user with the following rights is required:
The right to LOGIN to the instance.
The repository database rights:
the ownership of the database
the right to connect to the database
no restrictions on access rights within the database (to perform migrations in the data schema)
The agent is a standard application software that requires the following for full operation:
access to operating system features
access to the managed DBMS instance
To implement most features, the agent only requires the access level of a non-privileged operating system user. There is a small number of features that require privileged access. To maintain this functionality, additional system configuration and granting of the necessary rights are required. Without the configuration and privileges, the agent cannot perform operations, which adversely affects PPEM functionality. It is recommended to complete all necessary configurations before running the agent.
Access to the managed DBMS instance can be divided into the following parts:
Access to files and directories of the DBMS instance, which is provided using operating system access levels. The user on whose behalf the agent is running must have access to the main data directory.
By default, the main data directory is initialized by the
postgres owner with
0600 rights, so most DBMS installations
restrict access to this configuration. Therefore, the
optimal operational approach is to run the agent under the
postgres system user.
Access to the SQL interface of the DBMS instance, for which the agent requires the DBMS user with the following rights:
the right to LOGIN to the instance
the right to connect to all instance databases
the membership of the pg_monitor and
pg_signal_backend roles
Networking between the manager and agent can be initiated by either side:
The manager can send control instructions to agents, and agents return the execution results in response.
Agents can send to the manager requests to register themselves and DBMS instances as well as to update the state of instances. The manager, in turn, sends a response to the agents’ requests.
The manager and agent use the HTTPS protocol for communication.
By default, the manager uses the tcp/8080 port, and the agent
uses the tcp/8081 port. This traffic direction must be taken
into account when configuring network access rules. The address
and port are specified in the ppem-manager.yml manager
and ppem-agent.yml agent configuration files.
For secure data transfer, TLS configuration is recommended.
To work with the manager, user authentication and authorization are required. Authentication can be performed using:
Built-in PPEM tools — user and group data is stored in the repository and managed by the PPEM administrator.
An external LDAP directory (OpenLDAP or Active Directory). If you use the external directory, the manager must be configured to work with this directory. All user and group data is stored in an external directory and managed by a dedicated LDAP directory administrator.
The manager and agent API is secured by authorization. To execute API requests, the manager and agent perform mutual authentication and issue access tokens for subsequent authorization. The tokens have a limited lifetime — the manager and agents autonomously track token expiration and renew them when necessary.
There are three interaction types that require authentication and authorization during the manager’s operation:
All interactions are carried out via the HTTP or HTTPS protocol depending on the PPEM parameters.
In this interaction type, users work in the web application.
The user sends a POST /v1/sessions API
request to the manager’s endpoint to obtain access and refresh
tokens for subsequent operations. The API request contains
user credentials.
In the basic scenario, credentials are checked against the repository. When using LDAP authentication, credentials are first checked against the directory service, then if they are not found, they are checked against the repository.
Upon successful authentication, subsequent HTTP/HTTPS requests
from users to the manager include the
Authorization header, where the
Bearer keyword is followed by a space and
the access token text. For example:
headers: Content-Type: application/json Authorization: "Bearer eyJhbG..."
The granted access is determined using the role-based access control (RBAC) model in accordance with the roles assigned to the user:
You can assign roles to PPEM users directly or through PPEM groups they are members of.
You can assign roles to LDAP users only through PPEM groups they are members of.
To add an LDAP user to a PPEM group, the administrator must map the distinguished name (DN) of the user’s LDAP group to the PPEM group name. The user is then automatically added to the PPEM group when logging in to the web application.
The user group composition is periodically checked against the LDAP server and updated if necessary.
For more information about mapping LDAP groups to PPEM groups, refer to Integration with OpenLDAP and Active Directory.
In this interaction type, the manager sends API requests to agents to perform various operations.
The manager sends a POST /v1/sessions API
request to the agent’s endpoint to obtain access and refresh
tokens for subsequent operations. The API request is made via
the URL for connecting the agent to the manager from the
repository and contains two parameters:
name: The agent name from the
repository.
api_key: The API key for connecting the
agent to the manager from the repository.
Upon successful authentication, subsequent HTTP/HTTPS requests
from the manager to agents include the
Authorization header, where the
Bearer keyword is followed by a space and
the access token text. For example:
headers: Content-Type: application/json Authorization: "Bearer eyJhbG..."
The agent verifies the authenticity of the manager’s access
token. To do this, the agent generates a token based on the
known values of the name and
api_key parameters. If the generated and
received tokens match, the authentication is successful. If
the authenticity is confirmed and the requested endpoint is
found, further work is allowed.
In this interaction type, agents send reports on the API command execution and updates about serviced instance objects to the manager.
The agent sends a POST /v1/sessions API
request to the manager’s endpoint to obtain access and refresh
tokens for subsequent operations. The API request is made via
the URL for connecting the agent to the manager, which is
specified in the ppem-agent.yml agent
configuration file using the
agent.manager.url parameter. The API
request contains two parameters:
name: The agent name, which is
specified in the ppem-agent.yml agent
configuration file using the agent.name
parameter.
api_key: The API key for connecting the
agent to the manager, which is specified in the
ppem-agent.yml agent configuration file
using the agent.manager.api_key
parameter.
Upon successful authentication, subsequent HTTP/HTTPS requests
from the agent to the manager include the
Authorization header, where the
Bearer keyword is followed by a space and
the access token text. For example:
headers: Content-Type: application/json Authorization: "Bearer eyJhbG..."
The manager verifies the authenticity of the agent’s access
token. To do this, the manager generates a token based on the
known values of the name and
api_key parameters. If the generated and
received tokens match, the authentication is successful. If
the authenticity is confirmed and the requested endpoint is
found, further work is allowed.
The granted access is determined by the rules specified in the PPEM code for how the agent should access manager resources.
The lifetime of access and refresh tokens is limited. It can be
specified in the ppem-manager.yml manager
or ppem-agent.yml agent configuration file
using the jwt.lifetime.access and
jwt.lifetime.refresh parameters.
When the access token expires, the manager or agent starts responding to the token owner with the “401 Unauthorized” error and the “ERR_AUTH_TOKEN_EXPIRED” code, for example:
{
"error":{
"code":"ERR_AUTH_TOKEN_EXPIRED",
"title":"token is expired"
}
}
To get a new access token, the token owner must send a
PUT /v1/sessions API request to the endpoint
with the refresh token that was received along with the expired
access token. This results in new access and refresh tokens
that can be used for further work.
If the refresh token has also expired, the token owner must re-authenticate.
Access control in PPEM is implemented via the RBAC (Role-Based Access Control) model, which defines access restriction rules through roles and privileges. The model establishes the following basic conventions:
Object is a resource to which access should be granted or restricted.
Subject is a person (user) or an automated agent.
Privilege is a permission granting access to perform an operation with the object.
Role is a job function or title defined at the authorization level.
The basic conventions define the following extended conventions:
Access rule is a combination of a role, privileges, and their relationships.
Session is an association between a subject and a role.
One subject can have several roles.
One role can belong to several subjects.
One role can have several privileges.
One privilege can belong to several roles.
Privileges are not assigned to subjects directly but are acquired by subjects through roles.
Fundamental principles:
PPEM functionality is composed of plugins.
Each plugin helps to manage a specific resource.
Resources can be managed by basic CRUD operations: create, view, edit, and delete.
Additionally, resource management may include various RPC operations. Their names depend on the resource management operation.
Privileges allow subjects to perform certain operations. All resources are managed through privileges (basic minimum requirements):
RESOURCE\_create
RESOURCE\_view
RESOURCE\_edit
RESOURCE\_delete
Privileges are included in roles.
Roles can be assigned to a subject both at the time of subject creation and later.
When the plugin receives a request from a subject to perform an operation, it checks the subject’s permission to access the requested operation.
Subjects (users) can create their own roles (if they have privileges) and assign these roles to other subjects.
Privileges and roles may be object-specific. This means that you can specify a role and privilege that allows access to a limited set of objects.
Binding to an object can be done at the role level (all subjects with this role have access to the object) or at the user level (only one user has access to the object).
The following may act as subjects:
Users. Roles are assigned to users at creation time. If roles are not explicitly specified, a default role may be assigned.
Agents. This role is assigned when creating or registering agents.
Objects can be either resources or their representations in the repository, such as servers, agents, instances, users, and user groups.
On the first PPEM startup, the repository initialization is performed, during which system tables are populated (privileges and roles).
During initialization, each plugin is assigned its own set of privileges, roles, and “role-privilege” associations. For example, the accounts plugin has dedicated privileges and roles for managing access to its objects. Other plugins receive their own sets of privileges and roles to control access to their respective objects.
If users are granted necessary privileges, they can create custom roles, specify privilege sets, link roles to objects, and assign custom roles to subjects.
Access privleges are implemented by the following set of tables, which are owned by the accounts plugin:
privileges:
Privileges with object classes they regulate access to.
roles:
Roles with target object classes.
role_privileges:
The “role-privilege” association defines
relationships between roles and privileges included in them.
users:
Users of the system (subjects).
user_roles:
The “user-role” association that defines
relationships between users and roles assigned to them.
user_privileges:
The view that displays the “user-privilege”
associations.
groups:
The groups of the system.
group_roles:
The “group-role” association defines
relationships between groups and roles assigned to them.
group_users:
The “group-user” association defines
relationships between groups and users included in them.
privileges Table #The table has the following fields:
id: The unique privilege ID.
name: The unique privilege name.
title: The privilege description.
class: The class of objects to which
the privilege grants access.
source: The name of the plugin that
sets the privilege and then performs the access check.
The table is populated by the manager using migrations. Each plugin determines its own set of privileges.
An HTTP middleware handler is used to check privileges. Creation, modification, and deletion of privileges by the user is not supported, since the privilege check is implemented in the manager code.
roles Table #The table has the following fields:
id: The unique role ID.
name: The unique role name.
title: The role description.
class: The class of objects to which
the privilege grants access. The field value is used as a
UI hint to get privileges of the corresponding class and
the list of objects of this class.
source: The name of the plugin that
sets the role. For custom roles, the
user value is set.
Roles are created by the manager using migrations. The basic role set is established for the accounts plugin.
With the manager API, users can create, modify, and delete custom roles, but they cannot modify or delete roles set by the manager (system roles).
role_privileges Table #The table defines associations between roles and privileges and has the following fields:
id: The unique association ID.
role: The role ID.
privilege: The privilege ID.
parametric: Specifies whether object
binding is used.
object: The unique ID of an arbitrary
class object.
The object class is determined by the
privileges.class field value. The object ID
declared here restricts the privilege specified in
role_privileges.privilege to a single
object and allows access to the object only by members of a
single role specified in
role_privileges.role (access is granted to
users in user_roles.user who have
user_roles.role =
role_privileges.role).
The combination of role,
privilege, and object
is a unique key with the object IS NOT NULL
condition.
A parameterized object-specific “role-privilege”
association determines access
only to a single object (when specifying
object). If access is required for N
objects, N entries in the role_privileges
table are created.
users Table #The table has no RBAC-related fields. When creating a user via the manager API, you can specify a list of role IDs that will be assigned to the user.
user_roles Table #The table defines associations between users and roles and has the following fields:
id: The unique association ID.
user: The user ID.
role: The role ID.
object: The unique ID of an arbitrary
class object.
The object class is determined by the
privileges.class field value. The object ID
declared here restricts the privilege specified in
role_privileges.privilege to a single
object and allows access to the object by the user specified
in user_roles.user.
A parameterized object-specific
“role-privilege” association determines access
only to a single object (when specifying
object). If access is required for N
objects, N entries in the role_privileges
table are created.
user_privileges View #This view shows users with their roles and privileges and makes it easier to check privileges of a specific subject:
user:
user_roles.user
role:
user_roles.role
privilege:
privilege.name
object:
user_roles.objects or
role_privileges.object
groups Table #The table has no RBAC-related fields.
When creating a group, you can specify a list of role IDs that will be assigned to the group and, as a result, to all users included in it.
group_roles Table #The table defines associations between groups and roles and has the following fields:
group_id: The group ID.
role_id: The role ID.
object: The unique ID of an arbitrary
class object.
The object class is determined by the
privileges.class field value. The object ID
declared here restricts the privilege specified in
role_privileges.privilege to a single
object and allows access to the object by the user specified
in group_roles.role_id.
A parameterized object-specific “group-role”
association determines access only to a single object (when
specifying object). If access is required
for N objects, N entries in the
group_roles table are created.
group_users Table #The table defines associations between groups and users and has the following fields:
group_id: The group ID.
user_id: The user ID.
The “group-user” associations are not object-specific.
In general, it is assumed that roles can include privileges allowing access to all objects of any class.
For finer-grained access control, you can specify both the class
and the object ID in role_privileges.object.
This ensures that the role and privilege apply only to the
object with the specified ID. The object can be specified in
multiple locations:
In role_privileges.object for a role. In
this case, object access is granted to all role members.
In user_roles.object for a user. In this
case, access is granted only to a single user.
In group_roles.object for a group. In
this case, access is granted only to group users.
During the subject authentication, a session itself and session
JWT tokens are created. When an access token is created, it
includes user_id. When a user makes
requests to perform operations, the access token is attached to
the request headers.
The manager performs authorization,
extracts the user’s user_id from the access token and
checks for the privilege in roles. If the privilege is available,
access is granted. If the privilege is unavailable, the request
is rejected.
The following is required to check that a subject has the permission to perform an operation with an object:
user_id or agent_id:
The user or agent ID.
class: The class name for the object
(resource type).
object: The object ID
(optional).
The client specifies the
Authorization: Bearer header in the request
and attaches the access token.
The server receives the request and extracts the data needed to verify access:
The user_id (or
agent_id) value is extracted from the
access token.
The class value is defined based on the
privilege.
The object values:
For GET requests of the /resources
type, the values are extracted from the URL and request
parameters (ids=?).
For GET requests of the
/resources/objectID type, the values
are extracted from the URL.
For PUT requests, the values are extracted from the request body.
For DELETE requests of the /resources
type, the values are extracted from the request body (a
separate object field).
To verify that a user has a specific privilege, the handler may
need a mapping of operation IDs to their corresponding
privileges. The verification is performed via the repository and
the user_privileges view.