148 lines
4.0 KiB
Markdown
148 lines
4.0 KiB
Markdown
|
# Encrypted Config file API Service
|
|||
|
|
|||
|
This project implements a secure, encrypted API service using CherryPy and
|
|||
|
Python. The API allows clients to securely interact with config files. It
|
|||
|
supports reading, writing and modifying content. Complete files or sections /
|
|||
|
topics from the files. The service supports creating backup copies before every
|
|||
|
modifying action. The service employs JWE (JSON Web Encryption) tokens for
|
|||
|
authentication, along with ECC (Elliptic Curve Cryptography) for key exchange
|
|||
|
and AES for encrypting request and response bodies.
|
|||
|
|
|||
|
I strongly recomend running this behind reverse proxy (like nginx) that
|
|||
|
provides TLS communication between the server and client.
|
|||
|
|
|||
|
## Features
|
|||
|
|
|||
|
* JWE Token Authentication:
|
|||
|
The service uses encrypted JWE tokens for authentication and key exchange.
|
|||
|
|
|||
|
* ECC for Key Exchange:
|
|||
|
The client encrypts a symmetric key using the server’s ECC public key,
|
|||
|
which is used for AES encryption/decryption.
|
|||
|
|
|||
|
* AES Encryption:
|
|||
|
Both request and response bodies are encrypted using AES with a single use
|
|||
|
symmetric key, ensuring data confidentiality.
|
|||
|
|
|||
|
* Config File Handling:
|
|||
|
Supports reading, writing, and updating config files. Initially designed
|
|||
|
for WireGuard configs but easily extendable to any config format.
|
|||
|
|
|||
|
* POST-Only API:
|
|||
|
The API only accepts POST requests for all interactions.
|
|||
|
|
|||
|
* Single API Endpoint:
|
|||
|
The entire service operates through a single endpoint, keeping the API
|
|||
|
interface minimal and clean.
|
|||
|
|
|||
|
## Requirements
|
|||
|
|
|||
|
Python 3.11+
|
|||
|
CherryPy
|
|||
|
cryptography
|
|||
|
requests
|
|||
|
|
|||
|
Some controllers (file handlers) may have other dependencies.
|
|||
|
|
|||
|
## Config
|
|||
|
|
|||
|
config.toml
|
|||
|
|
|||
|
The config.toml file is used to configure the server, logging, and the paths to
|
|||
|
the config files. Here’s a sample configuration:
|
|||
|
|
|||
|
[server]
|
|||
|
host = "0.0.0.0"
|
|||
|
port = 8000
|
|||
|
base_url = '/'
|
|||
|
|
|||
|
[logging]
|
|||
|
log_file = "service.log"
|
|||
|
debug = true
|
|||
|
|
|||
|
[files]
|
|||
|
wireguard_wg0 = ["/etc/wireguard/wg0.conf", backup=true]
|
|||
|
|
|||
|
[client_keys]
|
|||
|
public_key = """
|
|||
|
-----BEGIN PUBLIC KEY-----
|
|||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA.....
|
|||
|
-----END PUBLIC KEY-----
|
|||
|
"""
|
|||
|
|
|||
|
- server: Defines the host, port and the url where the service will run.
|
|||
|
- logging: Configures the log file location and debug mode.
|
|||
|
- files: Key/[value] list of config files and their settings.
|
|||
|
- client_keys.public_key: The client’s ECC public key (used for verifying
|
|||
|
signatures and decrypting data).
|
|||
|
|
|||
|
Ensure the config.toml is in the same directory as your application.
|
|||
|
|
|||
|
## Sending Requests
|
|||
|
|
|||
|
All API requests must be sent as POST requests with an encrypted body. The
|
|||
|
encryption key is exchanged using the ECC-based JWE token provided in the
|
|||
|
Authorization header.
|
|||
|
|
|||
|
Headers:
|
|||
|
|
|||
|
Authorization: Must contain the JWE token in the format Bearer <token>.
|
|||
|
|
|||
|
Request Body:
|
|||
|
|
|||
|
The body of the request must be encrypted using AES, with the symmetric key
|
|||
|
provided inside the JWE token.
|
|||
|
|
|||
|
|
|||
|
Reading a config file:
|
|||
|
|
|||
|
{
|
|||
|
"action": "read_file",
|
|||
|
"file": "wireguard_wg0"
|
|||
|
}
|
|||
|
|
|||
|
Writing to a config file:
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
"action": "write_file",
|
|||
|
"file": "wireguard_wg0",
|
|||
|
"data": {
|
|||
|
[Interface]
|
|||
|
PrivateKey = PrivateKey123=
|
|||
|
Address = 10.10.200.1/32
|
|||
|
ListenPort = 51820
|
|||
|
|
|||
|
[Peer]
|
|||
|
PublicKey = PublicKey123=
|
|||
|
AllowedIPs = 10.10.200.2/32
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
## Response Structure Details
|
|||
|
|
|||
|
Response is json document like:
|
|||
|
|
|||
|
{
|
|||
|
"data": "<encrypted_data>",
|
|||
|
"signature": "<base64-encoded signature>"
|
|||
|
}
|
|||
|
|
|||
|
The decrypted data could look like this:
|
|||
|
|
|||
|
{
|
|||
|
"status": 200,
|
|||
|
"timestamp": "2024-10-22T10:30:00Z",
|
|||
|
"message": "File written successfully"
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
## Error Handling
|
|||
|
|
|||
|
400 Bad Request: Returned if the request is malformed or the token is invalid.
|
|||
|
401 Unauthorized: If the Authorization header or the JWE token is missing or invalid.
|
|||
|
403 Forbidden: If the signature validation fails.
|
|||
|
500 Internal Server Error: For unexpected errors in processing the request.
|