Smolkit is a low-footprint drop-in replacement for Polkit. In particular, low-footprint means there is no JavaScript engine required.
The basic purpose of Polkit, and therefore smolkit, is to answer queries
of the form "is user X allowed to perform action Y on service Z?" via
D-Bus, to allow unprivileged users to perform privileged operations
under certain conditions. See this example call of systemctl restart foo.service:
+-------+ +---------+ +---------+
| User | | Systemd | | Smolkit |
+-------+ +---------+ +---------+
| | |
| restart foo.service | |
|------------------------->| |
| | |
| | is User allowed systemd-action |
| | `restart` on `foo.service`? |
| |----------------------------------->|
| | |
| | | [check rules]
| | |--------------
| | | |
| | |<-------------
| | |
| | allowed/denied |
| |<-----------------------------------|
| | |
The heart of the smolkit D-Bus API is the CheckAuthorization method. Given a subject (i.e. user), an action identifier and further data needed to describe the action (e.g. which systemd service, whether to start/stop/restart, ..), it determines whether that specific action is allowed.
There are a number of methods in Polkit's API related to registering and using authentication agents (i.e. ways of showing password prompts integrated in each desktop environment in their specific style). Smolkit doesn't support authentication agents, so we don't implement these.
During start of smolkit, it reads its allow rules from a path supplied
as argument during the boot (e.g. /etc/smolkit/rules.d). Both a singular
rule file and a directory can be supplied.
Allow rules are JSON arrays containing one or more rules supplying a subject, an action-id and details. A small example would look like the following:
[
{
"subject": {
"user": "sec_smolkit"
},
"action_id": "org.freedesktop.systemd1.manage-units",
"details": {
"unit": "smolkit-test-start.service",
"verb": ["start", "stop", "restart"]
}
}
]This JSON file allows the user sec_smolkit to start/stop/restart the systemd unit smolkit-test-start.service.
Both unit and verb can take arrays as values.
The primary objective of smolkit is to work without a JavaScript
engine. Instead, the rules config is basically a list of allowed values
of the CheckAuthorization parameters.
The data structures representing the D-Bus side parameters of the
CheckAuthorization call are currently just type aliases over the
sdbus transport types. We may add separate classes for them as needed.
On the other hand there will be structures for allow rules containing subject, action ID and details as loaded from the allow rules JSON file. While the D-Bus types have specific info like a UID, the rules might have different information depending on the use case, e.g. a unix group name that is allowed.
This means there is some interpretation to be done in order to check whether a rule matches a query from D-Bus.
The SDK container is built from the Dockerfile. The CI creates this docker
container for us. For building locally there is
scripts/run-sdk-docker.sh which will rebuild
the SDK container if necessary. This also offers the possibility to run any
commands in the container, or just a shell if no command is given.
The build commands are collected in scripts/build.sh,
so a local build can be triggered using scripts/run-sdk-docker.sh scripts/build.sh. Check the help output of scripts/build.sh for additional
options like running tests or switching the compiler to Clang++.
There will be different types of tests in corresponding subfolders, e.g. tests/unit and tests/component.
There are two interfaces smolkit uses and that need to be replaced with doubles during tests: the interface to the Linux system, e.g. for looking up users and groups, and the D-Bus connection, including the services that send us requests.
The system D-Bus connection is replaced by locally started instances (see the setup code in the D-Bus interactions component test) on which we can send requests to smolkit and test the responses.
The system interface, i.e. the user lookup functions, are part of libc,
and less simple to replace directly. We have added a wrapper class
SystemUserLookup that allows intercepting calls in a mock class for
unit tests (see e.g. the MockSystemUserLookup
class in the subject unit tests).
For component tests, we exchange these libc functions with
mocks during dynamic
loading using the LD_PRELOAD mechanism (please refer to man 8 ld-linux for details). The implementation of this mock is very much
hard coded:
getpwuid()only knows about usersomeUserwith UID9001and returns no username for any other UID.getgrnam()only knows about groupsomeGroup(withsomeUseris a member of).
Copyright (C) 2022-2026 BMW Car IT GmbH Contact: http://www.bmw-carit.de/
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.