Skip to content

Simple RESTful API for Server-Client communication purposes

License

Notifications You must be signed in to change notification settings

AkaBlur/mini-share-point

Repository files navigation

Mini-Share-Point

A customizable REST-API in Python


Note

This application provides a simple Server-Client mechanism for securely exchaning small data amounts.

It can be customized with simple to load python modules to call as API-functions in a RESTful way to your liking.

Server deployment is containerized and allows easy configuration

The application is designed in such a way that little to no knowledge over specific Server-Client relationships are maintained.

Quick-Start Guide

Client-Side

  1. Download client.py, gen_key.py and cient_requirements.txt from util for your Client
  2. Create a local virtual environment and install requirements
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
  1. Generate a new key pair named client.key (private) and client.pub (public) for your client
python3 gen_key.py pair client
  1. Note your Clients' Public Key
cat client.pub
iSQhxAx4b3IqGZv0sT7n8zK07ugSYe697WSWDg/fb1Q=

Server-Side

  1. Create a Server instance by pulling the Docker image
docker run -itd --name mini_share_instance -p 8000:8000 ghcr.io/akablur/mini-share-point
  1. Your Server runs now on http://localhost:8000/v1
  2. Open a bash instance in your Server and append the Clients' key to your Server
docker exec -it mini_share_instance /bin/bash
# inside Docker instance
/app$ appendclient iSQhxAx4b3IqGZv0sT7n8zK07ugSYe697WSWDg/fb1Q=
[2024-03-30 11:34:46,540] INFO: Client Hex Secret: 2aed83f3d03b5520ce8e831088b219e333eb74d74243d3e9b4bd8e337229dac727ae4c249990bb6430d1b83cae7221ab19772e13a787087050a1e9529588e621
Server public key:
	p5eAkOXqfOX3OHeMIraFtDBE9khXP+1nkTWoAZv64S4=
  1. Note the Clients' generated Hex Secret and the Servers Public Key
# Clients' Hex Secret
2aed83f3d03b5520ce8e831088b219e333eb74d74243d3e9b4bd8e337229dac727ae4c249990bb6430d1b83cae7221ab19772e13a787087050a1e9529588e621
# Servers Public Key
p5eAkOXqfOX3OHeMIraFtDBE9khXP+1nkTWoAZv64S4=
  1. Restart Server
docker stop mini_share_point
docker start mini_share_point

Client-Side, again

  1. Create a file for the Servers Public Key and the Clients' Hex Secret
# Clients' Hex Secret
printf 2aed83f3d03b5520ce8e831088b219e333eb74d74243d3e9b4bd8e337229dac727ae4c249990bb6430d1b83cae7221ab19772e13a787087050a1e9529588e621 > client.secret
# Servers Public Key
printf p5eAkOXqfOX3OHeMIraFtDBE9khXP+1nkTWoAZv64S4= > server.pub
  1. Test your connection
python3 client.py http://localhost:8000/v1
[2024-03-30 14:20:53,643] INFO: It is currently: 
	Sat Mar 30 13:20:53 2024

Basic functionality

This app provides a Server that exposes a customizable API-endpoint. Communication with this API is only possible for registered Clients.

Communication between Client and Server is done via a customziable REST-API. The user can implement any functionality into the endpoint on the Server to be used by any client.

To provide security a Client needs some prerequisites to be allowed to communicate with the Server:

  • Private-Public-Keypair (for the Client)
  • A shared Secret with the Server
  • A valid Timestamp
  • The Servers Public-Key

For a simple demo there is a possible Client implementation already inside util. It utizilizes requests to perform a simple POST request to a deployed Server instance.
The Server therefore implements a simple demo method to call:

  • time_test
    • Returns the current time

Important

When operation the Server behind a Reverse Proxy, make sure to set the [Proxy] configuration accordingly! Otherwise resolving the Clients IP address won't be possible.

Configuration

Server deployment

Deploying the Server is just as simple as starting a Docker container:

docker run -itd -p 8000:8000 ghcr.io/akablur/mini-share-point:latest

The Server will then run on http://localhost:8000/.

It is also recommended to use docker-compose. Therefore a example compose.yaml is given:

services:
  mini-share-point:
    build: .
    environment:
      - PUID=1000
      - PGID=1000
    ports:
      - 8000:8000
    volumes:
      - ./config-docker:/config
      - ./modules:/app/modules
      - ./log:/log
      - ./sec:/sec

Setting a UID and GID is especially recommended if you want to mount external directories and preserve their ownership.

Configuration

After a successful deployment some configuration might be adjusted to your liking.

Generally there are four important directories that the Server is depending on. These are the internal Docker directories as following:

  • /config
  • /app/modules
  • /log
  • /sec

/config - Server configuration

This directory contains config.ini and modules.ini


config.ini is the configuration file for the Server. Here all settings regarding the main operation of the Server can be adjusted. Directories should not be changed, as these refer to docker internal directories.

Implemented options:
IPAddressTTL

  • time-to-live for internally cached IP addresses (in sec)
  • IP addresses are saved and cached for that amount of time
  • Every requests from non-cached IPs will generate a log entry

RequestTTL

  • time-to-live for a sent request to the Server (in sec)
  • Computed from the sent timestamp
  • When a request times out 401 will be returned

[Proxy]-section

  • Contains settings for possible Proxy headers
  • Set the amount of each header the Server is expected to see (check your proxy for that)
  • Highly recommended when operating behind a reverse proxy

modules.ini

  • Check /app/modules for explanation below 👇

/app/modules - Your custom modules

This directory contains all custom functions your Server can use.

Currently all functions are implemented as python files. Each file needs the following function as entry call:

def entry_call() -> str:
    ...

The custom function must also return a str as result.

All modules that should be used by the Server need to be registered inside the modules.ini. Inside this file all modules are listed in the following manner inside the [Modules] section:

function_name=module-name

function_name refers to the name the module will have when calling it via the API.

module-name is the name of the .py file inside the module directory containing your custom function for the API.

/log - Log directory

Well... log directory

Contains all latest logs

/sec - Key storage

This is the main storage for all Server-related keys.

Note

Handling those manually is not recommended as there are some implemented helper functions to assist with key creation and deletion. This directory should be considered as backup directory when you remove the Docker image and don't want to loose all the configured clients.

See Key-Handling for further information.

Key-Handling

As this Server uses a Public-Private-Key exchange system keeping your keys save and uncompromized there are some considerations when handling those key files.

For Server-Client communication a pair of Private and Public keys is generated. Both Client and Server will generate such a pair.

The Server will only have one keypair whose Public key will be shared by all Clients for encryption. It should be noted that the Server will automatically create a new key pair, when none is given (e.g. through mounting an external directory).

Automatically created key files will always be base64 encoded 32-bit values. It can be possible to use different key lengths but this is discouraged.

Key Generation

A simple method for generating new key pairs is given under util/gen_key.py.

This allows the generation of a single private key file, a public key file for an existing private key file on disk or the generation of a new key pair with a given name. Use as following:

python3 gen_key.py pair my_client

This will generate a new key pair named my_client saved into the current directory. For new clients this is an easy method to prepair a new set of keys.

Server-side Key Management

Warning

It is not recommended to manually add or remove keys!
Instead some methods are given below.

To execute a command simply open a terminal inside the container

docker exec -it my_docker_name /bin/bash

There exist two possible methods:

  • appendclient
  • purgeclients

appendclient

As the name implies this will add a generated Client key to use for a new Client. Just call the function inside the server with the Clients Public Key:

appendclient <CLIENTS-PUBLIC-KEY-BASE64>

This function will also return two values:

  • Server Public Key
  • Client Hex Secret

The Server Public Key is needed for the Client to encrypt traffic that needs to be sent to the Server. When accessing the API the Client Hex Secret needs also to be given inside the JSON request for authentication. This value should therefore also be saved within you client.

Important

Restart the Server after each change in the key storage!

purgeclients

This function will delete all Clients registered inside the Server. Clients need both a valid encryption key and a registered Secret inside the Server. Those are stored inside the Server without a saved relationship between each Client Key and its respecting Secret. For that reason a single Client can't be removed, but all Clients need to be removed and reinstated instead.

Important

Restart the Server after each change in the key storage!

REST API

The API itself is designed in a RESTful way.
The basic endpoint is: http://localhost:8000/v1

Data needs to be sent as JSONified string with appropiate MIME-Type set.

/v1 - endpoint

  • Main communication endpoint
Parameter Value
Method POST
MIME-Type application/json
Data JSON string

JSON data

  • JSON string that the Server expects to receive

Important

All data values need to be encrypted with the Servers Public Key!

Key Value
id Clients Secret
check CRC-32 of the Secret
ts Timestamp, as Unix time
entry Function to call

Important

Make sure you only send the actual values!
When reading in your Client Secret and keys remove any potential newline characters and such!

Return

  • 200 on successful request
  • Data returned is formatted as JSON string
Key Value
value Return value of custom function
  • 401 on every other request (malformed JSON, incorrect Secret, unregistered Key, etc.)

Development

Setup for local dev:

  • Export PYTHONPATH with modules directory to load custom modules
  • Optional: enable debugging with MSP_LOGLEVEL=DEBUG
  • Start local flask Server: flask --app mini_share_point run --debug

About

Simple RESTful API for Server-Client communication purposes

Resources

License

Stars

Watchers

Forks

Packages