# rcond [![main](https://github.com/0x1d/rcond/actions/workflows/main.yaml/badge.svg)](https://github.com/0x1d/rcond/actions/workflows/main.yaml) A distributed management daemon designed to remotely configure system components, including: - Network connections: Manage network connections through NetworkManager's D-Bus interface - System hostname: Update the system's hostname - Authorized SSH keys: Manage the user's authorized_keys file to add, remove, or modify authorized SSH keys - System state: Restart and shutdown the system - Cluster: Join and manage a cluster of rcond nodes ## Requirements - Make - Go - NetworkManager - systemd - Linux ## Installation In order to build and install `rcond` as a systemd service, you need to specify the target architecture and then run the install make target. ```sh export ARCH=arm64 make install ``` ## Development There are several make targets available: ```sh Available targets: generate: Generate server from Swagger specs test: run tests build: build binary for target $ARCH install: build and install binary for target $ARCH as systemd service uninstall: uninstall systemd service run: run and build binary for target $ARCH dev: run go programm dev-agent: run go programm with agent config upload: upload binary of given $ARCH to rpi-test ``` ## Configuration The default config file location is `/etc/rcond/config.yaml`. It can be overwritten by environment variables and flags. An full example configuration with comments can be found in `config/rcond.yaml` ### API Server The API server is the main component of the rcond daemon. It is responsible for managing the host and providing a REST API for managing the system. Example configuration: ```yaml rcond: addr: 0.0.0.0:8080 api_token: 1234567890 ``` ### Cluster The cluster agent is a component of rcond that is responsible for joining and managing a cluster of rcond nodes. This functionality can be used to manage and configure multiple hosts through a single API server. In the background, the cluster agent will use [Serf](https://github.com/hashicorp/serf) to form a cluster, handle node discovery and gossip. Forming a cluster is optional and can be enabled by configuring the cluster section in the config file. Example configuration: ```yaml cluster: # Enable the cluster agent enabled: true # Name of the node in the cluster node_name: rcond # Secret key for the cluster agent used for message encryption. # Must be 32 bytes long and base64 encoded. # Generate with: base64 /dev/urandom | tr -d '\n' | head -c 32 secret_key: DMXnaJUUbIBMj1Df0dPsQY+Sks1VxWTa # Advertise address for the cluster agent advertise_addr: 0.0.0.0 # Advertise port for the cluster agent advertise_port: 7946 # Bind address for the cluster agent bind_addr: 0.0.0.0 # Bind port for the cluster agent bind_port: 7946 # Join addresses for the cluster agent join: - 127.0.0.1:7947 ``` ### Environment Variables | Environment Variable | Description | Default | |----------------------|-----------------------------------------|---------------| | RCOND_ADDR | Address to bind the HTTP server to. | 0.0.0.0:8080 | | RCOND_API_TOKEN | API token to use for authentication. | N/A | ## API The full API specification can be found in [api/rcond.yaml](api/rcond.yaml). ### Authentication All endpoints except `/health` require authentication via an API token passed in the `X-API-Token` header. The token is configured via the `RCOND_API_TOKEN` environment variable when starting the daemon. ### Endpoints | Method | Path | Description | |---------|-------------------------------------|-----------------------------------------| | GET | `/health` | Health check endpoint | | POST | `/network/ap` | Create a WiFi access point | | POST | `/network/sta` | Connect to a WiFi access point | | PUT | `/network/interface/{interface}` | Activate a connection | | DELETE | `/network/interface/{interface}` | Deactivate a connection | | DELETE | `/network/connection/{uuid}` | Remove a connection | | GET | `/hostname` | Get the hostname | | POST | `/hostname` | Set the hostname | | POST | `/users/{user}/keys` | Add an authorized SSH key | | DELETE | `/users/{user}/keys/{fingerprint}` | Remove an authorized SSH key | | POST | `/system/restart` | Restart the system | | POST | `/system/shutdown` | Shutdown the system | | GET | `/cluster/members` | Get the cluster members | | POST | `/cluster/join` | Join cluster nodes | | POST | `/cluster/leave` | Leave the cluster | | POST | `/cluster/event` | Send a cluster event | ### Response Codes - 200: Success - 400: Bad request (invalid JSON payload) - 405: Method not allowed - 500: Internal server error ### Request/Response Format All endpoints use JSON for request and response payloads. ## Cluster Events Cluster events are used for broadcast messages to all nodes in the cluster. They are sent as HTTP POST requests to the `/cluster/event` endpoint. The request body should be a JSON object with the following fields: | Field | Description | Optional | |----------|-----------------------------------------|-----------| | `name` | The name of the event | No | | `payload`| The payload of the event | Yes | The response will be a JSON object with the following fields: | Field | Description | Optional | |----------|----------------------------------------------------------------------------------|-----------| | `status` | The status of the event. This is a string, either "success" or "error". | No | | `error` | If the status is "error", this field will contain a string describing the error. | Yes | Following events are implemented: | Event Name | Description | Payload | |------------|----------------------|---------| | restart | Restart the cluster | N/A | | shutdown | Shutdown the cluster | N/A | ## Examples ### Connect to a WiFi Access Point This example will automatically connect to a WiFi access point with the given SSID and password on the interface "wlan0". ```bash curl -X POST "http://rpi-test:8080/network/sta" \ -H "Content-Type: application/json" \ -H "X-API-Token: 1234567890" \ -d '{ "interface": "wlan0", "ssid": "MyAccessPoint", "password": "StrongPassword", "autoconnect": true }' ``` ### Setup an Access Point This example will create an access point on the interface "wlan0" with the given SSID and password. ```bash curl -X POST "http://rpi-test:8080/network/ap" \ -H "Content-Type: application/json" \ -H "X-API-Token: 1234567890" \ -d '{ "interface": "wlan0", "ssid": "MyAccessPoint", "password": "StrongPassword", "autoconnect": true }' ``` ### Restart the cluster This example will restart all nodes in the cluster ```bash curl -X POST "http://rpi-test:8080/cluster/event" \ -H "accept: application/json" \ -H "X-API-Token: 1234567890" \ -d '{ "name": "restart" }' ```