A Python framework to allow untrusted users to perform privileged system tasks.
Regent comes in two parts:
- a service which runs as the privileged system user, defines a set of operations it will perform, and listens for requests on a linux socket file
- a client library to ask the service to perform the operations
A service is intended for use with clients on a single host. Alternatively its socket can be mounted within a docker container to control its host or other containers.
The authentication system is designed on the assumption that the unprivileged user is untrusted and can be compromised. For non-harmful operations a basic shared key will deter casual attackers, and for more high-risk commands it supports out-of-channel activation, to allow two-factor authentication or administrator approval.
A service which defines a system command (whoami) and returns its output:
import subprocess
from regent.service import Operation, Service
class WhoAmI(Operation):
def perform(self):
value = subprocess.check_output("whoami")
value = value.strip().decode("utf-8")
return value
service = Service(
socket_path="/tmp/regent-whoami.sock",
socket_secret="123456",
)
service.register("whoami", WhoAmI)
service.listen()
A client which calls the service:
from regent.client import Client
client = Client(
socket_path="/tmp/regent-whoami.sock",
socket_secret="123456",
)
response = client.request("whoami")
print(response["data"])
These examples can be found in the examples dir and can be run with:
DEBUG=1 python examples/backend/whoami.py python examples/frontend/whoami.py
More complicated examples with validation and enhanced authentication can be found in
the examples dir, including:
- make changes to the firewall
- restart the server
Regent uses human-readable JSON, terminated in a newline. Using socat:
socat - UNIX-CONNECT:/tmp/my-regent.sock
send the following, ending in a UNIX-style newline (\n):
{"secret": "123456", "op": "my-op"}
and you'll receive your response:
{"error": "something failed"}
This is the raw API between the client and service. Knowledge of this will not be required in normal Regent use if you're using a client.
A connection to the service API should send a JSON object with the following key/values:
secret- Socket secret
op- Operation name
data- Optional: Data for the operation
The service will return either:
error- Error message
or
success- True
uid- Unique ID for this operation request, or null if complete
data- Data from the operation or pending async auth
JSON objects should be terminated with a newline.
If the original operation requires an asynchronous authentication step, the client should send the following JSON object:
secret- Socket secret
uid- UID for a stored operation request (passed from async auth)
data- Data for authenticating the auth request
The response will be a standard response object described above.
0.1.0 - 2022-11-19
- First release of Python version rewritten from original Perl
- Migrate out-of-channel email approval using OTPs
- Add out-of-channel app-based 2FA using TOTP
- Migrate to new socket backend to support asyncio and encrypted TCP sockets
- Improve logging