diff --git a/docs/server/_sources/administration_guide/access_token.md.txt b/docs/server/_sources/administration_guide/access_token.md.txt new file mode 100644 index 0000000..56bf409 --- /dev/null +++ b/docs/server/_sources/administration_guide/access_token.md.txt @@ -0,0 +1,77 @@ +# Access token + +---- + +The Anweddol server implementation can restrict its utilization by using the `tools` [Access token feature](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/access_token.html) for client authentication. + +An access token is a url-safe 124 characters long used on the implementation to restrict access. +These tokens are stored in a SQLite database file on the system. + +**NOTE** : There is one token for one client, since a client cannot store 2 tokens for one same server (see the [Client usage guide](https://anweddol-client.readthedocs.io/en/latest/usage_guide/index.html) to learn more). + +## Prerequisites + +This feature can be enabled or disabled. +Being disabled by default, you can enable it by changing the `enabled` field value for `True` : + +``` +[...] + # List of allowed / denied IPs + # Specify any IPs with 'any' + allowed_ip_list: + - any + denied_ip_list: [] + +# --- +# Access token parameters +token_authentication: + + # Enable this feature or not + # If this value is set to 'false', all parameters below will be ignored + enabled: True # <----- Here + +[...] +``` + +Then restart the server. + +**NOTE** : If this feature is enabled but no tokens are added, no clients will be able to use the server. + +## Add / delete a token + +To add a token to the database, execute : + +``` +$ anwdlserver access-tk -a +``` + +It will result with the created token and its entry ID on the standard output. + +On the client-side, you need to record this token in order to be able to authenticate. +See the [Client usage guide](https://anweddol-client.readthedocs.io/en/latest/usage_guide/index.html) to learn more. + +**NOTE** : Since the access tokens are hashed in the database (see the technical specifications [Access token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/access_token.html) section to learn more), there's no way to see them again in plain text : Store this plain token somewhere safe in order to use it for further operations. + +If you want to delete a token, execute : + +``` +$ anwdlserver access-tk -r +``` + +## Enable / Disable a token + +You have the possibility to enable or disable recorded tokens to temporarily disable its usage. + +To disable a token, execute : + +``` +$ anwdlserver access-tk -d +``` + +**NOTE** : A created token is automatically enabled. Use the `--disabled` option with the `-a` parameter to disable it. + +And to re-enable it : + +``` +$ anwdlserver access-tk -e +``` \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/configuration_file.md.txt b/docs/server/_sources/administration_guide/configuration_file.md.txt new file mode 100644 index 0000000..8e7b059 --- /dev/null +++ b/docs/server/_sources/administration_guide/configuration_file.md.txt @@ -0,0 +1,7 @@ +# Configuration file + +---- + +The Anweddol server implementation is using a YAML file for configuration. + +It is stored in `/etc/anweddol/config.yaml` after installation, read its header to learn about it. \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/container_iso.md.txt b/docs/server/_sources/administration_guide/container_iso.md.txt new file mode 100644 index 0000000..8ae959c --- /dev/null +++ b/docs/server/_sources/administration_guide/container_iso.md.txt @@ -0,0 +1,15 @@ +# Container ISO + +---- + +In order to provice a valid service, containers domains needs to run with a specific ISO image. + +## Download the ISO image + +This ISO image is actually a custom live Debian image that you can retrieve on the [official mirror](https://mega.nz/folder/BTFyVCLB#DNC2K8Lmhgbk6QWrVpeznw). + +You'll need to copy the downloaded `anweddol_container.iso` file on the `/etc/anweddol/iso/` folder by default, or you can specify the ISO path in the configuration file, in the `container_iso_path` key. + +> This ISO image is meant to be replaced at each update announced + +Learn more about the container ISO on the technical specifications [Container OS section](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#container-os). diff --git a/docs/server/_sources/administration_guide/index.md.txt b/docs/server/_sources/administration_guide/index.md.txt new file mode 100644 index 0000000..74ca7b1 --- /dev/null +++ b/docs/server/_sources/administration_guide/index.md.txt @@ -0,0 +1,70 @@ +# Administration guide + +---- + +Hello and welcome to the Anweddol server administration guide. + +The following sections will explain how to administrate and configure the Anweddol server implementation in an optimal way. + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +installation +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +server_usage +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +access_token +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +configuration_file +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +container_iso +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +logging +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +troubleshooting +``` \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/installation.md.txt b/docs/server/_sources/administration_guide/installation.md.txt new file mode 100644 index 0000000..10d8fa9 --- /dev/null +++ b/docs/server/_sources/administration_guide/installation.md.txt @@ -0,0 +1,212 @@ +# Installation + +---- + +This section covers the prerequisites needed for the system before the Anweddol server installation. + +## Anweddol server installation + +Install the Anweddol server by the sources : + +``` +$ git clone https://github.com/the-anweddol-project/Anweddol-server.git +$ cd Anweddol-server +$ sudo pip install . +``` + +The nessessary files and users will be created during the installation. + +**NOTE** : If the pip installation is launched with non-root permissions, only the `anwdlserver` package will be installed : the full setup will be skipped. + +## Environment setup + +### Libvirt + +[Libvirt](https://libvirt.org/) is a toolkit to manage virtualization platforms that the server is using to manage container domains. + +#### Installation + +Libvirt must be installed via your package manager : + +Apt : + +``` +$ sudo apt-get update +$ sudo apt-get install python-libvirt +``` + +DNF : + +``` +$ sudo dnf install python-libvirt +``` + +Yum : + +``` +$ sudo yum install python-libvirt +``` + +**NOTE** : You can also install it from pip by executing : + +``` +$ pip install libvirt-python +``` + +But you need to install build dependencies manually, and this topic is not covered in this documentation. + +#### Setup + +You need to modify the `/etc/libvirt/qemu.conf` file to give libvirt appropriate rights. + +Edit the `/etc/libvirt/qemu.conf` file : + +``` +$ sudo nano /etc/libvirt/qemu.conf +``` + +Find the `user` and `group` directives. By default, both are set to `root` : + +``` +[...] +Some examples of valid values are: +# +user = "qemu" # A user named "qemu" +user = "+0" # Super user (uid=0) +user = "100" # A user named "100" or a user with uid=100 +# +#user = "root" +The group for QEMU processes run by the system instance. It can be +specified in a similar way to user. +#group = "root" +[...] +``` + +Uncomment both lines and replace `root` with `anweddol` and the group with `libvirt` as shown below: + +``` +[...] +Some examples of valid values are: +# +user = "qemu" # A user named "qemu" +user = "+0" # Super user (uid=0) +user = "100" # A user named "100" or a user with uid=100 +# +user = "anweddol" +The group for QEMU processes run by the system instance. It can be +specified in a similar way to user. +group = "libvirt" +[...] +``` + +Then restart the libvirtd daemon : + +``` +$ sudo systemctl restart libvirtd.service +``` + +(source : [ostechnix](https://ostechnix.com/solved-cannot-access-storage-file-permission-denied-error-in-kvm-libvirt/)) + +### Networking + +For the containers to be able to communicate with the outside, the host system must have a bridge interface linking the main network interface to the container domain's interface. + +It consists of a bridge interface with the main network interface set as a slave to the bridge : +the container domain's interface will connect to it on the runtime via TAP link. + +> If the server is using a WI-FI interface as the main network interface, a more specific setup must be done before continuing (see the [debian documentation](https://wiki.debian.org/BridgeNetworkConnections#Bridging_with_a_wireless_NIC) for more). + +Follow the steps below according to your system : + +#### Debian-based systems + +First, you need to stop the networking service : + +``` +$ service network stop +``` + +Then you can create the `anwdlbr0` bridge interface, replacing `MASTER_INTERFACE_NAME` by your actual master interface name : + +``` +$ brctl addbr anwdlbr0 +$ brctl addif anwdlbr0 MASTER_INTERFACE_NAME +``` + +Create persistent configuration files for your interfaces : + +``` +$ printf "iface MASTER_INTERFACE_NAME inet manual" >> /etc/network/interfaces +$ printf "iface anwdlbr0 inet dhcp\n\tbridge_ports MASTER_INTERFACE_NAME" >> /etc/network/interfaces +``` + +Enable the `anwdlbr0` interface, and restart the networking service : + +``` +$ ifup anwdlbr0 +$ service network start +``` + + +#### Redhat-based systems + +Disable NetworkManager : + +``` +$ sudo chkconfig NetworkManager off +$ sudo chkconfig network on +$ sudo service NetworkManager stop +$ sudo service network start +``` + +Define the main network interface in networks-script, replacing `MASTER_INTERFACE_NAME` by your actual master interface name : + +``` +$ sudo printf "DEVICE=MASTER_INTERFACE_NAME\nHWADDR=$(ifconfig MASTER_INTERFACE_NAME | grep -o -E ..:..:..:..:..:..)\nONBOOT=yes\nBRIDGE=anwdlbr0\nNM_CONTROLLED=no" /etc/sysconfig/networks-script/MASTER_INTERFACE_NAME +``` + +Create the `anwdlbr0` bridge interface in networks-script : + +``` +$ sudo printf "DEVICE=anwdlbr0\nTYPE=Bridge\nBOOTPROTO=dhcp\nONBOOT=yes\nDELAY=0\nNM_CONTROLLED=no" /etc/sysconfig/networks-script/ifcfg-anwdlbr0 +``` + +Add sysctl rules to enable packet forwarding : + +``` +$ sudo printf "net.bridge.bridge-nf-call-ip6tables = 0\nnet.bridge.bridge-nf-call-iptables = 0\nnet.bridge.bridge-nf-call-arptables = 0" >> /etc/sysctl.conf +$ sudo sysctl -p /etc/sysctl.conf +``` + +Add iptables rules to accept packet forwarding from bridged interfaces : + +``` +$ sudo printf "-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT" /etc/sysconfig/anweddol-iptables-forward-rules +$ sudo lokkit --custom-rules=ipv4:filter:/etc/sysconfig/anweddol-iptables-forward-rules +``` + +Restart the network service : + +``` +$ sudo service network restart +``` + +### Container ISO + +Before using the server, you need to download a specific ISO image for the container domains in order to provide a functional service. + +See the [Container ISO](container_iso.md) section to learn more. + +## Getting started + +At this point, you should be able to start the server. See the [Server usage section](server_usage.md) to do so. + +## Anweddol server uninstallation + +To uninstall the Anweddol server, execute : + +``` +$ sudo anwdlserver-uninstall +``` + +The script will delete any files and everything associated with the Anweddol server. \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/logging.md.txt b/docs/server/_sources/administration_guide/logging.md.txt new file mode 100644 index 0000000..1d66709 --- /dev/null +++ b/docs/server/_sources/administration_guide/logging.md.txt @@ -0,0 +1,52 @@ +# Logging + +---- + +The Anweddol server CLI uses the `logging` python module to ensure viable logging feature. + +Logs are stored in `/var/log/anweddol/runtime.txt` and are stored in this format : + +``` +%(asctime)s %(levelname)s : %(message)s +``` + +Here is a sample of logs generated during the development phase of the Anweddol server : + +``` +2023-06-27 17:59:25,126 WARNING : [INIT] Initializing server (running as 'anweddol') ... +2023-06-27 17:59:25,126 INFO : [INIT] Loading instance RSA key pair ... +2023-06-27 17:59:25,700 INFO : [INIT] Initializing server interface ... +2023-06-27 17:59:25,711 INFO : [INIT] Binding handlers routine ... +2023-06-27 17:59:25,711 INFO : [INIT] All done. Server is ready +2023-06-27 17:59:25,711 INFO : Starting server ... +2023-06-27 17:59:25,712 INFO : Server is started +2023-06-27 17:59:42,724 INFO : (client ID 12ca17b) New client connected +2023-06-27 17:59:42,770 INFO : (client ID 12ca17b) Received STAT request +2023-06-27 17:59:42,778 INFO : (client ID 12ca17b) Connection closed +2023-06-27 17:59:49,958 INFO : (client ID 12ca17b) New client connected +2023-06-27 17:59:50,003 INFO : (client ID 12ca17b) Received CREATE request +2023-06-27 17:59:50,003 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 was created +2023-06-27 18:00:03,421 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 domain is started +2023-06-27 18:00:03,443 INFO : Connected (version 2.0, client OpenSSH_8.4p1) +2023-06-27 18:00:03,567 INFO : Authentication (password) successful! +2023-06-27 18:00:03,567 INFO : (client ID 12ca17b) Endpoint shell opened +2023-06-27 18:00:05,458 INFO : (client ID 12ca17b) Connection closed +2023-06-27 18:00:19,881 INFO : (client ID 12ca17b) New client connected +2023-06-27 18:00:19,927 INFO : (client ID 12ca17b) Received DESTROY request +2023-06-27 18:00:20,143 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 was destroyed +2023-06-27 18:00:20,145 INFO : (client ID 12ca17b) Connection closed +``` + +**NOTE** : Clients are represented by their IDs, here "12ca17b" : It is a way of programmatically identifying the client other than with his IP. It is the first 7 characters of the client's IP SHA256. + +## Log rotation + +You have the possibility to rotate logs by archiving or deleting them. + +Archived logs will be stored in a separate folder withing zipped files with the name format : + +``` +archived_.zip +``` + +where `DATE` is the rotation date. See the `log_rotation` section in the [configuration file](configuration_file.md) for more. \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/server_usage.md.txt b/docs/server/_sources/administration_guide/server_usage.md.txt new file mode 100644 index 0000000..d42db24 --- /dev/null +++ b/docs/server/_sources/administration_guide/server_usage.md.txt @@ -0,0 +1,52 @@ +# Server usage + +---- + +> You need to follows the [Installation](installation.md) section before continuing this tutorial. + +## Start the server + +First, you need the libvirtd daemon running : + +``` +$ sudo systemctl start libvirtd.service +``` + +(**optional**) You may need to check if the server environment is correctly set up : +``` +$ sudo anwdlserver start -c +``` + +If some error occured, check the [Troubleshooting](troubleshooting.md) section to fix them. + +Start the Anweddol server via systemd : + +``` +$ sudo systemctl start anweddol-server.service +``` + +Or via the CLI : + +``` +$ sudo anwdlserver start +``` + +(**optional**) add the option `-d` to enable direct execution (the server will run synchronously in the terminal, as the actual user) + +**NOTE** : It is preferable to use systemd for server lifetime control, mixed usage between CLI and systemd can cause hazardous behaviour. + +## Stop the server + +Stop the server via the systemd daemon : + +``` +$ sudo systemctl stop anweddol-server.service +``` + +Or via the CLI : + +``` +$ sudo anwdlserver stop +``` + +**NOTE** : As mentionned in the previous section, it is preferable to use systemd for server lifetime control : mixed usage between CLI and systemd can cause hazardous behaviour. \ No newline at end of file diff --git a/docs/server/_sources/administration_guide/troubleshooting.md.txt b/docs/server/_sources/administration_guide/troubleshooting.md.txt new file mode 100644 index 0000000..38efa39 --- /dev/null +++ b/docs/server/_sources/administration_guide/troubleshooting.md.txt @@ -0,0 +1,37 @@ +# Troubleshooting + +---- + +Here is a list of non-exhaustive potential problems that can be encountered while using the Anweddol server : + +## [...]`Libvirt : Permission denied` + +*Description* : The libvirt API failed to access or create a resource due to the lack of permissions. + +*Solution* : + +See the Installation [setup section](installation.md) to learn more. + +If the problem persists, try to disable the SELinux enforce feature : + +``` +$ sudo setenforce 0 +``` + +Do not forget to re-enable it when unused : + +``` +$ sudo setenforce 1 +``` + +## [...] `authentication unavailable: no polkit agent available to authenticate action 'org.libvirt.unix.manage'` + +*Description* : The user `anweddol` is not on the `libvirt` group. + +*Solution* : + +Set the user `anweddol` in the `libvirt` group: + +``` +$ sudo usermod -aG libvirt anweddol +``` \ No newline at end of file diff --git a/docs/server/_sources/contribute.md.txt b/docs/server/_sources/contribute.md.txt new file mode 100644 index 0000000..9740175 --- /dev/null +++ b/docs/server/_sources/contribute.md.txt @@ -0,0 +1,31 @@ +# Contribute + +---- + +Thank you for taking the time to contribute to the Anweddol project! + +In this guide you will retrieve the contribution guidelines. + +## Getting started + +In order to be able to bring new ideas and enhancements with ease, you can read the *Technical specifications* of the Anweddol server to know how it works. + +## Issues + +### Create an issue + +If you encounter a problem with the software, be it a bug or a potential error in the code or the docs, search for another issue of its kind first to prevent duplicates. If you find one, you can comment on it to give more importance to the issue. + +### Solve / contribute to an issue + +You can scroll through the issues and find one that interests you. There is no assignee for issues : Everyone is free to contribute to it. + +## Make changes + +### Pull requests + +Any changes will result in a new pull request, which will be reviewed before merging. + +### Documentation + +If your modification proposition involves a modification of the core features or of an already documented one, you need to provide the appropriate documentation as well. \ No newline at end of file diff --git a/docs/server/_sources/developer_section/api_references/core/client.md.txt b/docs/server/_sources/developer_section/api_references/core/client.md.txt new file mode 100644 index 0000000..19e0f70 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/client.md.txt @@ -0,0 +1,392 @@ +# Client + +--- + +> Client management features + +> Package `anwdlserver.core.client` + +## Constants + +Default values : + +| Name | Value | Description | +| ---------------------------- | ------ | -------------------------------------------- | +| `DEFAULT_STORE_REQUEST` | `True` | Store the request in the instance by default | +| `DEFAULT_AUTO_EXCHANGE_KEYS` | `True` | Exchange keys on connection by default | +| `DEFAULT_RECEIVE_FIRST` | `True` | Receive the public key first by default | + +Constants definition : + +| Name | Value | Definition | +| ------------- | ----- | -------------------------------------------------------------------------- | +| `MESSAGE_OK` | `"1"` | Message used during key exchange to transmit a state or an acknowledgement | +| `MESSAGE_NOK` | `"0"` | Message used during key exchange to transmit a state or an acknowledgement | + +## Classes + +### `ClientInstance` + +#### Definition + +``` +class ClientInstance( + socket: socket.socket, + timeout: int = None, + rsa_wrapper: RSAWrapper = None, + aes_wrapper: AESWrapper = None, + auto_exchange_key: bool = DEFAULT_AUTO_EXCHANGE_KEYS, +) +``` + +> Represents a connected client + +_Parameters_ : + +- `socket` : The client socket descriptor. Must be a connected active client socket +- `timeout` : The timeout to apply to the client +- `rsa_wrapper` : The `RSAWrapper` instance that will be used on the client +- `aes_wrapper` : The `AESWrapper` instance that will be used on the client +- `auto_exchange_key` : Automatically exchange keys on initialization + +**NOTE** : The client socket will be automatically closed on `__del__` method. The parameter `socket` must be a valid open socket descriptor representing a client connection. + +#### Methods + +``` +isClosed() -> bool +``` + +> Check if the client is closed + +_Parameters_ : + +- None + +_Return value_ : + +- `True` if the server is closed, `False` otherwise + +--- + +``` +getSocketDescriptor() -> socket.socket +``` + +> Get the client socket descriptor + +_Parameters_ : + +- None + +_Return value_ : + +- The socket descriptor of the client + +--- + +``` +getIP() -> str +``` + +> Get the client IP + +_Parameters_ : + +- None + +_Return value_ : + +- The client IP as an IPv4 string + +--- + +``` +getID() -> str +``` + +> Get the client ID + +_Parameters_ : + +- None + +_Return value_ : + +- The client ID. It is the first 7 characters of the client's IP SHA256 (see the administration guide [Logging section](https://anweddol-server.readthedocs.io/en/latest/administration_guide/logging.html) to learn more). + +--- + +``` +getTimestamp() -> int +``` + +> Get the client creation timestamp + +_Parameters_ : + +- None + +_Return value_: + +- The creation timestamp + +--- + +``` +getStoredRequest() -> None | dict +``` + +> Get the stored request + +_Parameters_ : + +- None + +_Return value_ : + +- A dictionary following the normalized [Request format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#request-format) or `None` if there is none + +--- + +``` +getRSAWrapper() -> RSAWrapper +``` + +> Get the client `RSAWrapper` instance + +_Parameters_ : + +- None + +_Return value_: + +- The `RSAWrapper` instance of the client + +--- + +``` +getAESWrapper() -> AESWrapper +``` + +> Get the client `AESWrapper` instance + +_Parameters_ : + +- None + +_Return value_: + +- The `AESWrapper` instance of the client + +--- + +``` +setRSAWrapper(rsa_wrapper: RSAWrapper) -> None +``` + +> Set the client `RSAWrapper` instance + +_Parameters_ : + +- `rsa_wrapper` : The `RSAWrapper` instance to set + +_Return value_ : + +- None + +--- + +``` +setAESWrapper(aes_wrapper: AESWrapper) -> None +``` + +> Set the client `AESWrapper` instance + +_Parameters_ : + +- `aes_wrapper` : The `AESWrapper` instance to set + +_Return value_ : + +- None + +--- + +``` +sendPublicRSAKey() -> None +``` + +> Send the local public RSA key + +_Parameters_ : + +- None + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `RuntimeError` + +_Possible raise classes_ : + +- `RuntimeError` + +--- + +``` +recvPublicRSAKey() -> None +``` + +> Receive the client public RSA key + +_Parameters_ : + +- None + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +sendAESKey() -> None +``` + +> Send the local AES key + +_Parameters_ : + +- None + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +recvAESKey() -> None +``` + +> Receive the local AES key + +_Parameters_ : + +- None + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +exchangeKeys(receive_first: bool = DEFAULT_RECEIVE_FIRST) -> None +``` + +> Exchange the local RSA and AES keys with the client + +_Parameters_ : + +- `receive_first` : `True` to receive the public key first, `False` otherwise + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +sendResponse( + success: bool, + message: str, + data: dict = {}, + reason: str = None +) -> None +``` + +> Send a response to the client + +_Parameters_ : + +- `success` : `True` to announce a success, `False` otherwise +- `message` : The message to send +- `data` : The data to send. The content must be an empty dictionary or a normalized [Response format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#response-format) dictionary. +- `reason` : The reason to specify if `success` is set to `False`. The value will be appended to the message like : `Refused request (reason : )` + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +recvRequest(store_request: bool = DEFAULT_STORE_REQUEST) -> tuple +``` + +> Receive a request from the client + +_Parameters_ : + +- `store_request` : Store the received request on instance or not + +_Return value_ : + +- The received request as a normalized [Request format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#request-format) dictionary + +_Possible raise classes_ : + +- `ValueError` +- `RuntimeError` + +--- + +``` +closeConnection() -> None +``` + +> Close the client connection + +_Parameters_ : + +- None + +_Return value_ : + +- None + +**NOTE** : This method is automatically called within the `__del__` method, but it is programatically better to call it naturally + +_Possible raise classes_ : + +- `RuntimeError` diff --git a/docs/server/_sources/developer_section/api_references/core/cryptography.md.txt b/docs/server/_sources/developer_section/api_references/core/cryptography.md.txt new file mode 100644 index 0000000..1a38f58 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/cryptography.md.txt @@ -0,0 +1,345 @@ +# Cryptography + +--- + +> RSA / AES encryption features. + +> Package `anwdlserver.core.crypto` + +## Constants + +Default values : + +| Name | Value | Description | +| ----------------------------- | ------- | --------------------------------------------------------- | +| `DEFAULT_RSA_KEY_SIZE` | `4096` | The default RSA key size | +| `DEFAULT_AES_KEY_SIZE` | `256` | The default AES key size | +| `DEFAULT_PEM_FORMAT` | `True` | Specify key in a PEM format by default | +| `DEFAULT_GENERATE_KEY_PAIR` | `True` | Generate key pair on initialization by default | +| `DEFAULT_DERIVATE_PUBLIC_KEY` | `False` | Derivate the public key out of the private key by default | + +## Classes + +### `RSAWrapper` + +#### Definition + +``` +class RSAWrapper( + key_size: int = DEFAULT_RSA_KEY_SIZE, + generate_key_pair: bool = DEFAULT_GENERATE_KEY_PAIR +) +``` + +> Provides [RSA encryption](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) functionality + +_Parameters_ : + +- `key_size` : The RSA key size, exprimed in bits. Must be a multiple of 2 +- `generate_key_pair` : Generate RSA key pair on initialization or not + +#### Methods + + generateKeyPair(self) -> None + +> Generate the RSA key pair + +_Parameters_ : + +- None + +_Return value_ : + +- None + +--- + + getKeySize() -> int + +> Get the local key size + +_Parameters_ : + +- None + +_Return value_ : + +- The key size exprimed in bytes + +--- + + getPublicKey(pem_format: bool = True) -> str | bytes + +> Get the local public key + +_Parameters_ : + +- `pem_format` : `True` to return the local public key in a [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/), `False` to return it as a byte sequence + +_Return value_ : + +- The local public key + +--- + + getPrivateKey(pem_format: bool = True) -> str | bytes + +> Get the local private key + +_Parameters_ : + +- `pem_format` : `True` to return the local key in a [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/), `False` to return it as a byte sequence + +_Return value_ : + +- The local private key + +--- + + getRemotePublicKey(pem_format: bool = True) -> None | str | bytes + +> Get the client public key + +_Parameters_ : + +- `pem_format` : `True` to return the client public key in a [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/), `False` to return it as a byte sequence + +_Return value_ : + +- The client public key, or `None` if there is none + +--- + + setPublicKey(public_key: str | bytes, pem_format: bool = True) -> None + +> Set the local public key + +_Parameters_ : + +- `public_key` : The public key, in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if the parameter `pem_format` is set to `True`, in a byte sequence otherwise +- `pem_format` : Specify the format of the key in the parameter `public_key`. The key must be in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if set to `True`, in a byte sequence otherwise + +_Return value_ : + +- None + +**NOTE** : The intern private key will be deleted in the process, you must use `setPrivateKey` to set the affiliated private key. + +--- + + setPrivateKey( + private_key: str | bytes, + pem_format: bool = DEFAULT_PEM_FORMAT, + derivate_public_key: bool = DEFAULT_DERIVATE_PUBLIC_KEY + ) -> None + +> Set the local private key + +_Parameters_ : + +- `private_key` : The private key, in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if the parameter `pem_format` is set to `True`, in a byte sequence otherwise +- `pem_format` : Specify the format of the key in the parameter `private_key`. The key must be in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if set to `True`, in a byte sequence otherwise +- `derivate_public_key` : Derivate the public key out of the private key or not + +_Return value_ : + +- None + +--- + + setRemotePublicKey( + remote_public_key: str | bytes, + pem_format: bool = DEFAULT_PEM_FORMAT, + ) -> None + +> Set the client public key + +_Parameters_ : + +- `remote_public_key` : The remote public key, in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if the parameter `pem_format` is set to `True`, in a byte sequence otherwise +- `pem_format` : Specify the format of the key in the parameter `private_key`. The key must be in [PEM format](https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/) if set to `True`, in a byte sequence otherwise + +_Return value_ : + +- None + +--- + + encryptData( + data: str | bytes, + encode: bool = True, + use_local_public_key: bool = False + ) -> bytes + +> Encrypt data + +_Parameters_ : + +- `data` : The data to encrypt. It can be a string or a byte sequence +- `encode` : Specify if the content in parameter `data` must be encoded before being processed (mus be set to `True` only if the parameter `data` is a string) +- `use_local_public_key` : Specify if the local public key must be used for encryption instead of the remote public key + +_Return value_ : + +- The encrypted `data` content as a byte sequence + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : If the parameter `use_local_public_key` is set to `False`, the remote public key must be set (`RuntimeError` will be raised otherwise) + +--- + + decryptData(cipher: bytes, decode: bool = True) -> str | bytes + +> Decrypt data + +_Parameters_ : + +- `cipher` : The encrypted cipher text as a byte sequence +- `decode` : Specify if the decrypted data should be decoded before being returned + +_Return value_ + +- The decrypted `data` content as a string or a byte sequence according to the value of `decode` + +--- + +``` +signData(data: str | bytes, encode: bool = True) -> bytes +``` + +> Sign a block of data + +_Parameters_ : + +- `data` : The data to sign +- `encode` : Specify if the content in parameter `data` must be encoded before being processed (set to `True` only if the parameter `data` is a string) + +_Peturn value_ : + +- The signed data, as a byte sequence + +--- + +``` +verifyDataSignature( + signature: bytes, + data: str | bytes, + encode: bool = True +) -> bool +``` + +> Verify the authenticity of a signed block of data + +_Parameters_ : + +- `signature` : The signed data, as a byte sequence +- `data` : The data to verify +- `encode` : Specify if the content in parameter `data` must be encoded before being processed (set to `True` only if the parameter `data` is a string) + +_Peturn value_ : + +- A boolean value : `True` if the data and its signature are authentic, `False` otherwise + +### `AESWrapper` + +#### Definition + + class AESWrapper(key_size: int = DEFAULT_AES_KEY_SIZE) + +> Provides [AES encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) functionality + +_Parameters_ : + +- `key_size` : The AES key size, exprimed in bytes. Must be 16, 24 or 32 bytes long + +#### Methods + + getKeySize() -> int + +> Get the key size + +_Parameters_ : + +- None + +_Return value_ : + +- The AES key size exprimed in bytes, as an integer + +--- + + getKey() -> bytes + +> Get the AES key + +_Parameters_ : + +- None + +_Return value_ : + +- The AES key as a byte sequence + +--- + + getIv() -> bytes + +> Get the [Initialisation Vector](https://en.wikipedia.org/wiki/Initialization_vector) (Iv) + +_Parameters_ : + +- None + +_Return value_ : + +- The IV as a 16 byte sequence + +--- + + setKey(key: bytes, iv: bytes = None) -> None + +> Set the AES key + +_Parameters_ : + +- `key` : The AES key, as a byte sequence. Must be 16, 24 or 32 bytes long +- `iv` : The [Initialisation Vector](https://en.wikipedia.org/wiki/Initialization_vector). Must be 16 bytes long + +_Return value_ : + +- None + +**NOTE** : If the parameter `iv` is not set, it will be automatically generated + +--- + + encryptData(data: str | bytes, encode: bool = True) -> bytes + +> Encrypt data + +_Parameters_ : + +- `data` : The data to encrypt. It can be a string or a byte sequence +- `encode` : Specify if the content in parameter `data` must be encoded before being processed (set to `True` only if the parameter `data` is a string) + +_Return value_ : + +- The `data` as a byte sequence + +--- + + decryptData(self, cipher: bytes, decode: bool = True) -> str | bytes + +> Decrypt data + +_Parameters_: + +- `cipher`: The encrypted cipher text as a byte sequence +- `decode`: Specify if the decrypted data should be decoded before being returned + +_Return value_ + +- The decrypted `data` content as a string or a byte sequence according to the value of `decode` diff --git a/docs/server/_sources/developer_section/api_references/core/database.md.txt b/docs/server/_sources/developer_section/api_references/core/database.md.txt new file mode 100644 index 0000000..0bd3cc1 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/database.md.txt @@ -0,0 +1,260 @@ +# Database + +--- + +> Database features with SQLite memory database + +> Package `anwdlserver.core.database` + +## Constants + +None + +## Classes + +### `DatabaseInterface` + +#### Definition + +``` +class DatabaseInterface() +``` + +> Provides an [SQLAlchemy](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/database.html) memory database instance + +_Parameters_ : + +- None + +**NOTE** : The database and its engine will be automatically closed on `__del__` method. Also, queries implying modifications on the database are automatically committed, and rollbacks are called if an error occured. + +#### Methods + +``` +getEngine() -> sqlalchemy.engine.Engine +``` + +> Get the SQLAlchemy [`sqlalchemy.engine.Engine`](https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Engine) object instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `sqlalchemy.engine.Engine` object of the instance + +--- + +``` +getEngineConnection() -> sqlalchemy.engine.Connection +``` + +> Get the SQLAlchemy [`sqlalchemy.engine.Connection`](https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Connection) object instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `sqlalchemy.engine.Connection` object of the instance + +--- + +``` +getRuntimeTableObject() -> sqlalchemy.schema.Table +``` + +> Get the SQLAlchemy [`sqlalchemy.schema.Table`](https://docs.sqlalchemy.org/en/20/core/metadata.html#sqlalchemy.schema.Table) object instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `sqlalchemy.schema.Table` object of the instance + +--- + +``` +getEntryID(container_uuid: str, client_token: str) -> None | int +``` + +> Get the credentials pair entry ID[^1] + +_Parameters_ : + +- `container_uuid` : The clear [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) +- `client_token` : The clear [client token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) + +_Return value_ : + +- The credentials entry ID[^1] if exists, `None` otherwise + +**NOTE** : This method must be used for client credentials verification. + +[^1]: Similar to the ROWID in sqlite, an integer that identifies the row + +--- + +``` +getContainerUUIDEntryID(container_uuid: str) -> None | int +``` + +> Get the entry ID[^1] of a specific container UUID + +_Parameters_ : + +- `container_uuid` : The container UUID to search for. It can be a [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) or a [client token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) + +_Return value_ : + +- The container UUID entry ID[^1] if exists, `None` otherwise + +**NOTE** : Only the `ContainerUUID` column value is concerned by this method + +--- + +``` +getEntry(entry_id: int) -> tuple +``` + +> Get an entry content + +_Parameters_ : + +- `entry_id` : The entry ID[^1] to get the credentials from + +_Return value_ : + +- A tuple representing the entry content : + +``` +( + entry_id, + creation_timestamp, + container_uuid, + client_token +) +``` + +- `entry_id` : The entry ID[^1] +- `creation_timestamp` : The entry creation timestamp +- `container_uuid` : The hashed [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) +- `client_token` : The hashed [client token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) + +**NOTE** : The `container_uuid` and the `client_token` values are hashed with SHA256 as described in the [Technical specifications](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/database.html#security). + +--- + +``` +addEntry(container_uuid: str) -> tuple +``` + +> Add an entry + +_Parameters_ : + +- `container_uuid` : The [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) to add + +_Return value_ : + +- A tuple representing the infomations of the created entry : + +``` +( + entry_id, + creation_timestamp, + client_token +) +``` + +- `entry_id` : The new entry ID[^1] +- `creation_timestamp` : The entry creation timestamp +- `client_token` : The [client token]https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials), in plain text + +_Possible raise classes_ : + +- `LookupError` + +**NOTE** : If the `container_uuid` is already specified on the database, `LookupError` is raised. Since the [client tokens](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) are hashed in the database (see the technical specifications [Database section](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/database.html) to learn more), there's no way to see them again in plain text : Store this clear created token somewhere safe in order to use it for further operations. + +--- + +``` +listEntries() -> list +``` + +> List entries + +_Parameters_ : + +- None + +_Return value_ : + +- A list of tuples representing the entries partial informations : + +``` +( + entry_id, + creation_timestamp +) +``` + +- `entry_id` : The created entry ID[^1] +- `creation_timestamp` : The entry creation timestamp + +--- + +``` +updateEntry(entry_id: int, container_uuid: str, client_token: str) +``` + +> Update an entry + +_Parameters_ : + +- `entry_id` : The entry ID[^1] to update +- `container_uuid` : The [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) to set +- `client_token` : THe [client token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) to set + +_Return value_ : + +- None + +--- + +``` +deleteEntry(entry_id: str) -> None +``` + +> Delete an entry + +_Parameters_ : + +- `entry_id` : The entry ID[^1] to delete + +_Return value_ : + +- None + +--- + +``` +closeDatabase() -> None +``` + +> Close the database instance + +_Parameters_ : + +- None + +_Return value_ : + +- None + +**NOTE** : This method is automatically called within the `__del__` method, but it is programatically better to call it naturally diff --git a/docs/server/_sources/developer_section/api_references/core/sanitization.md.txt b/docs/server/_sources/developer_section/api_references/core/sanitization.md.txt new file mode 100644 index 0000000..da048e3 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/sanitization.md.txt @@ -0,0 +1,97 @@ +# Sanitization + +--- + +> Normalized request / response values and formats + +> Package `anwdlserver.core.sanitize` + +## Constants + +None + +## Functions + +``` +verifyRequestContent(request_dict: dict) -> tuple +``` + +> Check if a request dictionary is a valid normalized [Request format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#request-format) + +_Parameters_ : + +- `request_dict` : The request dictionary + +_Return value_ : + +- A tuple representing the verification results : + +``` +( + True, + sanitized_request_dictionary +) +``` + +if the `request_dict` is a valid normalized [Request format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#request-format), + +``` +( + False, + errors_dictionary +) +``` + +otherwise. + +- `sanitized_request_dictionary` : The sanitized request as a normalized [Request format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#request-format) dictionary. +- `errors_dictionary` : A dictionary depicting the errors detected in `request_dict` according to the [Cerberus](https://docs.python-cerberus.org/en/stable/errors.html) error format. + +**NOTE** : The function `verifyRequestContent` does not use strict verification. It only checks if the required keys and values exist and are correct, but it is open to unknown keys or structures for the developer to be able to implement its own mechanisms (see the technical specifications [Sanitization section](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#sanitization) to learn more). + +--- + +``` +makeResponse( + success: bool, + message: str, + data: dict = {}, + reason: str = None +) -> tuple: +``` + +> Make a normalized response dictionary + +_Parameters_ : + +- `success` : `True` to announce a success, `False` otherwise +- `message` : The message to send +- `data` : The data to send. The content must be an empty dict or a normalized [Response format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#response-format). +- `reason` : The reason to specify if `success` is set to `False`. The value will be appended to the message like : `Refused request (reason : )` + +_Return value_ : + +- A tuple representing a valid [Response format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#response-format) dictionary : + +``` +( + True, + response_dictionary +) +``` + +if the operation succeeded, + +``` +( + False, + errors_dictionary +) +``` + +otherwise. + +- `response_dictionary` : The response dictionary as a normalized [Response format](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/communication.html#response-format). +- `errors_dictionary` : A dictionary depicting the errors detected in parameters according to the [Cerberus](https://docs.python-cerberus.org/en/stable/errors.html) error format. + +**NOTE** : The `sendResponse` method from `ClientInstance` wraps this function in its process. Like `verifyRequestContent`, the method only checks if the required keys and values exist and are correct, but it is open to unknown keys or structures for the developer to be able to implement its own mechanisms. diff --git a/docs/server/_sources/developer_section/api_references/core/server.md.txt b/docs/server/_sources/developer_section/api_references/core/server.md.txt new file mode 100644 index 0000000..53e658e --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/server.md.txt @@ -0,0 +1,448 @@ +# Server + +--- + +> Server management features + +> Package `anwdlserver.core.server` + +## Constants + +Default values : + +| Name | Value | Description | +| ----------------------------- | ------- | ------------------------------------------- | +| `DEFAULT_SERVER_BIND_ADDRESS` | `""` | The default server bind address | +| `DEFAULT_SERVER_LISTEN_PORT` | `6150` | The default server listen port | +| `DEFAULT_CLIENT_TIMEOUT` | `10` | The default timeout applied on a new client | +| `DEFAULT_ASYCHRONOUS` | `False` | Start the server synchronously by default | + +Constants definition : + +| Name | Value | Description | +| ------------------------------ | ---------------------- | ----------------------------------------------------------------------- | +| `REQUEST_VERB_CREATE` | `"CREATE"` | The verb indicating the intent to create a new container | +| `REQUEST_VERB_DESTROY` | `"DESTROY"` | The verb indicating the intent to destroy a container | +| `REQUEST_VERB_STAT` | `"STAT"` | The verb indicating the intent to get the server runtime statistics | +| `RESPONSE_MSG_OK` | `"OK"` | Message specifying that the request was correctly handled by the server | +| `RESPONSE_MSG_BAD_AUTH` | `"Bad authentication"` | Message specifying that invalid credentials were given to the server | +| `RESPONSE_MSG_BAD_REQ` | `"Bad request"` | Message specifying that the received request is malformed | +| `RESPONSE_MSG_REFUSED_REQ` | `"Refused request"` | Message specifying that the received request was refused | +| `RESPONSE_MSG_UNAVAILABLE` | `"Unavailable"` | Message specifying that the server is currently unavailable | +| `RESPONSE_MSG_INTERNAL_ERROR` | `"Internal error"` | Message specifying that the server experienced an internal error | +| `EVENT_CLIENT` | `1` | Identifies the `on_client` callback method | +| `EVENT_CLIENT_CLOSED` | `2` | Identifies the `on_client_closed` callback method | +| `EVENT_CONNECTION` | `3` | Identifies the `on_connection` callback method | +| `EVENT_CREATED_CONTAINER` | `4` | Identifies the `on_created_container` callback method | +| `EVENT_CREATED_ENDPOINT_SHELL` | `5` | Identifies the `on_created_endpoint_shell` callback method | +| `EVENT_DESTROYED_CONTAINER` | `6` | Identifies the `on_destroyed_container` callback method | +| `EVENT_MALFORMED_REQUEST` | `7` | Identifies the `on_malformed_request` callback method | +| `EVENT_REQUEST` | `8` | Identifies the `on_request` callback method | +| `EVENT_RUNTIME_ERROR` | `9` | Identifies the `on_runtime_error` callback method | +| `EVENT_STARTED` | `10` | Identifies the `on_started` callback method | +| `EVENT_STOPPED` | `11` | Identifies the `on_stopped` callback method | +| `EVENT_UNKNOWN_VERB` | `12` | Identifies the `on_unknown_verb` callback method | + +## Classes + +### `ServerInterface` + +#### Definition + +``` +class ServerInterface( + container_iso_path: str = None, + bind_address: str = DEFAULT_SERVER_BIND_ADDRESS, + listen_port: int = DEFAULT_SERVER_LISTEN_PORT, + client_timeout: int = DEFAULT_CLIENT_TIMEOUT, + runtime_virtualization_interface: VirtualizationInterface = None, + runtime_database_interface: DatabaseInterface = None, + runtime_rsa_wrapper: RSAWrapper = None +) +``` + +> Main server utility class + +_Parameters_ : + +- `container_iso_path` : The container ISO path that will be used for containers +- `bind_address` : The bind address that the server will be using +- `listen_port` : The listen port that the server will be using +- `client_timeout` : The timeout that will be applied to clients +- `runtime_virtualization_interface` : The `VirtualizationInterface` instance that will be used by the server +- `runtime_database_interface` : The `DatabaseInterface` instance that will be used by the server +- `runtime_rsa_wrapper` : The `RSAWrapper` instance that will be used by the server + +**NOTE** : The container ISO path must point to a valid [ISO image](https://anweddol-server.readthedocs.io/en/latest/administration_guide/container_iso.html). The server is automatically stopped when the `__del__` method is called. + +#### Methods + +``` +getRuntimeDatabaseInterface() -> DatabaseInterface +``` + +> Returns the runtime `DatabaseInterface` instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `DatabaseInterface` instance used by the server + +--- + +``` +getRuntimeVirtualizationInterface() -> VirtualizationInterface +``` + +> Returns the runtime `VirtualizationInterface` instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `VirtualizationInterface` instance used by the server + +--- + +``` +getRuntimeRSAWrapper() -> RSAWrapper +``` + +> Returns the runtime `RSAWrapper` instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `RSAWrapper` instance used by the server + +--- + +``` +getRuntimeStatistics() -> tuple +``` + +> Returns the server's actual runtime statistics + +_Parameters_ : + +- None + +_Return value_: + +- A tuple containing the server runtime statistics : + +``` +( + is_running, + recorded_runtime_errors_amount, + uptime, + available_containers_amount +) +``` + +- `is_running` : Boolean value set to `True` if the server is currently running, `False` otherwise +- `recorded_runtime_errors_amount` : The amount of errors recorded during the runtime +- `uptime` : The server uptime, exprimed in seconds +- `available_containers_amount` : The available containers amount + +--- + +``` +setRequestHandler(verb: str, routine: callable) -> None +``` + +> Set a request handler + +_Parameters_ : + +- `verb` : The request verb to handle. It can be a custom one or a normalized one : + - `REQUEST_VERB_CREATE` + - `REQUEST_VERB_STAT` + - `REQUEST_VERB_DESTROY` +- `routine` : A callable object that will be called when a received request verb is equal to `verb` value + +_Return value_ : + +- None + +--- + +``` +setEventHandler(event: int, routine: callable) -> None +``` + +> Set an event handler + +_Parameters_ : + +- `event` : The event to handle. It can be : + + - `EVENT_MALFORMED_REQUEST` + - `EVENT_UNKNOWN_VERB` + - `EVENT_RUNTIME_ERROR` + - `EVENT_REQUEST` + - `EVENT_CONNECTION` + - `EVENT_CREATED_CONTAINER` + - `EVENT_CREATED_ENDPOINT_SHELL` + - `EVENT_CLIENT` + - `EVENT_STARTED` + - `EVENT_STOPPED` + - `EVENT_CLIENT_CLOSED` + - `EVENT_DESTROYED_CONTAINER` + +- `routine` : A callable object that will be called when the `event` event is triggered. + +**NOTE** : This is the alternative of callback event decorators. Refer to the [Callback events](https://anweddol-server.readthedocs.io/en/latest/developer_section/api_references/core/server.html#callback-events) section below to know their roles and effects. + +--- + +``` +startServer(asynchronous: bool = DEFAULT_ASYCHRONOUS) -> None +``` + +> Start the server + +_Parameters_ : + +- `asynchronous` : Start the server asynchronously. Default is `False`, executing it synchronously + +_Return value_ : + +- None + +**NOTE** : The execution is blocked when set to synchronous mode (by default) + +--- + +``` +stopServer() -> None +``` + +> Stop the server + +_Parameters_ : + +- None + +_Return value_: + +- None + +**NOTE** : This method is automatically called within the `__del__` method, but it is programatically better to call it naturally + +--- + +``` +restartServer(asynchronous: bool = DEFAULT_ASYCHRONOUS) -> None +``` + +> Restart the server + +_Parameters_ : + +- `asynchronous` : Start the server asynchronously. Default is `False`, executing it synchronously + +_Return value_ : + +- None + +#### Callback events + +Callback events are decorators used to bind a routine or a callable object to an intern event. + +``` +# The decorator depicting the event. Here EVENT_CREATED_CONTAINER +@ServerInterface.on_created_container +def routine(**kwargs): + # The routine that will be executed when the event will be triggered + ... +``` + +Arguments provided to routines are different depending of the callback used. +That's why it is recommended to use `**kwargs` and to refer to the documentation to know the names of the passed parameters. + +**NOTE** : When a client instance or raw socket is closed during a callback routine, the main server process will detect it, automatically terminating the session as a result. It is the same for container shell instances. + +To have a proper example on how to use these decorators, see the _Basic server_ on the examples section. + +``` +@ServerInterface.on_client +``` + +> Triggered when a new client is connected and the keys are exchanged + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the client + +_Affiliated constant_ : `EVENT_CLIENT` + +--- + +``` +@ServerInterface.on_client_closed +``` + +> Triggered when a connected client was closed + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance Callback events representing the client + +_Affiliated constant_ : `EVENT_CLIENT_CLOSED` + +--- + +``` +@ServerInterface.on_connection +``` + +> Triggered when a new client is connected and the keys have not been exchanged yet + +_Provided parameters_ : + +- `client_socket` : The raw client socket instance + +_Affiliated constant_ : `EVENT_CONNECTION` + +--- + +``` +@ServerInterface.on_created_container +``` + +> Triggered when a new container instance is created + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the affiliated client +- `container_instance` : The `ContainerInstance` instance of the created container + +_Affiliated constant_ : `EVENT_CREATED_CONTAINER` + +--- + +``` +@ServerInterface.on_created_endpoint_shell +``` + +> Triggered when an endpoint shell was opened on a container + +_Provided parameters_ : + +- `endpoint_shell_instance` : The `EndpointShellInstance` on the container +- `container_instance` : The `ContainerInstance` instance representing the affiliated container +- `client_instance` : The `ClientInstance` instance representing the affiliated client + +_Affiliated constant_ : `EVENT_CREATED_ENDPOINT_SHELL` + +--- + +``` +@ServerInterface.on_destroyed_container +``` + +> Triggered when a container was destroyed + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the affiliated client +- `container_instance` : The `ContainerInstance` instance representing the affiliated container + +_Affiliated constant_ : `EVENT_DESTROYED_CONTAINER` + +--- + +``` +@ServerInterface.on_malformed_request +``` + +> Triggered when the server has received a malformed request + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the affiliated client +- `cerberus_error_dict` : The [cerberus error dict](https://docs.python-cerberus.org/en/stable/errors.html#errors-error-handling) depicting the error detected on the request + +_Affiliated constant_ : `EVENT_MALFORMED_REQUEST` + +--- + +``` +@ServerInterface.on_request +``` + +> Triggered when the server receives a request + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the affiliated client + +_Affiliated constant_ : `EVENT_REQUEST` + +--- + +``` +@ServerInterface.on_runtime_error +``` + +> Triggered when an exception was raised during the server process + +_Provided parameters_ : + +- `name` : The name of the thread where the error occured +- `ex_class` : The exception class +- `traceback` : The full traceback of the exception as a string +- (optional) `client_instance` : The `ClientInstance` instance representing the affiliated client + +_Affiliated constant_ : `EVENT_RUNTIME_ERROR` + +--- + +``` +@ServerInterface.on_started +``` + +> Triggered when the server is started and ready to operate + +_Provided parameters_ : + +- None + +_Affiliated constant_ : `EVENT_STARTED` + +--- + +``` +@ServerInterface.on_stopped +``` + +> Triggered when the server was stopped + +_Provided parameters_ : + +- None + +_Affiliated constant_ : `EVENT_STOPPED` + +--- + +``` +@ServerInterface.on_unknown_verb +``` + +> Triggered when the server has received an unknown or unsupported verb + +_Provided parameters_ : + +- `client_instance` : The `ClientInstance` instance representing the affiliated client + +_Affiliated constant_ : `EVENT_UNKNOWN_VERB` diff --git a/docs/server/_sources/developer_section/api_references/core/utilities.md.txt b/docs/server/_sources/developer_section/api_references/core/utilities.md.txt new file mode 100644 index 0000000..62e644c --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/utilities.md.txt @@ -0,0 +1,75 @@ +# Utilities + +--- + +> Miscellaneous features + +> Package `anwdlserver.core.util` + +## Constants + +None + +## Functions + +``` +isPortBindable(port: int) -> bool +``` + +> Check if a port is bindable + +_Parameters_ : + +- `port` : The port to check. It must be an integer between 1 and 65535 + +_Return value_ : + +- `True` if the port is bindable, `False` otherwise + +--- + +``` +isSocketClosed(socket_descriptor: socket.socket) -> bool +``` + +> Check if a socket descriptor is closed + +_Parameters_ : + +- `socket_descriptor` : The socket descriptor to check + +_Return value_ : + +- `True` if the socket descriptor is closed, `False` otherwise + +--- + +``` +isValidIP(ip: str) -> bool +``` + +> Check if the IP is a valid IPv4 format + +_Parameters_ : + +- `ip` : The IP to check as a string + +_Return value_ : + +- `True` if the IP is valid , `False` otherwise + +--- + +``` +isInterfaceExists(interface_name: str) -> bool +``` + +> Check if the network interface exists on the system + +_Parameters_ : + +- `interface_name` : The interface name to check as a string + +_Return value_ : + +- `True` if the interface exists , `False` otherwise diff --git a/docs/server/_sources/developer_section/api_references/core/virtualization.md.txt b/docs/server/_sources/developer_section/api_references/core/virtualization.md.txt new file mode 100644 index 0000000..e63cd48 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/core/virtualization.md.txt @@ -0,0 +1,825 @@ +# Virtualization + +--- + +> Virtualization management and utilities, using libvirt API + +> Package `anwdlserver.core.virtualization` + +## Constants + +Default parameters : + +| Name | Value | Description | +| ---------------------------------------- | --------------------- | ---------------------------------------------------------------------------------------- | +| `DEFAULT_LIBVIRT_DRIVER_URI` | `"qemu:///system"` | The default server bind address | +| `DEFAULT_CONTAINER_ENDPOINT_USERNAME` | `"endpoint"` | The default server listen port | +| `DEFAULT_CONTAINER_ENDPOINT_PASSWORD` | `"endpoint"` | The default timeout applied on a new client | +| `DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT` | `22` | Start the server asynchronously by default | +| `DEFAULT_BRIDGE_INTERFACE_NAME` | `"anwdlbr0"` | The default bridge interface name that container domains will use | +| `DEFAULT_NAT_INTERFACE_NAME` | `"virbr0"` | The default NAT interface name that container domains will use | +| `DEFAULT_CONTAINER_MAX_TRYOUT` | `20` | The maximum attemps to check the network availability of a container domain before error | +| `DEFAULT_MAX_ALLOWED_CONTAINERS` | `6` | The maximum allowed amount of active containers on an instance | +| `DEFAULT_CONTAINER_MEMORY` | `2048` | The memory, in Mb, allocated to container domains | +| `DEFAULT_CONTAINER_VCPUS` | `2` | The amount of VCPUs allocated to container domains | +| `DEFAULT_CONTAINER_PORT_RANGE` | `range(10000, 15000)` | The port range that will be assigned to container domains SSH servers | +| `DEFAULT_CONTAINER_WAIT_AVAILABLE` | `True` | Wait for the network to be available on a container domain before continuing by default | +| `DEFAULT_CONTAINER_DESTROY_DOMAIN` | `True` | Destroy the domain rather than shutting it down by default | +| `DEFAULT_STORE_CONTAINER` | `True` | Store the container instance on the virtualization interface or not by default | + +## Classes + +### `EndpointShellInstance` + +#### Definition + +``` +class EndpointShellInstance( + ssh_client: paramiko.client.SSHClient +) +``` + +> Represents a opened SSH shell to a container endpoint + +_Parameters_ : + +- `ssh_client`: The [`paramiko.client.SSHClient`](https://docs.paramiko.org/en/stable/api/client.html) instance representing the SSH connection. + +**NOTE** : The endpoint SSH shell will be automatically closed on `__del__` method. + +#### Methods + +``` +isClosed() -> bool +``` + +> Check if the connection is closed + +_Parameters_ : + +- None + +_Return value_ : + +- A boolean value. `True` if the connection is closed, `False` otherwise + +--- + +``` +getSSHClient() -> paramiko.client.SSHClient +``` + +> Get the [`paramiko.client.SSHClient`](https://docs.paramiko.org/en/stable/api/client.html) connection object + +_Parameters_ : + +- None + +_Return value_ : + +- The [`paramiko.client.SSHClient`](https://docs.paramiko.org/en/stable/api/client.html) object of the instance + +--- + +``` +getStoredContainerSSHCredentials() -> tuple +``` + +> Get the stored container SSH credentials + +_Parameters_ : + +- None + +_Return value_ : + +- A tuple representing the stored container SSH credentials : + +``` +( + username, + password, + listen_port +) +``` + +- `username` : The SSH username +- `password` : The SSH password +- `listen_port` : The SSH server listen port + +**NOTE** : Container SSH credentials are stored on the instance within the `setContainerSSHCredentials` method. For these credentials to be sent to the client in the server process, you need to call this method if you want to set specific credentials. + +--- + +``` +executeCommand(command: str) -> tuple +``` + +> Execute a command on the remote container + +_Parameters_ : + +- `command` : The command to execute + +_Return value_ : + +- A tuple representing the stdout and the stderr of the command output : + +``` +( + stdout, + stderr +) +``` + +- `stdout` : The standard output stream of the result +- `stderr` : The standard error stream of the result + +--- + +``` +generateContainerSSHCredentials( + port_range: range = DEFAULT_CONTAINER_PORT_RANGE, +) -> tuple +``` + +> Generate new container SSH credentials + +_Parameters_ : + +- `port_range` : The port range which a random new listen port is chosen + +_Return value_ : + +- A tuple representing the generated container SSH credentials : + +``` +( + username, + password, + listen_port +) +``` + +- `username` : The SSH username +- `password` : The SSH password +- `listen_port` : The SSH server listen port + +--- + +``` +setContainerSSHCredentials( + username: str, + password: str, + listen_port: int, +) -> None +``` + +> Set the container SSH credentials on the remote container + +_Parameters_ : + +- `username` : The SSH username to set +- `password` : The SSH password to set +- `listen_port` : The SSH server listen port to set + +_Return value_ : + +- None + +**NOTE** : This method uses the `anweddol_container_setup.sh` script on the container to set the SSH credentials on ot, thus calling the `closeShell` method after completion (see the technical specifications [Virtualization section](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#administration)) to learn more). + +--- + +``` +closeShell() -> None +``` + +> Close the SSH connection + +_Parameters_ : + +- None + +_Return value_ : + +- None + +**NOTE** : This method is automatically called within the `__del__` method and the `setContainerSSHCredentials` method when called, but it is programatically better to call it naturally + +### `ContainerInstance` + +#### Definition + +``` +class ContainerInstance( + uuid: str, + iso_path: str, + memory: int = DEFAULT_CONTAINER_MEMORY, + vcpus: int = DEFAULT_CONTAINER_VCPUS, + nat_interface_name: str = DEFAULT_NAT_INTERFACE_NAME, + bridge_interface_name: str = DEFAULT_BRIDGE_INTERFACE_NAME, + endpoint_username: str = DEFAULT_CONTAINER_ENDPOINT_USERNAME, + endpoint_password: str = DEFAULT_CONTAINER_ENDPOINT_PASSWORD, + endpoint_listen_port: int = DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT +) +``` + +> Represents a container instance + +_Parameters_ : + +- `uuid` : The new [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) +- `iso_path` : The ISO path that will be used for the container domain +- `memory` : The memory amount to allocate on the container, exprimed in Mb +- `vcpus` : The VCPUs amount to allocate on the container +- `nat_interface_name` : The [NAT interface name](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/networking.html#container) that will be used by the container +- `bridge_interface_name` : [The bridge interface](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/networking.html#container) name that will be used by the container +- `endpoint_username` : The [endpoint username](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#administration) that will be used for container administration +- `endpoint_password` : The [endpoint password](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#administration) that will be used for container administration +- `endpoint_listen_port` : The [endpoint listen port](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#administration) that will be used for container administration + +#### Methods + +``` +isDomainRunning() -> bool +``` + +> Check if the container domain is running + +_Parameters_ : + +- None + +_Return value_ : + +- A boolean value. `True` if the domain is running, `False` otherwise + +--- + +``` +getNATInterfaceName() -> str +``` + +> Get the NAT interface name of the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The NAT interface name of the instance + +--- + +``` +getBridgeInterfaceName() -> str +``` + +> Get the bridge interface name of the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The bridge interface name of the instance + +--- + +``` +getDomainDescriptor() -> None | libvirt.virDomain +``` + +> Get the `libvirt.virDomain` object of the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `libvirt.virDomain` object of the instance, or `None` is unavailable + +--- + +``` +getUUID() -> str +``` + +> Get the instance [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) + +_Parameters_ : + +- None + +_Return value_ : + +- The [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) of the instance + +--- + +``` +getISOPath() -> str +``` + +> Get the instance ISO path + +_Parameters_ : + +- None + +_Return value_ : + +- The instance ISO path on local storage + +--- + +``` +getMAC() -> str +``` + +> Get the container MAC address + +_Parameters_ : + +- None + +_Return value_ : + +- The instance container MAC address + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : The container domain must be started and ready in order to get its MAC address. + +--- + +``` +getIP() -> None | str +``` + +> Get the container IP address + +_Parameters_ : + +- None + +_Return value_ : + +- The instance container IP address, or `None` if the domain is not started or not ready yet + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : The container domain must be started and ready in order to get its IP, since the method will fetch it from the dnsmasq interface status file located in `/var/lib/libvirt/dnsmasq/` with its MAC address. + +--- + +``` +getMemory() -> int +``` + +> Get the allocated container memory amount + +_Parameters_ : + +- None + +_Return value_ : + +- The memory amount allocated to the container + +--- + +``` +getVCPUs() -> int +``` + +> Get the allocated container Virtual CPUs amount + +_Parameters_ : + +- None + +_Return value_ : + +- The VCPUs amount allocated to the container + +--- + +``` +setDomainDescriptor(domain_descriptor: libvirt.virDomain) -> None +``` + +> Set the `libvirt.virDomain` object of the instance + +_Parameters_ : + +- `domain_descriptor` : The `libvirt.virDomain` object to set on the instance + +_Return value_ : + +- None + +--- + +``` +setISOPath(iso_path: str) -> None +``` + +> Set the container ISO path of the instance + +_Parameters_ : + +- `iso_path` : The ISO path to set on the instance + +_Return value_ : + +- None + +--- + +``` +setMemory(memory: int) -> None +``` + +> Set the memory amount to allocate on the container + +_Parameters_ : + +- `memory` : The memory to allocate on the container, exprimed in Mb + +_Return value_ : + +- None + +**NOTE** : The parameter `memory` must be a value greater than 0 + +--- + +``` +setVCPUs(vcpus: int) -> None +``` + +> Set the Virtual CPUs amount to allocate on the container + +_Parameters_ : + +- `vcpus` : The amount of VCPUs to allocate on the container + +_Return value_ : + +- None + +**NOTE** : The parameter `vcpus` must be a value greater than 0 + +--- + +``` +setNATInterfaceName(nat_interface_name: str) -> None +``` + +> Set the NAT interface name of the container + +_Parameters_ : + +- `nat_interface_name` : The NAT interface name that will be used on the container domain + +_Return value_ : + +- None + +--- + +``` +setBridgeInterfaceName(bridge_interface_name: str) -> None +``` + +> Set the bridge interface name of the container + +_Parameters_ : + +- `bridge_interface_name` : The bridge interface name that will be used on the container domain + +_Return value_ : + +- None + +--- + +``` +setEndpointSSHAuthenticationCredentials( + endpoint_username: str = DEFAULT_CONTAINER_ENDPOINT_USERNAME, + endpoint_password: str = DEFAULT_CONTAINER_ENDPOINT_PASSWORD, + endpoint_listen_port: str = DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT +) -> None +``` + +> Set the container [endpoint SSH credentials](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#administration) for SSH authentication + +_Parameters_ : + +- `endpoint_username` : The endpoint username to set +- `endpoint_password` : The endpoint password to set +- `endpoint_listen_port` : The endpoint listen port to set + +_Return value_ : + +- None + +--- + +``` +makeISOChecksum() -> str +``` + +> Get the SHA256 digest of the instance container ISO + +_Parameters_ : + +- None + +_Return value_ : + +- The SHA256 digest of the container ISO + +--- + +``` +createEndpointShell() -> EndpointShellInstance +``` + +> Create a `EndpointShellInstance` object on the running container + +_Parameters_ : + +- None + +_Return value_ : + +- The `EndpointShellInstance` object representing the SSH shell on the container domain endpoint + +--- + +``` +startDomain( + wait_available: bool = DEFAULT_CONTAINER_WAIT_AVAILABLE, + wait_max_tryout: int = DEFAULT_CONTAINER_MAX_TRYOUT, + driver_uri: str = DEFAULT_LIBVIRT_DRIVER_URI +) -> None +``` + +> Start the [container domain](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#api) + +_Parameters_ : + +- `wait_available` : Wait for the network to be available on the domain or not +- `wait_max_tryout` : The amount of attemps to check if the network is available on the domain before raising `TimeoutError` +- `driver_uri` : The hypervisor [driver URI](https://libvirt.org/uri.html) to use. + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `RuntimeError` +- `TimeoutError` + +**NOTE** : By default, the qemu hypervisor URI `qemu:///system` is used. The container domain must be stopped before being started. + +--- + +``` +stopDomain(destroy: bool = DEFAULT_CONTAINER_DESTROY_DOMAIN) -> None +``` + +> Stop the container domain + +_Parameters_ : + +- `destroy` : Destroy the container domain rather than shutting it down or not + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : The container domain must be running before being stopped. + +### `VirtualizationInterface` + +#### Definition + +``` +class VirtualizationInterface( + container_iso_path: str, + max_allowed_containers: int = DEFAULT_MAX_ALLOWED_CONTAINERS, +) +``` + +> Provides container management functionality + +_Parameters_ : + +- `container_iso_path` : The container ISO path to set on created containers +- `max_allowed_containers` : The maximum allowed active containers amount on the instance, must be greater than 0 + +#### Methods + +``` +getIsoPath() -> str +``` + +> Get the instance ISO path + +_Parameters_ : + +- None + +_Return value_ : + +- The instance ISO path + +--- + +``` +getMaxAllowedContainersAmount() -> int +``` + +> Get the maximum allowed active containers amount on the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The maximum allowed active containers amount on the instance, is greater than 0 + +--- + +``` +getContainersAmount() -> int +``` + +> Get the active containers amount on the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The amount of active containers on the instance + +--- + +``` +getAvailableContainersAmount() -> int +``` + +> Get the amount of available containers left + +_Parameters_ : + +- None + +_Return value_ : + +- The amount of available containers left + +**NOTE** : This method justs substract the maximum allowed active containers amount on the instance with the actual active containers amount. + +--- + +``` +listStoredContainers() -> list +``` + +> List stored containers + +_Parameters_ : + +- None + +_Return value_ : + +- A list enumerating the stored container UUIDs + +--- + +``` +getStoredContainer(container_uuid: str) -> None | ContainerInstance +``` + +> Get a stored container + +_Parameters_ : + +- `container_uuid` : The container UUID to search for + +_Return value_ : + +- The `ContainerInstance` object of the crated container if exists, `None` otherwise + +--- + +``` +setIsoPath(iso_path: str) -> None +``` + +> Set the instance ISO path + +_Parameters_ : + +- `iso_path` : The instance ISO path to set. Must point to a valid ISO image + +_Return value_ : + +- None + +--- + +``` +setMaxAllowedContainers(max_allowed_containers: int) -> None +``` + +> Set the maximum allowed active container amount on the instance + +_Parameters_ : + +- `max_allowed_containers` : The maximum allowed active containers amount on the instance, must be greater than 0 + +_Return value_ : + +- None + +--- + +``` +addStoredContainer(container_instance: ContainerInstance) -> None +``` + +> Add a container on the instance + +_Parameters_ : + +- `container_instance` : The `ContainerInstance` object to add + +_Return value_ : + +- None + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : If the stored container amount become equal to the `max_allowed_containers` value from the class initialization, a `RuntimeError` will be raised. + +--- + +``` +deleteStoredContainer(container_uuid: str) -> None +``` + +> Delete a stored container + +_Parameters_ : + +- `container_instance` : The [container UUID](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials) to delete + +_Return value_ : + +- None + +--- + +``` +createContainer(store: bool = DEFAULT_STORE_CONTAINER) -> ContainerInstance +``` + +> Create a container + +_Parameters_ : + +- `store` : `True` to store the created container, `False` otherwise + +_Return value_ : + +- The `ContainerInstance` object of the created container + +_Possible raise classes_ : + +- `RuntimeError` + +**NOTE** : If the stored container amount become equal to the `max_allowed_containers` value from the class initialization, a `RuntimeError` will be raised. \ No newline at end of file diff --git a/docs/server/_sources/developer_section/api_references/tools/accesstk.md.txt b/docs/server/_sources/developer_section/api_references/tools/accesstk.md.txt new file mode 100644 index 0000000..116dcc0 --- /dev/null +++ b/docs/server/_sources/developer_section/api_references/tools/accesstk.md.txt @@ -0,0 +1,228 @@ +# Access token + +---- + +> Access token management features + +> Package `anwdlserver.tools.accesstk` + +## Constants + +Default values : + +| Name | Value | Description | +| ---------------------------- | -------- | -------------------------------------------- | +| `DEFAULT_DISABLE_TOKEN` | `False` | Enable created tokens by default | + +## Classes + +### `AccessTokenManager` + +#### Definition + +``` +class AccessTokenManager(auth_token_db_path: str) +``` + +> Provides access token features + +_Parameters_ : + +- `auth_token_db_path` : The access tokens database file path + +**NOTE** : The database and its cursors will be automatically closed on `__del__` method. Also, queries implying modifications on the database are automatically committed, and rollbacks are called if an error occured. + +#### Methods + +``` +getDatabaseConnection() -> sqlite3.Connection +``` + +> Get the [`sqlite3.Connection`](https://docs.python.org/3.8/library/sqlite3.html#sqlite3.Connection) object of the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `sqlite3.Connection` object of the instance + +--- +``` +getCursor() -> sqlite3.Cursor: +``` + +> Get the [`sqlite3.Cursor`](https://docs.python.org/3.8/library/sqlite3.html#sqlite3.Cursor) object of the instance + +_Parameters_ : + +- None + +_Return value_ : + +- The `sqlite3.Cursor` object of the instance + +--- +``` +getEntryID(access_token: str) -> None | int +``` + +> Get the access token entry ID (similar to the ROWID in sqlite, identifies the row) + +_Parameters_ : + +- `access_token` : The clear access token to search for + +_Return value_ : + +- The access token entry ID if exists, `None` otherwise + +**NOTE** : This method must be used for client access verification. If the access token entry is disabled, the method will ignore the entry and return `None` as a result. + +--- +``` +getEntry(entry_id: int) -> tuple +``` + +> Get an entry content + +_Parameters_ : + +- `entry_id` : The entry ID to get + +_Return value_ : + +- A tuple representing the entry content : + +``` +( + entry_id, + creation_timestamp, + access_token, + enabled +) +``` + +- `entry_id` : The entry ID +- `creation_timestamp` : The entry creation timestamp +- `access_token` : The hashed access token +- `enabled` : A boolean value (`1` or `0`) depicting if the entry is enabled or not + +**NOTE** : The `access_token` value is hashed with SHA256 as described in the [Technical specifications](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/access_token.html#tokens). + +--- +``` +addEntry(disable: bool = DEFAULT_DISABLE_TOKEN) -> tuple +``` + +> Create an entry + +_Parameters_ : + +- `disable` : `True` to disable the token entry by default, `False` otherwise + +_Return value_ : + +- A tuple representing the created token entry informations : + +``` +( + entry_id, + auth_token +) +``` + +- `entry_id` : The created entry ID +- `auth_token` : The created access token, in plain text + +**NOTE** : Since tokens are hashed with SHA256 in the database (see the technical specifications [Access token](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/access_token.html#tokens) section to learn more), there's no way to see them again in plain text : Store this clear created token somewhere safe in order to use it for further operations. + +--- +``` +listEntries() -> list +``` + +> List entries + +_Parameters_ : + +- None + +_Return value_ : + +- A list of tuples representing every entries on the database : + +``` +( + entry_id, + creation_timestamp, + enabled +) +``` + +- `entry_id` : The entry ID +- `creation_timestamp` : The entry creation timestamp +- `enabled` : `True` if the entry is enabled, `False` otherwise + +--- +``` +enableEntry(entry_id: int) -> None +``` + +> Enable the usage of an entry + +_Parameters_ : + +- `entry_id` : The entry ID to enable + +_Return value_ : + +- None + +--- +``` +disableEntry(entry_id: int) -> None +``` + +> Disable the usage of an entry + +_Parameters_ : + +- `entry_id` : The entry ID to disable + +_Return value_ : + +- None + +--- +``` +deleteEntry(entry_id: int) -> None +``` + +> Delete an entry + +_Parameters_ : + +- `entry_id` : The entry ID to delete on the database + +_Return value_ : + +- None + +--- +``` +closeDatabase() -> None +``` + +> Close the database + +_Parameters_ : + +- None + +_Return value_ : + +- None + +**NOTE** : This method is automatically called within the `__del__` method, but it is programatically better to call it naturally \ No newline at end of file diff --git a/docs/server/_sources/developer_section/cli/json_output.md.txt b/docs/server/_sources/developer_section/cli/json_output.md.txt new file mode 100644 index 0000000..5710edd --- /dev/null +++ b/docs/server/_sources/developer_section/cli/json_output.md.txt @@ -0,0 +1,224 @@ +# CLI JSON output + +---- + +In order to communicate with other programs in an easy way, the Anweddol server CLI provides a JSON output feature that allows [inter-program communication](https://clig.dev/#simple-parts-that-work-together. + +## Global structure + +Each commands with the `--json` parameter results on a single JSON structure printed on `stdout` : + +``` +{ + "status": STATUS + "message": MESSAGE + "data": DATA +} +``` + +- `STATUS` : The result status, it can be `"OK"` if there was no errors during the process, `"ERROR"` otherwise. +- `MESSAGE` : The message according to the command purpose. +- `DATA` : A dictionary containing every exploitable informations that a command can generate. + +The `DATA` dictionary content changes according to the command context (see below). + +**NOTE** : Configuration file related errors arent produced in a JSON format. + +## Specific result JSON structures + +### Errors + +When an error is raised during the process with any `--json` parameter set with subcommands, the output JSON will be : + +``` +{ + "status": "ERROR", + "message": "An error occured", + "data": { + "error": ERROR + } +} +``` + +- `ERROR` : The error message that occured + +### `start` sub-command + +`anwdlserver start` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Server is started", + "data": {} +} +``` + +**NOTE** : If the option `-d` is set, no output will be displayed. + +If the option `-c` is set : + +``` +{ + "status": "OK", + "message": "Check done", + "data": { + "errors_recorded": ERRORS_RECORDED, + "errors_list": ERRORS_LIST + } +} +``` + +If an error occured during server environment verification, the JSON structure looks like this : + +``` +{ + "status": "ERROR", + "message": "Errors detected on server environment", + "data": { + "errors_recorded": ERRORS_RECORDED, + "errors_list": ERRORS_LIST + } +} +``` + +- `ERRORS_RECORDED` : The amount of errors recorded +- `ERRORS_LIST` : The errors messages list + +### `stop` sub-command + +`anwdlserver stop` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Server is stopped", + "data": {} +} +``` + +### `restart` sub-command + +`anwdlserver restart` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Server is started", + "data": {} +} +``` + +### `dl-iso` sub-command + +`anwdlserver dl-iso` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "ISO image was successfully downloaded", + "data": { + "checksums": CHECKSUMS, + "version": VERSION, + "file_path": FILE_PATH + } +} +``` + +- `CHECKSUMS` : The downloaded ISO MD5 and SHA256 checksums, in a list +- `VERSION` : The downloaded [ISO version](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/iso_management.html#official-mirror) +- `FILE_PATH` : The downloaded ISO file path + +if the option `-m` is set : + +``` +{ + "status": "OK", + "message": "Remote ISO metadata", + "data": { + "checksums": CHECKSUMS, + "version": VERSION, + } +} +``` + +- `CHECKSUMS` : The downloaded ISO MD5 and SHA256 checksums, in a list +- `VERSION` : The downloaded [ISO version](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/tools/iso_management.html#official-mirror) + +### `access-tk` sub-command + +`anwdlserver access-tk -a` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "New access token created", + "data": { + "entry_id": ENTRY_ID, + "access_token": ACCESS_TOKEN, + } +} +``` + +- `ENTRY_ID` : The created entry ID +- `ACCESS_TOKEN` : The new access token, in plain text + +`anwdlserver access-tk -l` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Recorded entries ID", + "data": { + "entry_list": ENTRY_LIST + } +} +``` + +- `ENTRY_LIST` : The recorded entries list + +`anwdlserver access-tk -r` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Entry ID was deleted", + "data": {} +} +``` + +`anwdlserver access-tk -e` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Entry ID was enabled", + "data": {} +} +``` + +`anwdlserver access-tk -d` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "Entry ID was disabled", + "data": {} +} +``` + +### `regen-rsa` sub-command + +`anwdlserver regen-rsa` with the `--json` parameter will result in : + +``` +{ + "status": "OK", + "message": "RSA keys re-generated", + "data": { + "fingerprint": FINGERPRINT + } +} +``` + +- `FINGERPRINT` : The new generated public key's SHA256 digest \ No newline at end of file diff --git a/docs/server/_sources/developer_section/examples/basic_server.md.txt b/docs/server/_sources/developer_section/examples/basic_server.md.txt new file mode 100644 index 0000000..55f6f70 --- /dev/null +++ b/docs/server/_sources/developer_section/examples/basic_server.md.txt @@ -0,0 +1,23 @@ +# Basic server + +---- + +Here is a simple server stub that can be used as a functional server : + +``` +from anwdlserver.core.server import ServerInterface + +# Replace it with your own path +CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso" + +# Create a new ServerInterface instance +server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH) + +# Print the message when the server is ready +@server.on_started +def notify_started(**kwargs): + print("Server is started") + +# Start the server +server.startServer() +``` \ No newline at end of file diff --git a/docs/server/_sources/developer_section/examples/custom_container_capacity.md.txt b/docs/server/_sources/developer_section/examples/custom_container_capacity.md.txt new file mode 100644 index 0000000..d8ee839 --- /dev/null +++ b/docs/server/_sources/developer_section/examples/custom_container_capacity.md.txt @@ -0,0 +1,38 @@ +# Set custom container capacity + +---- + +``` +from anwdlserver.core.server import ServerInterface + +# Replace it with your own path +CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso" + +# Create a new ServerInterface instance +server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH) + +# Print the message when the server is ready +@server.on_started +def notify_started(**kwargs): + print("Server is started") + +# Print a message when a request is received +@server_interface.on_request +def notify_request(**kwargs): + request_verb = kwargs["client_instance"].getStoredRequest() + + print("Received {} request".format(request_verb["verb"])) + +# Set created containers capacity with 2048 Mb of memory and 2 VCPUs +@server.on_container_created +def handle_container_creation(**kwargs): + container_instance = kwargs.get("container_instance") + + print(f"New container created : {container_instance.getUUID()}") + + container_instance.setMemory(2048) + container_instance.setVCPUs(2) + +# Start the server +server.startServer() +``` \ No newline at end of file diff --git a/docs/server/_sources/developer_section/examples/custom_verb_handle.md.txt b/docs/server/_sources/developer_section/examples/custom_verb_handle.md.txt new file mode 100644 index 0000000..9e93c24 --- /dev/null +++ b/docs/server/_sources/developer_section/examples/custom_verb_handle.md.txt @@ -0,0 +1,40 @@ +# Handle a custom request verb + +---- + +This stub shows how to implement a custom verb handling, here the server will respond "PONG" in parameters when a client sends a "PING" request : + +``` +from anwdlserver.core.server import ( + ServerInterface, + RESPONSE_MSG_OK +) + +# Replace it with your own path +CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso" + +# Sends 'PONG' in response when a 'PING' request is received +def handle_ping_request(**kwargs): + client_instance = kwargs.get("client_instance") + + print("Received PING request") + + client_instance.sendResponse(True, RESPONSE_MSG_OK, data={"answer": "PONG"}) + + # Not mandatory, but programatically correct + client_instance.closeConnection() + +# Create a new ServerInterface instance +server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH) + +# Print the message when the server is ready +@server.on_started +def notify_started(**kwargs): + print("Server is started") + +# Add the new 'PING' request handler +server.setRequestHandler("PING", handle_ping_request) + +# Start the server +server.startServer() +``` \ No newline at end of file diff --git a/docs/server/_sources/developer_section/examples/ip_filtering.md.txt b/docs/server/_sources/developer_section/examples/ip_filtering.md.txt new file mode 100644 index 0000000..a8c38dd --- /dev/null +++ b/docs/server/_sources/developer_section/examples/ip_filtering.md.txt @@ -0,0 +1,39 @@ +# Implement IP filtering + +---- + +Here is a server stub that involves an IP filtering feature : + +> For performance reasons, it is recommended to implement an IP filtering feature with the `on_connection` callback since it is faster than a whole client instance. + +``` +from anwdlserver.core.server import ServerInterface + +# Replace it with your own path / values +CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso" +DENIED_IP_ARRAY = ["1.1.1.1", "3.3.3.3", "5.5.5.5"] + +# Create a new ServerInterface instance +server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH) + +# Print the message when the server is ready +@server.on_started +def notify_started(**kwargs): + print("Server is started") + +# IP filtering implementation +@server.on_connection +def handle_connection(**kwargs): + client_socket = kwargs.get("client_socket") + client_ip = client_socket.getpeername()[0] + + if client_ip in DENIED_IP_ARRAY: + # The server process will notice that the client socket is closed + # and will pass the session after the end of this routine. + print(f"Connection closed for denied IP : {client_ip}") + + client_socket.close() + +# Start the server +server.startServer() +``` \ No newline at end of file diff --git a/docs/server/_sources/developer_section/index.md.txt b/docs/server/_sources/developer_section/index.md.txt new file mode 100644 index 0000000..6417bc9 --- /dev/null +++ b/docs/server/_sources/developer_section/index.md.txt @@ -0,0 +1,166 @@ +# Developer section + +---- + +Hello and welcome to the Anweddol server developer documentation. + +Here, you'll find every informations and documentation about the server [python](https://www.python.org/) API features. + +**NOTE** : At the root of `anwdlserver`, there is the CLI source code : They are not meant to be used on an external program since it's the server implementation's code itself. + +## Important note + +The Anweddol server installation requires a specific setup before installation and usage in order to provide a functional service. + +See the administration guide [Installation section](https://anweddol-server.readthedocs.io/en/latest/administration_guide/installation.html) to learn more before getting started on the server API. + +## Examples + +See basic server stubs that can be used as examples. + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +examples/basic_server +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +examples/custom_verb_handle +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +examples/ip_filtering +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +examples/custom_container_capacity +``` + +## API references + +Learn about every features that the Anweddol server can provide. +You can also see the *Technical specificatons* section to get every informations on how the Anweddol server works. + +### Core features + +The core features, also called `core`, are every needed functionnalities that an Anweddol server must have in order to provide a valid service. + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/client +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/cryptography +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/database +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/sanitization +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/server +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/utilities +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/core/virtualization +``` + +### Tools features + +The `tools` features are additional functionnalities (authentication utilities, ...) coming with the Anweddol server package. + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +api_references/tools/accesstk +``` + +## CLI references + +The actual Anweddol server CLI provides a JSON output feature that allows inter-program communication. + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +cli/json_output +``` + +## Troubleshooting + +A troubleshooting page is also available for the Anweddol server API : + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +troubleshooting +``` \ No newline at end of file diff --git a/docs/server/_sources/developer_section/troubleshooting.md.txt b/docs/server/_sources/developer_section/troubleshooting.md.txt new file mode 100644 index 0000000..93c27ce --- /dev/null +++ b/docs/server/_sources/developer_section/troubleshooting.md.txt @@ -0,0 +1,19 @@ +# Troubleshooting + +---- + +Here is a list of non-exhaustive potential problems that can be encountered while using the Anweddol server API : + +## `libvirt: error : Cannot get interface MTU on 'virbr0'`[...] + +*Description* : The libvirt daemon is not started. + +*Solution* : + +Start the libvirtd daemon : + +``` +$ sudo systemctl start libvirtd.service +``` + +Then restart your script. \ No newline at end of file diff --git a/docs/server/_sources/index.md.txt b/docs/server/_sources/index.md.txt new file mode 100644 index 0000000..210bfcc --- /dev/null +++ b/docs/server/_sources/index.md.txt @@ -0,0 +1,82 @@ +# The Anweddol server + +[![made-with-python](https://img.shields.io/badge/Made%20with-Python-important)](https://www.python.org/) +[![build-passing](https://img.shields.io/badge/build-passing-green.svg)](https://shields.io/) +[![license](https://img.shields.io/badge/license-GPLv3-blue.svg)](https://shields.io/) +[![reddit](https://img.shields.io/reddit/subreddit-subscribers/Anweddol?style=social)](https://www.reddit.com/r/Anweddol/) + +---- + +## Global overview + +Anweddol is a client/server system providing temporary, SSH-controllable virtual machines to enhance anonymity online. + +It’s usefulness comes when someone wants to use a fully functional computer while being exposed to less dangers by using it remotely on a dedicated server, and by destroying it after use. + +## Effective scenario + +*terminology :* + +- **Container** : A temporary virtual machine hosted on a server that can be controlled from the client side. + +1. Alice wants to have access to a container. She will first send a request asking it to create a new container. +2. The server will start a new container, administer it and send the SSH credentials to Alice for her to be able to securely interact with the container. +3. Once that Alice is done, she will send another request with previously received container identification and credentials to the server. +4. The server identifies the affiliated running container, and destroys it. + +## Contents + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +administration_guide/index +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +developer_section/index +``` + +```{toctree} +--- +maxdepth: 2 +includehidden: +--- + +technical_specifications/index +``` + +## Contribution + +See the contribution section to know how to contribute : + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +contribute +``` + +## Links + +Here is the social links of the Anweddol project : + +- [https://www.reddit.com/r/Anweddol/](https://www.reddit.com/r/Anweddol/) +- [https://github.com/the-anweddol-project](https://github.com/the-anweddol-project) +- [https://anweddol.zulipchat.com](https://anweddol.zulipchat.com) + +You can also mail `the-anweddol-project@proton.me`, consider using the [PGP public key](https://the-anweddol-project.github.io/contact/A050C2B36F2E80BE6FDE6E0F0D3F21975020EFC0.asc) + +## Indices and tables + +- {ref}`genindex` +- {ref}`search` \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/core/client_authentication.md.txt b/docs/server/_sources/technical_specifications/core/client_authentication.md.txt new file mode 100644 index 0000000..2b7afe9 --- /dev/null +++ b/docs/server/_sources/technical_specifications/core/client_authentication.md.txt @@ -0,0 +1,35 @@ +# Client authentication + +---- + +Natively, the server handles 2 kinds of credentials : + +- The session credentials ; +- The container credentials ; + +## Session credentials + +The session credentials are credentials used to authenticate a client when a DESTROY request is received. +There is 2 affiliated members : + +- The container UUID ; +- The client token ; + +The container UUID is the [UUID of the container](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/virtualization.html#management) that the client wants to destroy. + +The client token is a private token given to the client when a container is created on receipt of a CREATE request, to ensure that the client who wants to destroy the container in question is actually the one who owns it. A token is a 255-character url-safe string, to guarantee minimum usurpability. + +## Container credentials + +The container credentials are the SSH credentials to use with the container in order to be able to interact with it. +There is 3 affiliated members : + +- The SSH username ; +- The SSH password ; +- The SSH listen port ; + +The SSH username is in the format `user_NUM`, where NUM is a random number between 10000 and 90000. + +The SSH password is a 120 character string. + +The SSH listen port is a random port between 10000 and 15000, whose bindability is checked before assignation. \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/core/communication.md.txt b/docs/server/_sources/technical_specifications/core/communication.md.txt new file mode 100644 index 0000000..d0ff81b --- /dev/null +++ b/docs/server/_sources/technical_specifications/core/communication.md.txt @@ -0,0 +1,248 @@ +# Communication + +---- + +## Format + +Requests and responses sent between the client and the server are JSON structures. + -> A widely used data format, cross-platform and easily manipulable. + +Before sending anything, the size of the packet is sent in an 8 byte message, padded with '=' characters : + +| Message | Length | Padded message length | +| --------------- | ------------- | --------------------------- | +| `"hello world"` | 11 characters | `"11======"` (8 characters) | + +Thus, the native theorical maximum packet size is 99 999 999 bytes. + +## Request format + +Here is a typical request structure that a server will receive : + +``` +{ + "verb": VERB; + "parameters": PARAMETERS +} +``` + +- `VERB` : Like an HTTP request, the verb depicts the action to execute on the server side. There is 3 natively supported verbs : + + - `"CREATE"` : Defines the intent to create a new container. + - `"DESTROY"` : Defines the intent to destroy a previously created container. + - `"STAT"` : Defines the intent to gather information about a server runtime. + +- `PARAMETERS` : This is the section reserved for any kind of parameters used to provide additional information in a request. + +> Only the `"DESTROY"` request requires some parameters to authenticate the client (see the [Authentication](https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html) section to learn more). + +## Response format + +Here is a typical response structure that a client will receive : + +``` +{ + "success": SUCCESS; + "message": MESSAGE; + "data": DATA +} +``` + +- `SUCCESS` : The success is a boolean value that defines the current state of the request on the server. + + - `True` : The request was successfully satisfied. + - `False` : There was an error during the processing of the request. + +- `MESSAGE` : The additional information coming along with the success of the response. +It can be anything that explains what happened on the server side (see the 'Error handling' point below). + +- `DATA` : The data section, reserved for returned parameters. + +## Error handling + +When an error occurs during the processing of a request, a message is set in the response explaining what's wrong. + +Here is a non-exhaustive list of status codes and their messages : + +|Message | Meaning | +|---------------------- | -------------------------------------------- | +|`"OK"` | The request was successfully satisfied | +|`"Bad authentication"` | The sender specified invalid credentials | +|`"Bad request"` | The previous request was malformed | +|`"Refused request"` | The request was refused | +|`"Unavailable"` | The server is currently unavailable | +|`"Internal error"` | The server is experiencing an internal error | + +Note that messages depicting an error may come with an additional explanation of why the error occurred : + +``` +"Bad request (reason : Unsupported or unknown verb)" +``` + +## Security + +### Encryption + +For security and integrity reasons, requests and responses are encrypted in AES 256 CBC. +Each AES key and Initialization Vectors are different for every client connection session. + +RSA keys length is 4096 bytes by default. The RSA implementation is used to send the connection session AES key to the client securely. + +Here is a visual example of how the keys are exchanged with a client : + +> Bold text mean RSA encrypted text + +> '>'/'<' symbol means 'send' and 'o' symbol means 'receive' + +| A | packet content | B | +|---|------------------|---| +|> | connexion |o | +|> | A RSA public key |o | +|o | validation |< | +|o | B RSA public Key |< | +|> | validation |o | +|> | **A AES Key** |o | +|o | validation |< | +|o | **B AES Key** |< | +|> | validation |o | + +### Sanitization + +Requests and responses are sanitized upon sending and receiving at each end. +Here is the raw [cerberus](https://docs.python-cerberus.org/en/stable/index.html) validation scheme used to verify the format and content : + +**Requests** + +``` +{ + "verb": { + "type": "string", + "regex": r"^[A-Z]{1,}$", + "required": True, + }, + "parameters": { + "type": "dict", + "required": True, + "schema": { + "container_uuid": { + "type": "string", + "required": False, + "check_with": __check_container_uuid, + "dependencies": ["client_token"] + }, + "client_token": { + "type": "string", + "required": False, + "check_with": __check_client_token, + "dependencies": ["container_uuid"] + } + } + } +} +``` + +**Responses** + +``` +{ + "success": { + "type": "boolean", + "required": True + }, + "message": { + "type": "string", + "required": True + }, + "data": { + "type": "dict", + "required": True, + "schema": { + "container_uuid": { + "type": "string", + "regex": r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "required": False, + "dependencies": [ + "client_token", + "container_iso_sha256", + "container_username", + "container_password", + "container_listen_port" + ] + }, + "client_token": { + "type": "string", + "regex": r"^[0-9a-zA-Z-_]{255}$", + "required": False, + "dependencies": [ + "container_uuid", + "container_iso_sha256", + "container_username", + "container_password", + "container_listen_port" + ] + }, + "container_iso_sha256": { + "type": "string", + "regex": r"^[a-f0-9]{64}$", + "required": False, + "dependencies": [ + "container_uuid", + "client_token", + "container_username", + "container_password", + "container_listen_port" + ] + }, + "container_username": { + "type": "string", + "regex": r"^user_[0-9]{5}$", + "required": False, + "dependencies": [ + "container_uuid", + "client_token", + "container_iso_sha256", + "container_password", + "container_listen_port" + ] + }, + "container_password": { + "type": "string", + "regex": r"^[a-zA-Z0-9]{1,}$", + "required": False, + "dependencies": [ + "container_uuid", + "client_token", + "container_iso_sha256", + "container_username", + "container_listen_port" + ] + }, + "container_listen_port": { + "type": "integer", + "required": False, + "min": 1, + "max": 65535, + "dependencies": [ + "container_uuid", + "client_token", + "container_iso_sha256", + "container_username", + "container_password" + ] + }, + "uptime": { + "type": "integer", + "required": False, + "min": 0, + "dependencies": ["available"] + }, + "available": { + "type": "integer", + "required": False, + "min": 0, + "dependencies": ["uptime"] + } + } + } +} +``` \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/core/database.md.txt b/docs/server/_sources/technical_specifications/core/database.md.txt new file mode 100644 index 0000000..8ec9127 --- /dev/null +++ b/docs/server/_sources/technical_specifications/core/database.md.txt @@ -0,0 +1,25 @@ +# Database + +---- + +## Engine + +The server is using a sqlite-based SQLAlchemy ORM memory database engine to ensure its content volatility. +See the [SQLAlchemy website](https://www.sqlalchemy.org/) to learn more. + +## Table representation + +Here is a representation of the SQL table used during the server run time : + +| EntryID | CreationTimestamp | ContainerUUID | ClientToken | +| --------------------- | ----------------- | ------------- | ------------- | +| `Integer primary key` | `Integer` | `String` | `String` | + +- `EntryID` : Store the entry ID +- `CreationTimestamp` : Store the creation timestamp of the entry +- `ContainerUUID` : Store the container UUID +- `ClientToken` : Store the affiliated client token + +## Security + +The data written in the `ContainerUUID` and the `ClientToken` columns are hashed with SHA256. \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/core/networking.md.txt b/docs/server/_sources/technical_specifications/core/networking.md.txt new file mode 100644 index 0000000..61d122f --- /dev/null +++ b/docs/server/_sources/technical_specifications/core/networking.md.txt @@ -0,0 +1,23 @@ +# Networking + +---- + +## Server + +In order to let clients and containers communicate, the server needs a specific interface. + +A bridge interface `anwdlbr0` must be created on the server, with its current physical network interface configured as a slave on it. Containers will connect via TAP on this bridge, ensuring full-bridged networking for the container. + +> Read the 'Administration guide' for more info. + +## Container + +Each container have two network interfaces : +- A NAT interface ; +- A bridged interface ; + +-> The NAT interface is used for server administration. +It is an interface based on the `virbr0` libvirt virtual bridge. + +-> The bridged interface is used to receive client traffic. +It is based on the manually created bridge on the system (see above), thus providing full-bridged networking. diff --git a/docs/server/_sources/technical_specifications/core/virtualization.md.txt b/docs/server/_sources/technical_specifications/core/virtualization.md.txt new file mode 100644 index 0000000..332a2e4 --- /dev/null +++ b/docs/server/_sources/technical_specifications/core/virtualization.md.txt @@ -0,0 +1,122 @@ +# Virtualization + +---- + +## API + +The Anweddol server uses the Libvirt API to manage containers. +You'll retrieve the full documentation on the [libvirt python binding](https://www.libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/) documentation website. + +A *container* is a libvirt domain wrapper utility running with a live debian image (see below). + +The domain XML used for container domain definition is : + +``` + + container_uuid + memory + memory + vcpus/vcpu> + container_uuid + + hvm + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + +``` + +- `container_uuid` : The container UUID +- `memory` : The container domain memory amount +- `vcpus` : The container domain virtual CPUs amount +- `iso_path` : The ISO file path that will be used by the container domain +- `nat_interface_name` : The NAT interface name that will be used by the container domain +- `bridge_interface_name` : The bridge interface name that will be used by the container domain + +## Container OS + +In order to provice a valid service, containers domains needs to run with a specific ISO image. + +The operating system running on containers is a custom live Debian to ensure as little data remanence as possible on the virtual domain : All basic tools like text editors, network interactions, development environment are already pre-installed to bring comfortable usage for the client. + +See the [ISO factory repository](https://github.com/the-anweddol-project/Anweddol-ISO-factory) to get the script that creates these ISO images. +You can retrieve a copy of this container ISO on the [official mirror](https://mega.nz/folder/BTFyVCLB#DNC2K8Lmhgbk6QWrVpeznw). + +This mirror URL contains this file tree : + +``` +. +├── anweddol_container.iso +├── md5sum.txt +├── sha256sum.txt +└── version.txt +``` + +- `anweddol_container.iso` is the actual container ISO image. +- `md5sum.txt` contains the MD5 checksum of `anweddol_container.iso` +- `sha256sum.txt` contains the SHA256 checksum of `anweddol_container.iso` +- `version.txt` is the version of the ISO. It contains an integer which is incremented by 1 each updates. + +## Management + +During a server runtime, running container coordinates created for clients are stored in memory to manage them. + +If a container is shut down, a routine will detect that its domain is not active, and will delete the container's presence in memory and database to prevent ghosted credentials. + +Containers are identified with an UUID4 format string, and their [libvirt domains UUID](https://libvirt.gitlab.io/libvirt-appdev-guide-python/libvirt_application_development_guide_using_python-Guest_Domains-Information-UUID.html) are the same one. + +## Administration + +When a container is started, the server first waits for the network to be available inside. +Then, the server generates new user coordinates and a new listen port that the container will use for client service. + +On every container a specific user is defined called **endpoint**. + +This is a user dedicated to container administration that the server will interact via SSH to administrate it (it is actually calling a bash script inside that you can retrieve on the ISO factory [github repository](https://github.com/the-anweddol-project/Anweddol-ISO-factory)). Its default password is `endpoint`, and the container default SSH server is listening on the conventional port (80). + +The bash script executes 3 tasks : + +- Create the new specified user that the client will use ; +- Set up the new SSH listen port ; +- Disabling the endpoint user once its task is executed ; + +The username is in the format `user_NUM`, where NUM is a random number between 10000 and 90000. +The user password is a 120 character string. +The new listen port is a random port between 10000 and 15000, whose bindability is checked before assignation. + +When the script will be called, the container will only accept SSH connections with the newly created user, since the endpoint user will be disabled in the process. \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/index.md.txt b/docs/server/_sources/technical_specifications/index.md.txt new file mode 100644 index 0000000..6a6658b --- /dev/null +++ b/docs/server/_sources/technical_specifications/index.md.txt @@ -0,0 +1,64 @@ +# Technical specifications +--- + +Hello and welcome to the Anweddol server technical specifications. + +You'll retrieve documentation about how the Anweddol server and its affiliated features works. + +## Core features + +```{toctree} +--- +maxdepth: 2 +includehidden: +--- + +core/client_authentication +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +core/communication +``` + +```{toctree} +--- +maxdepth: 3 +includehidden: +--- + +core/database +``` + +```{toctree} +--- +maxdepth: 2 +includehidden: +--- + +core/networking +``` + +```{toctree} +--- +maxdepth: 2 +includehidden: +--- + +core/virtualization +``` + +## Tools features + +```{toctree} +--- +maxdepth: 2 +includehidden: +--- + +tools/access_token +``` \ No newline at end of file diff --git a/docs/server/_sources/technical_specifications/tools/access_token.md.txt b/docs/server/_sources/technical_specifications/tools/access_token.md.txt new file mode 100644 index 0000000..d7d9588 --- /dev/null +++ b/docs/server/_sources/technical_specifications/tools/access_token.md.txt @@ -0,0 +1,35 @@ +# Access token + +---- + +An access token system is provided in the `tools` features. +It is used to restrict users of the service by providing tokens to authenticate them before processing the request. + +A typical use case is when a client sends a request on a server, it must send an access token in the request parameters that must match one of the tokens in the database in order to be processed by the server. + +## Tokens + +Tokens used are 124 characters url-safe strings. + +## Database + +### Engine + +This feature is using a SQLite file to store data. + +### Table representation + +Here is a representation of the used SQL table : + +| EntryID | CreationTimestamp | AccessToken | Enabled | +|------------------------------- | ------------------- | --------------- | ------------------ | +| `INTEGER NOT NULL PRIMARY KEY` | `INTEGER NOT NULL` | `TEXT NOT NULL` | `INTEGER NOT NULL` | + +- `EntryID` : Identifies the row +- `CreationTimestamp` : Store the row creation timestamp +- `AccessToken` : Store the affiliated access token +- `Enabled` : Store a boolean value (`1` / `0`) depicting if the row must be used or ignored + +### Security + +Any tokens written in the `AccessToken` column are hashed with SHA256. \ No newline at end of file diff --git a/docs/server/_static/alabaster.css b/docs/server/_static/alabaster.css new file mode 100644 index 0000000..517d0b2 --- /dev/null +++ b/docs/server/_static/alabaster.css @@ -0,0 +1,703 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/docs/server/_static/basic.css b/docs/server/_static/basic.css new file mode 100644 index 0000000..7577acb --- /dev/null +++ b/docs/server/_static/basic.css @@ -0,0 +1,903 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/server/_static/custom.css b/docs/server/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/docs/server/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs/server/_static/doctools.js b/docs/server/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/docs/server/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/server/_static/documentation_options.js b/docs/server/_static/documentation_options.js new file mode 100644 index 0000000..f11cda6 --- /dev/null +++ b/docs/server/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: 'beta-1.2.8', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/server/_static/file.png b/docs/server/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/docs/server/_static/file.png differ diff --git a/docs/server/_static/icon2.png b/docs/server/_static/icon2.png new file mode 100644 index 0000000..75f6436 Binary files /dev/null and b/docs/server/_static/icon2.png differ diff --git a/docs/server/_static/language_data.js b/docs/server/_static/language_data.js new file mode 100644 index 0000000..250f566 --- /dev/null +++ b/docs/server/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/docs/server/_static/minus.png b/docs/server/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/docs/server/_static/minus.png differ diff --git a/docs/server/_static/plus.png b/docs/server/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/docs/server/_static/plus.png differ diff --git a/docs/server/_static/pygments.css b/docs/server/_static/pygments.css new file mode 100644 index 0000000..9abe04b --- /dev/null +++ b/docs/server/_static/pygments.css @@ -0,0 +1,83 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/server/_static/searchtools.js b/docs/server/_static/searchtools.js new file mode 100644 index 0000000..97d56a7 --- /dev/null +++ b/docs/server/_static/searchtools.js @@ -0,0 +1,566 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = docUrlRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = docUrlRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/server/_static/sphinx_highlight.js b/docs/server/_static/sphinx_highlight.js new file mode 100644 index 0000000..aae669d --- /dev/null +++ b/docs/server/_static/sphinx_highlight.js @@ -0,0 +1,144 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + parent.insertBefore( + span, + parent.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/docs/server/administration_guide/access_token.html b/docs/server/administration_guide/access_token.html new file mode 100644 index 0000000..a46d774 --- /dev/null +++ b/docs/server/administration_guide/access_token.html @@ -0,0 +1,190 @@ + + + + + + + + Access token — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Access token

+
+

The Anweddol server implementation can restrict its utilization by using the tools Access token feature for client authentication.

+

An access token is a url-safe 124 characters long used on the implementation to restrict access. +These tokens are stored in a SQLite database file on the system.

+

NOTE : There is one token for one client, since a client cannot store 2 tokens for one same server (see the Client usage guide to learn more).

+
+

Prerequisites

+

This feature can be enabled or disabled. +Being disabled by default, you can enable it by changing the enabled field value for True :

+
[...]
+  # List of allowed / denied IPs
+  # Specify any IPs with 'any'
+  allowed_ip_list:
+    - any
+  denied_ip_list: []
+
+# ---
+# Access token parameters
+token_authentication:
+
+  # Enable this feature or not
+  # If this value is set to 'false', all parameters below will be ignored 
+  enabled: True # <----- Here
+
+[...]
+
+
+

Then restart the server.

+

NOTE : If this feature is enabled but no tokens are added, no clients will be able to use the server.

+
+
+

Add / delete a token

+

To add a token to the database, execute :

+
$ anwdlserver access-tk -a
+
+
+

It will result with the created token and its entry ID on the standard output.

+

On the client-side, you need to record this token in order to be able to authenticate. +See the Client usage guide to learn more.

+

NOTE : Since the access tokens are hashed in the database (see the technical specifications Access token section to learn more), there’s no way to see them again in plain text : Store this plain token somewhere safe in order to use it for further operations.

+

If you want to delete a token, execute :

+
$ anwdlserver access-tk -r <entry_id>
+
+
+
+
+

Enable / Disable a token

+

You have the possibility to enable or disable recorded tokens to temporarily disable its usage.

+

To disable a token, execute :

+
$ anwdlserver access-tk -d <entry_id>
+
+
+

NOTE : A created token is automatically enabled. Use the --disabled option with the -a parameter to disable it.

+

And to re-enable it :

+
$ anwdlserver access-tk -e <entry_id>
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/configuration_file.html b/docs/server/administration_guide/configuration_file.html new file mode 100644 index 0000000..91f349a --- /dev/null +++ b/docs/server/administration_guide/configuration_file.html @@ -0,0 +1,130 @@ + + + + + + + + Configuration file — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Configuration file

+
+

The Anweddol server implementation is using a YAML file for configuration.

+

It is stored in /etc/anweddol/config.yaml after installation, read its header to learn about it.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/container_iso.html b/docs/server/administration_guide/container_iso.html new file mode 100644 index 0000000..a3f256e --- /dev/null +++ b/docs/server/administration_guide/container_iso.html @@ -0,0 +1,141 @@ + + + + + + + + Container ISO — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Container ISO

+
+

In order to provice a valid service, containers domains needs to run with a specific ISO image.

+
+

Download the ISO image

+

This ISO image is actually a custom live Debian image that you can retrieve on the official mirror.

+

You’ll need to copy the downloaded anweddol_container.iso file on the /etc/anweddol/iso/ folder by default, or you can specify the ISO path in the configuration file, in the container_iso_path key.

+
+

This ISO image is meant to be replaced at each update announced

+
+

Learn more about the container ISO on the technical specifications Container OS section.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/index.html b/docs/server/administration_guide/index.html new file mode 100644 index 0000000..497c6e1 --- /dev/null +++ b/docs/server/administration_guide/index.html @@ -0,0 +1,193 @@ + + + + + + + + Administration guide — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Administration guide

+
+

Hello and welcome to the Anweddol server administration guide.

+

The following sections will explain how to administrate and configure the Anweddol server implementation in an optimal way.

+ + + + + +
+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/installation.html b/docs/server/administration_guide/installation.html new file mode 100644 index 0000000..4bbc8ff --- /dev/null +++ b/docs/server/administration_guide/installation.html @@ -0,0 +1,299 @@ + + + + + + + + Installation — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Installation

+
+

This section covers the prerequisites needed for the system before the Anweddol server installation.

+
+

Anweddol server installation

+

Install the Anweddol server by the sources :

+
$ git clone https://github.com/the-anweddol-project/Anweddol-server.git
+$ cd Anweddol-server
+$ sudo pip install .
+
+
+

The nessessary files and users will be created during the installation.

+

NOTE : If the pip installation is launched with non-root permissions, only the anwdlserver package will be installed : the full setup will be skipped.

+
+
+

Environment setup

+
+

Libvirt

+

Libvirt is a toolkit to manage virtualization platforms that the server is using to manage container domains.

+
+

Installation

+

Libvirt must be installed via your package manager :

+

Apt :

+
$ sudo apt-get update
+$ sudo apt-get install python-libvirt
+
+
+

DNF :

+
$ sudo dnf install python-libvirt
+
+
+

Yum :

+
$ sudo yum install python-libvirt
+
+
+

NOTE : You can also install it from pip by executing :

+
$ pip install libvirt-python
+
+
+

But you need to install build dependencies manually, and this topic is not covered in this documentation.

+
+
+

Setup

+

You need to modify the /etc/libvirt/qemu.conf file to give libvirt appropriate rights.

+

Edit the /etc/libvirt/qemu.conf file :

+
$ sudo nano /etc/libvirt/qemu.conf
+
+
+

Find the user and group directives. By default, both are set to root :

+
[...] 
+Some examples of valid values are:
+#
+user = "qemu"   # A user named "qemu"
+user = "+0"     # Super user (uid=0)
+user = "100"    # A user named "100" or a user with uid=100
+#
+#user = "root"
+The group for QEMU processes run by the system instance. It can be
+specified in a similar way to user.
+#group = "root"
+[...]
+
+
+

Uncomment both lines and replace root with anweddol and the group with libvirt as shown below:

+
[...] 
+Some examples of valid values are:
+#
+user = "qemu"   # A user named "qemu"
+user = "+0"     # Super user (uid=0)
+user = "100"    # A user named "100" or a user with uid=100
+#
+user = "anweddol"
+The group for QEMU processes run by the system instance. It can be
+specified in a similar way to user.
+group = "libvirt"
+[...]
+
+
+

Then restart the libvirtd daemon :

+
$ sudo systemctl restart libvirtd.service
+
+
+

(source : ostechnix)

+
+
+
+

Networking

+

For the containers to be able to communicate with the outside, the host system must have a bridge interface linking the main network interface to the container domain’s interface.

+

It consists of a bridge interface with the main network interface set as a slave to the bridge : +the container domain’s interface will connect to it on the runtime via TAP link.

+
+

If the server is using a WI-FI interface as the main network interface, a more specific setup must be done before continuing (see the debian documentation for more).

+
+

Follow the steps below according to your system :

+
+

Debian-based systems

+

First, you need to stop the networking service :

+
$ service network stop
+
+
+

Then you can create the anwdlbr0 bridge interface, replacing MASTER_INTERFACE_NAME by your actual master interface name :

+
$ brctl addbr anwdlbr0
+$ brctl addif anwdlbr0 MASTER_INTERFACE_NAME
+
+
+

Create persistent configuration files for your interfaces :

+
$ printf "iface MASTER_INTERFACE_NAME inet manual" >> /etc/network/interfaces
+$ printf "iface anwdlbr0 inet dhcp\n\tbridge_ports MASTER_INTERFACE_NAME" >> /etc/network/interfaces
+
+
+

Enable the anwdlbr0 interface, and restart the networking service :

+
$ ifup anwdlbr0
+$ service network start
+
+
+
+
+

Redhat-based systems

+

Disable NetworkManager :

+
$ sudo chkconfig NetworkManager off
+$ sudo chkconfig network on
+$ sudo service NetworkManager stop
+$ sudo service network start
+
+
+

Define the main network interface in networks-script, replacing MASTER_INTERFACE_NAME by your actual master interface name :

+
$ sudo printf "DEVICE=MASTER_INTERFACE_NAME\nHWADDR=$(ifconfig MASTER_INTERFACE_NAME | grep -o -E ..:..:..:..:..:..)\nONBOOT=yes\nBRIDGE=anwdlbr0\nNM_CONTROLLED=no" /etc/sysconfig/networks-script/MASTER_INTERFACE_NAME
+
+
+

Create the anwdlbr0 bridge interface in networks-script :

+
$ sudo printf "DEVICE=anwdlbr0\nTYPE=Bridge\nBOOTPROTO=dhcp\nONBOOT=yes\nDELAY=0\nNM_CONTROLLED=no" /etc/sysconfig/networks-script/ifcfg-anwdlbr0
+
+
+

Add sysctl rules to enable packet forwarding :

+
$ sudo printf "net.bridge.bridge-nf-call-ip6tables = 0\nnet.bridge.bridge-nf-call-iptables = 0\nnet.bridge.bridge-nf-call-arptables = 0" >> /etc/sysctl.conf
+$ sudo sysctl -p /etc/sysctl.conf
+
+
+

Add iptables rules to accept packet forwarding from bridged interfaces :

+
$ sudo printf "-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT" /etc/sysconfig/anweddol-iptables-forward-rules
+$ sudo lokkit --custom-rules=ipv4:filter:/etc/sysconfig/anweddol-iptables-forward-rules
+
+
+

Restart the network service :

+
$ sudo service network restart
+
+
+
+
+
+

Container ISO

+

Before using the server, you need to download a specific ISO image for the container domains in order to provide a functional service.

+

See the Container ISO section to learn more.

+
+
+
+

Getting started

+

At this point, you should be able to start the server. See the Server usage section to do so.

+
+
+

Anweddol server uninstallation

+

To uninstall the Anweddol server, execute :

+
$ sudo anwdlserver-uninstall
+
+
+

The script will delete any files and everything associated with the Anweddol server.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/logging.html b/docs/server/administration_guide/logging.html new file mode 100644 index 0000000..0d69bc8 --- /dev/null +++ b/docs/server/administration_guide/logging.html @@ -0,0 +1,171 @@ + + + + + + + + Logging — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Logging

+
+

The Anweddol server CLI uses the logging python module to ensure viable logging feature.

+

Logs are stored in /var/log/anweddol/runtime.txt and are stored in this format :

+
%(asctime)s %(levelname)s : %(message)s
+
+
+

Here is a sample of logs generated during the development phase of the Anweddol server :

+
2023-06-27 17:59:25,126 WARNING : [INIT] Initializing server (running as 'anweddol') ...
+2023-06-27 17:59:25,126 INFO : [INIT] Loading instance RSA key pair ...
+2023-06-27 17:59:25,700 INFO : [INIT] Initializing server interface ...
+2023-06-27 17:59:25,711 INFO : [INIT] Binding handlers routine ...
+2023-06-27 17:59:25,711 INFO : [INIT] All done. Server is ready
+2023-06-27 17:59:25,711 INFO : Starting server ...
+2023-06-27 17:59:25,712 INFO : Server is started
+2023-06-27 17:59:42,724 INFO : (client ID 12ca17b) New client connected
+2023-06-27 17:59:42,770 INFO : (client ID 12ca17b) Received STAT request
+2023-06-27 17:59:42,778 INFO : (client ID 12ca17b) Connection closed
+2023-06-27 17:59:49,958 INFO : (client ID 12ca17b) New client connected
+2023-06-27 17:59:50,003 INFO : (client ID 12ca17b) Received CREATE request
+2023-06-27 17:59:50,003 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 was created
+2023-06-27 18:00:03,421 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 domain is started
+2023-06-27 18:00:03,443 INFO : Connected (version 2.0, client OpenSSH_8.4p1)
+2023-06-27 18:00:03,567 INFO : Authentication (password) successful!
+2023-06-27 18:00:03,567 INFO : (client ID 12ca17b) Endpoint shell opened
+2023-06-27 18:00:05,458 INFO : (client ID 12ca17b) Connection closed
+2023-06-27 18:00:19,881 INFO : (client ID 12ca17b) New client connected
+2023-06-27 18:00:19,927 INFO : (client ID 12ca17b) Received DESTROY request
+2023-06-27 18:00:20,143 INFO : (client ID 12ca17b) Container 5d730281-414d-4bd8-ae01-13ebf302fd17 was destroyed
+2023-06-27 18:00:20,145 INFO : (client ID 12ca17b) Connection closed
+
+
+

NOTE : Clients are represented by their IDs, here “12ca17b” : It is a way of programmatically identifying the client other than with his IP. It is the first 7 characters of the client’s IP SHA256.

+
+

Log rotation

+

You have the possibility to rotate logs by archiving or deleting them.

+

Archived logs will be stored in a separate folder withing zipped files with the name format :

+
archived_<DATE>.zip
+
+
+

where DATE is the rotation date. See the log_rotation section in the configuration file for more.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/server_usage.html b/docs/server/administration_guide/server_usage.html new file mode 100644 index 0000000..ee66b76 --- /dev/null +++ b/docs/server/administration_guide/server_usage.html @@ -0,0 +1,169 @@ + + + + + + + + Server usage — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Server usage

+
+
+

You need to follows the Installation section before continuing this tutorial.

+
+
+

Start the server

+

First, you need the libvirtd daemon running :

+
$ sudo systemctl start libvirtd.service
+
+
+

(optional) You may need to check if the server environment is correctly set up :

+
$ sudo anwdlserver start -c
+
+
+

If some error occured, check the Troubleshooting section to fix them.

+

Start the Anweddol server via systemd :

+
$ sudo systemctl start anweddol-server.service
+
+
+

Or via the CLI :

+
$ sudo anwdlserver start
+
+
+

(optional) add the option -d to enable direct execution (the server will run synchronously in the terminal, as the actual user)

+

NOTE : It is preferable to use systemd for server lifetime control, mixed usage between CLI and systemd can cause hazardous behaviour.

+
+
+

Stop the server

+

Stop the server via the systemd daemon :

+
$ sudo systemctl stop anweddol-server.service
+
+
+

Or via the CLI :

+
$ sudo anwdlserver stop
+
+
+

NOTE : As mentionned in the previous section, it is preferable to use systemd for server lifetime control : mixed usage between CLI and systemd can cause hazardous behaviour.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/administration_guide/troubleshooting.html b/docs/server/administration_guide/troubleshooting.html new file mode 100644 index 0000000..3827bea --- /dev/null +++ b/docs/server/administration_guide/troubleshooting.html @@ -0,0 +1,156 @@ + + + + + + + + Troubleshooting — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Troubleshooting

+
+

Here is a list of non-exhaustive potential problems that can be encountered while using the Anweddol server :

+
+

[…]Libvirt : Permission denied

+

Description : The libvirt API failed to access or create a resource due to the lack of permissions.

+

Solution :

+

See the Installation setup section to learn more.

+

If the problem persists, try to disable the SELinux enforce feature :

+
$ sudo setenforce 0
+
+
+

Do not forget to re-enable it when unused :

+
$ sudo setenforce 1
+
+
+
+
+

[…] authentication unavailable: no polkit agent available to authenticate action 'org.libvirt.unix.manage'

+

Description : The user anweddol is not on the libvirt group.

+

Solution :

+

Set the user anweddol in the libvirt group:

+
$ sudo usermod -aG libvirt anweddol
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/contribute.html b/docs/server/contribute.html new file mode 100644 index 0000000..fadf3e2 --- /dev/null +++ b/docs/server/contribute.html @@ -0,0 +1,156 @@ + + + + + + + + Contribute — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Contribute

+
+

Thank you for taking the time to contribute to the Anweddol project!

+

In this guide you will retrieve the contribution guidelines.

+
+

Getting started

+

In order to be able to bring new ideas and enhancements with ease, you can read the Technical specifications of the Anweddol server to know how it works.

+
+
+

Issues

+
+

Create an issue

+

If you encounter a problem with the software, be it a bug or a potential error in the code or the docs, search for another issue of its kind first to prevent duplicates. If you find one, you can comment on it to give more importance to the issue.

+
+
+

Solve / contribute to an issue

+

You can scroll through the issues and find one that interests you. There is no assignee for issues : Everyone is free to contribute to it.

+
+
+
+

Make changes

+
+

Pull requests

+

Any changes will result in a new pull request, which will be reviewed before merging.

+
+
+

Documentation

+

If your modification proposition involves a modification of the core features or of an already documented one, you need to provide the appropriate documentation as well.

+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/client.html b/docs/server/developer_section/api_references/core/client.html new file mode 100644 index 0000000..4732b09 --- /dev/null +++ b/docs/server/developer_section/api_references/core/client.html @@ -0,0 +1,534 @@ + + + + + + + + Client — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Client

+
+
+

Client management features

+
+
+

Package anwdlserver.core.client

+
+
+

Constants

+

Default values :

+ + + + + + + + + + + + + + + + + + + + + +

Name

Value

Description

DEFAULT_STORE_REQUEST

True

Store the request in the instance by default

DEFAULT_AUTO_EXCHANGE_KEYS

True

Exchange keys on connection by default

DEFAULT_RECEIVE_FIRST

True

Receive the public key first by default

+

Constants definition :

+ + + + + + + + + + + + + + + + + +

Name

Value

Definition

MESSAGE_OK

"1"

Message used during key exchange to transmit a state or an acknowledgement

MESSAGE_NOK

"0"

Message used during key exchange to transmit a state or an acknowledgement

+
+
+

Classes

+
+

ClientInstance

+
+

Definition

+
class ClientInstance(
+	socket: socket.socket,
+	timeout: int = None,
+	rsa_wrapper: RSAWrapper = None,
+	aes_wrapper: AESWrapper = None,
+	auto_exchange_key: bool = DEFAULT_AUTO_EXCHANGE_KEYS,
+)
+
+
+
+

Represents a connected client

+
+

Parameters :

+
    +
  • socket : The client socket descriptor. Must be a connected active client socket

  • +
  • timeout : The timeout to apply to the client

  • +
  • rsa_wrapper : The RSAWrapper instance that will be used on the client

  • +
  • aes_wrapper : The AESWrapper instance that will be used on the client

  • +
  • auto_exchange_key : Automatically exchange keys on initialization

  • +
+

NOTE : The client socket will be automatically closed on __del__ method. The parameter socket must be a valid open socket descriptor representing a client connection.

+
+
+

Methods

+
isClosed() -> bool
+
+
+
+

Check if the client is closed

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • True if the server is closed, False otherwise

  • +
+
+
getSocketDescriptor() -> socket.socket
+
+
+
+

Get the client socket descriptor

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The socket descriptor of the client

  • +
+
+
getIP() -> str
+
+
+
+

Get the client IP

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The client IP as an IPv4 string

  • +
+
+
getID() -> str
+
+
+
+

Get the client ID

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The client ID. It is the first 7 characters of the client’s IP SHA256 (see the administration guide Logging section to learn more).

  • +
+
+
getTimestamp() -> int
+
+
+
+

Get the client creation timestamp

+
+

Parameters :

+
    +
  • None

  • +
+

Return value:

+
    +
  • The creation timestamp

  • +
+
+
getStoredRequest() -> None | dict
+
+
+
+

Get the stored request

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A dictionary following the normalized Request format or None if there is none

  • +
+
+
getRSAWrapper() -> RSAWrapper
+
+
+
+

Get the client RSAWrapper instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value:

+
    +
  • The RSAWrapper instance of the client

  • +
+
+
getAESWrapper() -> AESWrapper
+
+
+
+

Get the client AESWrapper instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value:

+
    +
  • The AESWrapper instance of the client

  • +
+
+
setRSAWrapper(rsa_wrapper: RSAWrapper) -> None
+
+
+
+

Set the client RSAWrapper instance

+
+

Parameters :

+
    +
  • rsa_wrapper : The RSAWrapper instance to set

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setAESWrapper(aes_wrapper: AESWrapper) -> None
+
+
+
+

Set the client AESWrapper instance

+
+

Parameters :

+
    +
  • aes_wrapper : The AESWrapper instance to set

  • +
+

Return value :

+
    +
  • None

  • +
+
+
sendPublicRSAKey() -> None
+
+
+
+

Send the local public RSA key

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+
+
recvPublicRSAKey() -> None
+
+
+
+

Receive the client public RSA key

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
sendAESKey() -> None
+
+
+
+

Send the local AES key

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
recvAESKey() -> None
+
+
+
+

Receive the local AES key

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
exchangeKeys(receive_first: bool = DEFAULT_RECEIVE_FIRST) -> None
+
+
+
+

Exchange the local RSA and AES keys with the client

+
+

Parameters :

+
    +
  • receive_first : True to receive the public key first, False otherwise

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
sendResponse(
+	success: bool,
+	message: str,
+	data: dict = {},
+	reason: str = None
+) -> None
+
+
+
+

Send a response to the client

+
+

Parameters :

+
    +
  • success : True to announce a success, False otherwise

  • +
  • message : The message to send

  • +
  • data : The data to send. The content must be an empty dictionary or a normalized Response format dictionary.

  • +
  • reason : The reason to specify if success is set to False. The value will be appended to the message like : Refused request (reason : <specified_reason>)

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
recvRequest(store_request: bool = DEFAULT_STORE_REQUEST) -> tuple
+
+
+
+

Receive a request from the client

+
+

Parameters :

+
    +
  • store_request : Store the received request on instance or not

  • +
+

Return value :

+ +

Possible raise classes :

+
    +
  • ValueError

  • +
  • RuntimeError

  • +
+
+
closeConnection() -> None
+
+
+
+

Close the client connection

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : This method is automatically called within the __del__ method, but it is programatically better to call it naturally

+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/cryptography.html b/docs/server/developer_section/api_references/core/cryptography.html new file mode 100644 index 0000000..0ad53c8 --- /dev/null +++ b/docs/server/developer_section/api_references/core/cryptography.html @@ -0,0 +1,518 @@ + + + + + + + + Cryptography — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Cryptography

+
+
+

RSA / AES encryption features.

+
+
+

Package anwdlserver.core.crypto

+
+
+

Constants

+

Default values :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Name

Value

Description

DEFAULT_RSA_KEY_SIZE

4096

The default RSA key size

DEFAULT_AES_KEY_SIZE

256

The default AES key size

DEFAULT_PEM_FORMAT

True

Specify key in a PEM format by default

DEFAULT_GENERATE_KEY_PAIR

True

Generate key pair on initialization by default

DEFAULT_DERIVATE_PUBLIC_KEY

False

Derivate the public key out of the private key by default

+
+
+

Classes

+
+

RSAWrapper

+
+

Definition

+
class RSAWrapper(
+    key_size: int = DEFAULT_RSA_KEY_SIZE,
+    generate_key_pair: bool = DEFAULT_GENERATE_KEY_PAIR
+)
+
+
+
+

Provides RSA encryption functionality

+
+

Parameters :

+
    +
  • key_size : The RSA key size, exprimed in bits. Must be a multiple of 2

  • +
  • generate_key_pair : Generate RSA key pair on initialization or not

  • +
+
+
+

Methods

+
generateKeyPair(self) -> None
+
+
+
+

Generate the RSA key pair

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+
+
getKeySize() -> int
+
+
+
+

Get the local key size

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The key size exprimed in bytes

  • +
+
+
getPublicKey(pem_format: bool = True) -> str | bytes
+
+
+
+

Get the local public key

+
+

Parameters :

+
    +
  • pem_format : True to return the local public key in a PEM format, False to return it as a byte sequence

  • +
+

Return value :

+
    +
  • The local public key

  • +
+
+
getPrivateKey(pem_format: bool = True) -> str | bytes
+
+
+
+

Get the local private key

+
+

Parameters :

+
    +
  • pem_format : True to return the local key in a PEM format, False to return it as a byte sequence

  • +
+

Return value :

+
    +
  • The local private key

  • +
+
+
getRemotePublicKey(pem_format: bool = True) -> None | str | bytes
+
+
+
+

Get the client public key

+
+

Parameters :

+
    +
  • pem_format : True to return the client public key in a PEM format, False to return it as a byte sequence

  • +
+

Return value :

+
    +
  • The client public key, or None if there is none

  • +
+
+
setPublicKey(public_key: str | bytes, pem_format: bool = True) -> None
+
+
+
+

Set the local public key

+
+

Parameters :

+
    +
  • public_key : The public key, in PEM format if the parameter pem_format is set to True, in a byte sequence otherwise

  • +
  • pem_format : Specify the format of the key in the parameter public_key. The key must be in PEM format if set to True, in a byte sequence otherwise

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : The intern private key will be deleted in the process, you must use setPrivateKey to set the affiliated private key.

+
+
setPrivateKey(
+    private_key: str | bytes,
+    pem_format: bool = DEFAULT_PEM_FORMAT,
+    derivate_public_key: bool = DEFAULT_DERIVATE_PUBLIC_KEY
+) -> None
+
+
+
+

Set the local private key

+
+

Parameters :

+
    +
  • private_key : The private key, in PEM format if the parameter pem_format is set to True, in a byte sequence otherwise

  • +
  • pem_format : Specify the format of the key in the parameter private_key. The key must be in PEM format if set to True, in a byte sequence otherwise

  • +
  • derivate_public_key : Derivate the public key out of the private key or not

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setRemotePublicKey(
+    remote_public_key: str | bytes,
+    pem_format: bool = DEFAULT_PEM_FORMAT,
+) -> None
+
+
+
+

Set the client public key

+
+

Parameters :

+
    +
  • remote_public_key : The remote public key, in PEM format if the parameter pem_format is set to True, in a byte sequence otherwise

  • +
  • pem_format : Specify the format of the key in the parameter private_key. The key must be in PEM format if set to True, in a byte sequence otherwise

  • +
+

Return value :

+
    +
  • None

  • +
+
+
encryptData(
+    data: str | bytes,
+    encode: bool = True,
+    use_local_public_key: bool = False
+) -> bytes
+
+
+
+

Encrypt data

+
+

Parameters :

+
    +
  • data : The data to encrypt. It can be a string or a byte sequence

  • +
  • encode : Specify if the content in parameter data must be encoded before being processed (mus be set to True only if the parameter data is a string)

  • +
  • use_local_public_key : Specify if the local public key must be used for encryption instead of the remote public key

  • +
+

Return value :

+
    +
  • The encrypted data content as a byte sequence

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : If the parameter use_local_public_key is set to False, the remote public key must be set (RuntimeError will be raised otherwise)

+
+
decryptData(cipher: bytes, decode: bool = True) -> str | bytes
+
+
+
+

Decrypt data

+
+

Parameters :

+
    +
  • cipher : The encrypted cipher text as a byte sequence

  • +
  • decode : Specify if the decrypted data should be decoded before being returned

  • +
+

Return value

+
    +
  • The decrypted data content as a string or a byte sequence according to the value of decode

  • +
+
+
signData(data: str | bytes, encode: bool = True) -> bytes
+
+
+
+

Sign a block of data

+
+

Parameters :

+
    +
  • data : The data to sign

  • +
  • encode : Specify if the content in parameter data must be encoded before being processed (set to True only if the parameter data is a string)

  • +
+

Peturn value :

+
    +
  • The signed data, as a byte sequence

  • +
+
+
verifyDataSignature(
+    signature: bytes,
+    data: str | bytes,
+    encode: bool = True
+) -> bool
+
+
+
+

Verify the authenticity of a signed block of data

+
+

Parameters :

+
    +
  • signature : The signed data, as a byte sequence

  • +
  • data : The data to verify

  • +
  • encode : Specify if the content in parameter data must be encoded before being processed (set to True only if the parameter data is a string)

  • +
+

Peturn value :

+
    +
  • A boolean value : True if the data and its signature are authentic, False otherwise

  • +
+
+
+
+

AESWrapper

+
+

Definition

+
class AESWrapper(key_size: int = DEFAULT_AES_KEY_SIZE)
+
+
+
+

Provides AES encryption functionality

+
+

Parameters :

+
    +
  • key_size : The AES key size, exprimed in bytes. Must be 16, 24 or 32 bytes long

  • +
+
+
+

Methods

+
getKeySize() -> int
+
+
+
+

Get the key size

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The AES key size exprimed in bytes, as an integer

  • +
+
+
getKey() -> bytes
+
+
+
+

Get the AES key

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The AES key as a byte sequence

  • +
+
+
getIv() -> bytes
+
+
+
+

Get the Initialisation Vector (Iv)

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The IV as a 16 byte sequence

  • +
+
+
setKey(key: bytes, iv: bytes = None) -> None
+
+
+
+

Set the AES key

+
+

Parameters :

+
    +
  • key : The AES key, as a byte sequence. Must be 16, 24 or 32 bytes long

  • +
  • iv : The Initialisation Vector. Must be 16 bytes long

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : If the parameter iv is not set, it will be automatically generated

+
+
encryptData(data: str | bytes, encode: bool = True) -> bytes
+
+
+
+

Encrypt data

+
+

Parameters :

+
    +
  • data : The data to encrypt. It can be a string or a byte sequence

  • +
  • encode : Specify if the content in parameter data must be encoded before being processed (set to True only if the parameter data is a string)

  • +
+

Return value :

+
    +
  • The data as a byte sequence

  • +
+
+
decryptData(self, cipher: bytes, decode: bool = True) -> str | bytes
+
+
+
+

Decrypt data

+
+

Parameters:

+
    +
  • cipher: The encrypted cipher text as a byte sequence

  • +
  • decode: Specify if the decrypted data should be decoded before being returned

  • +
+

Return value

+
    +
  • The decrypted data content as a string or a byte sequence according to the value of decode

  • +
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/database.html b/docs/server/developer_section/api_references/core/database.html new file mode 100644 index 0000000..219a0bb --- /dev/null +++ b/docs/server/developer_section/api_references/core/database.html @@ -0,0 +1,383 @@ + + + + + + + + Database — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Database

+
+
+

Database features with SQLite memory database

+
+
+

Package anwdlserver.core.database

+
+
+

Constants

+

None

+
+
+

Classes

+
+

DatabaseInterface

+
+

Definition

+
class DatabaseInterface()
+
+
+
+

Provides an SQLAlchemy memory database instance

+
+

Parameters :

+
    +
  • None

  • +
+

NOTE : The database and its engine will be automatically closed on __del__ method. Also, queries implying modifications on the database are automatically committed, and rollbacks are called if an error occured.

+
+
+

Methods

+
getEngine() -> sqlalchemy.engine.Engine
+
+
+
+

Get the SQLAlchemy sqlalchemy.engine.Engine object instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The sqlalchemy.engine.Engine object of the instance

  • +
+
+
getEngineConnection() -> sqlalchemy.engine.Connection
+
+
+
+

Get the SQLAlchemy sqlalchemy.engine.Connection object instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The sqlalchemy.engine.Connection object of the instance

  • +
+
+
getRuntimeTableObject() -> sqlalchemy.schema.Table
+
+
+
+

Get the SQLAlchemy sqlalchemy.schema.Table object instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The sqlalchemy.schema.Table object of the instance

  • +
+
+
getEntryID(container_uuid: str, client_token: str) -> None | int
+
+
+
+

Get the credentials pair entry ID[1]

+
+

Parameters :

+ +

Return value :

+
    +
  • The credentials entry ID[1] if exists, None otherwise

  • +
+

NOTE : This method must be used for client credentials verification.

+
+
getContainerUUIDEntryID(container_uuid: str) -> None | int
+
+
+
+

Get the entry ID[1] of a specific container UUID

+
+

Parameters :

+ +

Return value :

+
    +
  • The container UUID entry ID[1] if exists, None otherwise

  • +
+

NOTE : Only the ContainerUUID column value is concerned by this method

+
+
getEntry(entry_id: int) -> tuple
+
+
+
+

Get an entry content

+
+

Parameters :

+
    +
  • entry_id : The entry ID[1] to get the credentials from

  • +
+

Return value :

+
    +
  • A tuple representing the entry content :

  • +
+
(
+	entry_id,
+	creation_timestamp,
+	container_uuid,
+	client_token
+)
+
+
+
    +
  • entry_id : The entry ID[1]

  • +
  • creation_timestamp : The entry creation timestamp

  • +
  • container_uuid : The hashed container UUID

  • +
  • client_token : The hashed client token

  • +
+

NOTE : The container_uuid and the client_token values are hashed with SHA256 as described in the Technical specifications.

+
+
addEntry(container_uuid: str) -> tuple
+
+
+
+

Add an entry

+
+

Parameters :

+ +

Return value :

+
    +
  • A tuple representing the infomations of the created entry :

  • +
+
(
+	entry_id,
+	creation_timestamp,
+	client_token
+)
+
+
+
    +
  • entry_id : The new entry ID[1]

  • +
  • creation_timestamp : The entry creation timestamp

  • +
  • client_token : The [client token]https://anweddol-server.readthedocs.io/en/latest/technical_specifications/core/client_authentication.html#session-credentials), in plain text

  • +
+

Possible raise classes :

+
    +
  • LookupError

  • +
+

NOTE : If the container_uuid is already specified on the database, LookupError is raised. Since the client tokens are hashed in the database (see the technical specifications Database section to learn more), there’s no way to see them again in plain text : Store this clear created token somewhere safe in order to use it for further operations.

+
+
listEntries() -> list
+
+
+
+

List entries

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A list of tuples representing the entries partial informations :

  • +
+
(
+	entry_id,
+	creation_timestamp
+)
+
+
+
    +
  • entry_id : The created entry ID[1]

  • +
  • creation_timestamp : The entry creation timestamp

  • +
+
+
updateEntry(entry_id: int, container_uuid: str, client_token: str)
+
+
+
+

Update an entry

+
+

Parameters :

+ +

Return value :

+
    +
  • None

  • +
+
+
deleteEntry(entry_id: str) -> None
+
+
+
+

Delete an entry

+
+

Parameters :

+
    +
  • entry_id : The entry ID[1] to delete

  • +
+

Return value :

+
    +
  • None

  • +
+
+
closeDatabase() -> None
+
+
+
+

Close the database instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : This method is automatically called within the __del__ method, but it is programatically better to call it naturally

+
+ +
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/sanitization.html b/docs/server/developer_section/api_references/core/sanitization.html new file mode 100644 index 0000000..9abc259 --- /dev/null +++ b/docs/server/developer_section/api_references/core/sanitization.html @@ -0,0 +1,218 @@ + + + + + + + + Sanitization — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Sanitization

+
+
+

Normalized request / response values and formats

+
+
+

Package anwdlserver.core.sanitize

+
+
+

Constants

+

None

+
+
+

Functions

+
verifyRequestContent(request_dict: dict) -> tuple
+
+
+
+

Check if a request dictionary is a valid normalized Request format

+
+

Parameters :

+
    +
  • request_dict : The request dictionary

  • +
+

Return value :

+
    +
  • A tuple representing the verification results :

  • +
+
(
+	True,
+	sanitized_request_dictionary
+)
+
+
+

if the request_dict is a valid normalized Request format,

+
(
+	False,
+	errors_dictionary
+)
+
+
+

otherwise.

+
    +
  • sanitized_request_dictionary : The sanitized request as a normalized Request format dictionary.

  • +
  • errors_dictionary : A dictionary depicting the errors detected in request_dict according to the Cerberus error format.

  • +
+

NOTE : The function verifyRequestContent does not use strict verification. It only checks if the required keys and values exist and are correct, but it is open to unknown keys or structures for the developer to be able to implement its own mechanisms (see the technical specifications Sanitization section to learn more).

+
+
makeResponse(
+    success: bool,
+    message: str,
+    data: dict = {},
+    reason: str = None
+) -> tuple:
+
+
+
+

Make a normalized response dictionary

+
+

Parameters :

+
    +
  • success : True to announce a success, False otherwise

  • +
  • message : The message to send

  • +
  • data : The data to send. The content must be an empty dict or a normalized Response format.

  • +
  • reason : The reason to specify if success is set to False. The value will be appended to the message like : Refused request (reason : <specified_reason>)

  • +
+

Return value :

+ +
(
+	True,
+	response_dictionary
+)
+
+
+

if the operation succeeded,

+
(
+	False,
+	errors_dictionary
+)
+
+
+

otherwise.

+
    +
  • response_dictionary : The response dictionary as a normalized Response format.

  • +
  • errors_dictionary : A dictionary depicting the errors detected in parameters according to the Cerberus error format.

  • +
+

NOTE : The sendResponse method from ClientInstance wraps this function in its process. Like verifyRequestContent, the method only checks if the required keys and values exist and are correct, but it is open to unknown keys or structures for the developer to be able to implement its own mechanisms.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/server.html b/docs/server/developer_section/api_references/core/server.html new file mode 100644 index 0000000..484047b --- /dev/null +++ b/docs/server/developer_section/api_references/core/server.html @@ -0,0 +1,634 @@ + + + + + + + + Server — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Server

+
+
+

Server management features

+
+
+

Package anwdlserver.core.server

+
+
+

Constants

+

Default values :

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Name

Value

Description

DEFAULT_SERVER_BIND_ADDRESS

""

The default server bind address

DEFAULT_SERVER_LISTEN_PORT

6150

The default server listen port

DEFAULT_CLIENT_TIMEOUT

10

The default timeout applied on a new client

DEFAULT_ASYCHRONOUS

False

Start the server synchronously by default

+

Constants definition :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Name

Value

Description

REQUEST_VERB_CREATE

"CREATE"

The verb indicating the intent to create a new container

REQUEST_VERB_DESTROY

"DESTROY"

The verb indicating the intent to destroy a container

REQUEST_VERB_STAT

"STAT"

The verb indicating the intent to get the server runtime statistics

RESPONSE_MSG_OK

"OK"

Message specifying that the request was correctly handled by the server

RESPONSE_MSG_BAD_AUTH

"Bad authentication"

Message specifying that invalid credentials were given to the server

RESPONSE_MSG_BAD_REQ

"Bad request"

Message specifying that the received request is malformed

RESPONSE_MSG_REFUSED_REQ

"Refused request"

Message specifying that the received request was refused

RESPONSE_MSG_UNAVAILABLE

"Unavailable"

Message specifying that the server is currently unavailable

RESPONSE_MSG_INTERNAL_ERROR

"Internal error"

Message specifying that the server experienced an internal error

EVENT_CLIENT

1

Identifies the on_client callback method

EVENT_CLIENT_CLOSED

2

Identifies the on_client_closed callback method

EVENT_CONNECTION

3

Identifies the on_connection callback method

EVENT_CREATED_CONTAINER

4

Identifies the on_created_container callback method

EVENT_CREATED_ENDPOINT_SHELL

5

Identifies the on_created_endpoint_shell callback method

EVENT_DESTROYED_CONTAINER

6

Identifies the on_destroyed_container callback method

EVENT_MALFORMED_REQUEST

7

Identifies the on_malformed_request callback method

EVENT_REQUEST

8

Identifies the on_request callback method

EVENT_RUNTIME_ERROR

9

Identifies the on_runtime_error callback method

EVENT_STARTED

10

Identifies the on_started callback method

EVENT_STOPPED

11

Identifies the on_stopped callback method

EVENT_UNKNOWN_VERB

12

Identifies the on_unknown_verb callback method

+
+
+

Classes

+
+

ServerInterface

+
+

Definition

+
class ServerInterface(
+	container_iso_path: str = None,
+	bind_address: str = DEFAULT_SERVER_BIND_ADDRESS,
+	listen_port: int = DEFAULT_SERVER_LISTEN_PORT,
+	client_timeout: int = DEFAULT_CLIENT_TIMEOUT,
+	runtime_virtualization_interface: VirtualizationInterface = None,
+	runtime_database_interface: DatabaseInterface = None,
+	runtime_rsa_wrapper: RSAWrapper = None
+)
+
+
+
+

Main server utility class

+
+

Parameters :

+
    +
  • container_iso_path : The container ISO path that will be used for containers

  • +
  • bind_address : The bind address that the server will be using

  • +
  • listen_port : The listen port that the server will be using

  • +
  • client_timeout : The timeout that will be applied to clients

  • +
  • runtime_virtualization_interface : The VirtualizationInterface instance that will be used by the server

  • +
  • runtime_database_interface : The DatabaseInterface instance that will be used by the server

  • +
  • runtime_rsa_wrapper : The RSAWrapper instance that will be used by the server

  • +
+

NOTE : The container ISO path must point to a valid ISO image. The server is automatically stopped when the __del__ method is called.

+
+
+

Methods

+
getRuntimeDatabaseInterface() -> DatabaseInterface
+
+
+
+

Returns the runtime DatabaseInterface instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The DatabaseInterface instance used by the server

  • +
+
+
getRuntimeVirtualizationInterface() -> VirtualizationInterface
+
+
+
+

Returns the runtime VirtualizationInterface instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The VirtualizationInterface instance used by the server

  • +
+
+
getRuntimeRSAWrapper() -> RSAWrapper
+
+
+
+

Returns the runtime RSAWrapper instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The RSAWrapper instance used by the server

  • +
+
+
getRuntimeStatistics() -> tuple
+
+
+
+

Returns the server’s actual runtime statistics

+
+

Parameters :

+
    +
  • None

  • +
+

Return value:

+
    +
  • A tuple containing the server runtime statistics :

  • +
+
(
+	is_running,
+	recorded_runtime_errors_amount,
+	uptime,
+	available_containers_amount
+)
+
+
+
    +
  • is_running : Boolean value set to True if the server is currently running, False otherwise

  • +
  • recorded_runtime_errors_amount : The amount of errors recorded during the runtime

  • +
  • uptime : The server uptime, exprimed in seconds

  • +
  • available_containers_amount : The available containers amount

  • +
+
+
setRequestHandler(verb: str, routine: callable) -> None
+
+
+
+

Set a request handler

+
+

Parameters :

+
    +
  • verb : The request verb to handle. It can be a custom one or a normalized one :

    +
      +
    • REQUEST_VERB_CREATE

    • +
    • REQUEST_VERB_STAT

    • +
    • REQUEST_VERB_DESTROY

    • +
    +
  • +
  • routine : A callable object that will be called when a received request verb is equal to verb value

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setEventHandler(event: int, routine: callable) -> None
+
+
+
+

Set an event handler

+
+

Parameters :

+
    +
  • event : The event to handle. It can be :

    +
      +
    • EVENT_MALFORMED_REQUEST

    • +
    • EVENT_UNKNOWN_VERB

    • +
    • EVENT_RUNTIME_ERROR

    • +
    • EVENT_REQUEST

    • +
    • EVENT_CONNECTION

    • +
    • EVENT_CREATED_CONTAINER

    • +
    • EVENT_CREATED_ENDPOINT_SHELL

    • +
    • EVENT_CLIENT

    • +
    • EVENT_STARTED

    • +
    • EVENT_STOPPED

    • +
    • EVENT_CLIENT_CLOSED

    • +
    • EVENT_DESTROYED_CONTAINER

    • +
    +
  • +
  • routine : A callable object that will be called when the event event is triggered.

  • +
+

NOTE : This is the alternative of callback event decorators. Refer to the Callback events section below to know their roles and effects.

+
+
startServer(asynchronous: bool = DEFAULT_ASYCHRONOUS) -> None
+
+
+
+

Start the server

+
+

Parameters :

+
    +
  • asynchronous : Start the server asynchronously. Default is False, executing it synchronously

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : The execution is blocked when set to synchronous mode (by default)

+
+
stopServer() -> None
+
+
+
+

Stop the server

+
+

Parameters :

+
    +
  • None

  • +
+

Return value:

+
    +
  • None

  • +
+

NOTE : This method is automatically called within the __del__ method, but it is programatically better to call it naturally

+
+
restartServer(asynchronous: bool = DEFAULT_ASYCHRONOUS) -> None
+
+
+
+

Restart the server

+
+

Parameters :

+
    +
  • asynchronous : Start the server asynchronously. Default is False, executing it synchronously

  • +
+

Return value :

+
    +
  • None

  • +
+
+
+

Callback events

+

Callback events are decorators used to bind a routine or a callable object to an intern event.

+
# The decorator depicting the event. Here EVENT_CREATED_CONTAINER
+@ServerInterface.on_created_container
+def routine(**kwargs):
+	# The routine that will be executed when the event will be triggered
+	...
+
+
+

Arguments provided to routines are different depending of the callback used. +That’s why it is recommended to use **kwargs and to refer to the documentation to know the names of the passed parameters.

+

NOTE : When a client instance or raw socket is closed during a callback routine, the main server process will detect it, automatically terminating the session as a result. It is the same for container shell instances.

+

To have a proper example on how to use these decorators, see the Basic server on the examples section.

+
@ServerInterface.on_client
+
+
+
+

Triggered when a new client is connected and the keys are exchanged

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the client

  • +
+

Affiliated constant : EVENT_CLIENT

+
+
@ServerInterface.on_client_closed
+
+
+
+

Triggered when a connected client was closed

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance Callback events representing the client

  • +
+

Affiliated constant : EVENT_CLIENT_CLOSED

+
+
@ServerInterface.on_connection
+
+
+
+

Triggered when a new client is connected and the keys have not been exchanged yet

+
+

Provided parameters :

+
    +
  • client_socket : The raw client socket instance

  • +
+

Affiliated constant : EVENT_CONNECTION

+
+
@ServerInterface.on_created_container
+
+
+
+

Triggered when a new container instance is created

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
  • container_instance : The ContainerInstance instance of the created container

  • +
+

Affiliated constant : EVENT_CREATED_CONTAINER

+
+
@ServerInterface.on_created_endpoint_shell
+
+
+
+

Triggered when an endpoint shell was opened on a container

+
+

Provided parameters :

+
    +
  • endpoint_shell_instance : The EndpointShellInstance on the container

  • +
  • container_instance : The ContainerInstance instance representing the affiliated container

  • +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
+

Affiliated constant : EVENT_CREATED_ENDPOINT_SHELL

+
+
@ServerInterface.on_destroyed_container
+
+
+
+

Triggered when a container was destroyed

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
  • container_instance : The ContainerInstance instance representing the affiliated container

  • +
+

Affiliated constant : EVENT_DESTROYED_CONTAINER

+
+
@ServerInterface.on_malformed_request
+
+
+
+

Triggered when the server has received a malformed request

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
  • cerberus_error_dict : The cerberus error dict depicting the error detected on the request

  • +
+

Affiliated constant : EVENT_MALFORMED_REQUEST

+
+
@ServerInterface.on_request
+
+
+
+

Triggered when the server receives a request

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
+

Affiliated constant : EVENT_REQUEST

+
+
@ServerInterface.on_runtime_error
+
+
+
+

Triggered when an exception was raised during the server process

+
+

Provided parameters :

+
    +
  • name : The name of the thread where the error occured

  • +
  • ex_class : The exception class

  • +
  • traceback : The full traceback of the exception as a string

  • +
  • (optional) client_instance : The ClientInstance instance representing the affiliated client

  • +
+

Affiliated constant : EVENT_RUNTIME_ERROR

+
+
@ServerInterface.on_started
+
+
+
+

Triggered when the server is started and ready to operate

+
+

Provided parameters :

+
    +
  • None

  • +
+

Affiliated constant : EVENT_STARTED

+
+
@ServerInterface.on_stopped
+
+
+
+

Triggered when the server was stopped

+
+

Provided parameters :

+
    +
  • None

  • +
+

Affiliated constant : EVENT_STOPPED

+
+
@ServerInterface.on_unknown_verb
+
+
+
+

Triggered when the server has received an unknown or unsupported verb

+
+

Provided parameters :

+
    +
  • client_instance : The ClientInstance instance representing the affiliated client

  • +
+

Affiliated constant : EVENT_UNKNOWN_VERB

+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/utilities.html b/docs/server/developer_section/api_references/core/utilities.html new file mode 100644 index 0000000..ff9197e --- /dev/null +++ b/docs/server/developer_section/api_references/core/utilities.html @@ -0,0 +1,202 @@ + + + + + + + + Utilities — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Utilities

+
+
+

Miscellaneous features

+
+
+

Package anwdlserver.core.util

+
+
+

Constants

+

None

+
+
+

Functions

+
isPortBindable(port: int) -> bool
+
+
+
+

Check if a port is bindable

+
+

Parameters :

+
    +
  • port : The port to check. It must be an integer between 1 and 65535

  • +
+

Return value :

+
    +
  • True if the port is bindable, False otherwise

  • +
+
+
isSocketClosed(socket_descriptor: socket.socket) -> bool
+
+
+
+

Check if a socket descriptor is closed

+
+

Parameters :

+
    +
  • socket_descriptor : The socket descriptor to check

  • +
+

Return value :

+
    +
  • True if the socket descriptor is closed, False otherwise

  • +
+
+
isValidIP(ip: str) -> bool
+
+
+
+

Check if the IP is a valid IPv4 format

+
+

Parameters :

+
    +
  • ip : The IP to check as a string

  • +
+

Return value :

+
    +
  • True if the IP is valid , False otherwise

  • +
+
+
isInterfaceExists(interface_name: str) -> bool
+
+
+
+

Check if the network interface exists on the system

+
+

Parameters :

+
    +
  • interface_name : The interface name to check as a string

  • +
+

Return value :

+
    +
  • True if the interface exists , False otherwise

  • +
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/core/virtualization.html b/docs/server/developer_section/api_references/core/virtualization.html new file mode 100644 index 0000000..c3f0352 --- /dev/null +++ b/docs/server/developer_section/api_references/core/virtualization.html @@ -0,0 +1,964 @@ + + + + + + + + Virtualization — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Virtualization

+
+
+

Virtualization management and utilities, using libvirt API

+
+
+

Package anwdlserver.core.virtualization

+
+
+

Constants

+

Default parameters :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Name

Value

Description

DEFAULT_LIBVIRT_DRIVER_URI

"qemu:///system"

The default server bind address

DEFAULT_CONTAINER_ENDPOINT_USERNAME

"endpoint"

The default server listen port

DEFAULT_CONTAINER_ENDPOINT_PASSWORD

"endpoint"

The default timeout applied on a new client

DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT

22

Start the server asynchronously by default

DEFAULT_BRIDGE_INTERFACE_NAME

"anwdlbr0"

The default bridge interface name that container domains will use

DEFAULT_NAT_INTERFACE_NAME

"virbr0"

The default NAT interface name that container domains will use

DEFAULT_CONTAINER_MAX_TRYOUT

20

The maximum attemps to check the network availability of a container domain before error

DEFAULT_MAX_ALLOWED_CONTAINERS

6

The maximum allowed amount of active containers on an instance

DEFAULT_CONTAINER_MEMORY

2048

The memory, in Mb, allocated to container domains

DEFAULT_CONTAINER_VCPUS

2

The amount of VCPUs allocated to container domains

DEFAULT_CONTAINER_PORT_RANGE

range(10000, 15000)

The port range that will be assigned to container domains SSH servers

DEFAULT_CONTAINER_WAIT_AVAILABLE

True

Wait for the network to be available on a container domain before continuing by default

DEFAULT_CONTAINER_DESTROY_DOMAIN

True

Destroy the domain rather than shutting it down by default

DEFAULT_STORE_CONTAINER

True

Store the container instance on the virtualization interface or not by default

+
+
+

Classes

+
+

EndpointShellInstance

+
+

Definition

+
class EndpointShellInstance(
+	ssh_client: paramiko.client.SSHClient
+)
+
+
+
+

Represents a opened SSH shell to a container endpoint

+
+

Parameters :

+ +

NOTE : The endpoint SSH shell will be automatically closed on __del__ method.

+
+
+

Methods

+
isClosed() -> bool
+
+
+
+

Check if the connection is closed

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A boolean value. True if the connection is closed, False otherwise

  • +
+
+
getSSHClient() -> paramiko.client.SSHClient
+
+
+
+

Get the paramiko.client.SSHClient connection object

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+ +
+
getStoredContainerSSHCredentials() -> tuple
+
+
+
+

Get the stored container SSH credentials

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A tuple representing the stored container SSH credentials :

  • +
+
(
+	username,
+	password,
+	listen_port
+)
+
+
+
    +
  • username : The SSH username

  • +
  • password : The SSH password

  • +
  • listen_port : The SSH server listen port

  • +
+

NOTE : Container SSH credentials are stored on the instance within the setContainerSSHCredentials method. For these credentials to be sent to the client in the server process, you need to call this method if you want to set specific credentials.

+
+
executeCommand(command: str) -> tuple
+
+
+
+

Execute a command on the remote container

+
+

Parameters :

+
    +
  • command : The command to execute

  • +
+

Return value :

+
    +
  • A tuple representing the stdout and the stderr of the command output :

  • +
+
(
+	stdout,
+	stderr
+)
+
+
+
    +
  • stdout : The standard output stream of the result

  • +
  • stderr : The standard error stream of the result

  • +
+
+
generateContainerSSHCredentials(
+	port_range: range = DEFAULT_CONTAINER_PORT_RANGE,
+) -> tuple
+
+
+
+

Generate new container SSH credentials

+
+

Parameters :

+
    +
  • port_range : The port range which a random new listen port is chosen

  • +
+

Return value :

+
    +
  • A tuple representing the generated container SSH credentials :

  • +
+
(
+	username,
+	password,
+	listen_port
+)
+
+
+
    +
  • username : The SSH username

  • +
  • password : The SSH password

  • +
  • listen_port : The SSH server listen port

  • +
+
+
setContainerSSHCredentials(
+	username: str,
+    password: str,
+    listen_port: int,
+) -> None
+
+
+
+

Set the container SSH credentials on the remote container

+
+

Parameters :

+
    +
  • username : The SSH username to set

  • +
  • password : The SSH password to set

  • +
  • listen_port : The SSH server listen port to set

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : This method uses the anweddol_container_setup.sh script on the container to set the SSH credentials on ot, thus calling the closeShell method after completion (see the technical specifications Virtualization section) to learn more).

+
+
closeShell() -> None
+
+
+
+

Close the SSH connection

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : This method is automatically called within the __del__ method and the setContainerSSHCredentials method when called, but it is programatically better to call it naturally

+
+
+
+

ContainerInstance

+
+

Definition

+
class ContainerInstance(
+	uuid: str,
+	iso_path: str,
+	memory: int = DEFAULT_CONTAINER_MEMORY,
+	vcpus: int = DEFAULT_CONTAINER_VCPUS,
+	nat_interface_name: str = DEFAULT_NAT_INTERFACE_NAME,
+	bridge_interface_name: str = DEFAULT_BRIDGE_INTERFACE_NAME,
+	endpoint_username: str = DEFAULT_CONTAINER_ENDPOINT_USERNAME,
+	endpoint_password: str = DEFAULT_CONTAINER_ENDPOINT_PASSWORD,
+	endpoint_listen_port: int = DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT
+)
+
+
+
+

Represents a container instance

+
+

Parameters :

+
    +
  • uuid : The new container UUID

  • +
  • iso_path : The ISO path that will be used for the container domain

  • +
  • memory : The memory amount to allocate on the container, exprimed in Mb

  • +
  • vcpus : The VCPUs amount to allocate on the container

  • +
  • nat_interface_name : The NAT interface name that will be used by the container

  • +
  • bridge_interface_name : The bridge interface name that will be used by the container

  • +
  • endpoint_username : The endpoint username that will be used for container administration

  • +
  • endpoint_password : The endpoint password that will be used for container administration

  • +
  • endpoint_listen_port : The endpoint listen port that will be used for container administration

  • +
+
+
+

Methods

+
isDomainRunning() -> bool
+
+
+
+

Check if the container domain is running

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A boolean value. True if the domain is running, False otherwise

  • +
+
+
getNATInterfaceName() -> str
+
+
+
+

Get the NAT interface name of the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The NAT interface name of the instance

  • +
+
+
getBridgeInterfaceName() -> str
+
+
+
+

Get the bridge interface name of the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The bridge interface name of the instance

  • +
+
+
getDomainDescriptor() -> None | libvirt.virDomain
+
+
+
+

Get the libvirt.virDomain object of the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The libvirt.virDomain object of the instance, or None is unavailable

  • +
+
+
getUUID() -> str
+
+
+
+

Get the instance container UUID

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+ +
+
getISOPath() -> str
+
+
+
+

Get the instance ISO path

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The instance ISO path on local storage

  • +
+
+
getMAC() -> str
+
+
+
+

Get the container MAC address

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The instance container MAC address

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : The container domain must be started and ready in order to get its MAC address.

+
+
getIP() -> None | str
+
+
+
+

Get the container IP address

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The instance container IP address, or None if the domain is not started or not ready yet

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : The container domain must be started and ready in order to get its IP, since the method will fetch it from the dnsmasq interface status file located in /var/lib/libvirt/dnsmasq/ with its MAC address.

+
+
getMemory() -> int
+
+
+
+

Get the allocated container memory amount

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The memory amount allocated to the container

  • +
+
+
getVCPUs() -> int
+
+
+
+

Get the allocated container Virtual CPUs amount

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The VCPUs amount allocated to the container

  • +
+
+
setDomainDescriptor(domain_descriptor: libvirt.virDomain) -> None
+
+
+
+

Set the libvirt.virDomain object of the instance

+
+

Parameters :

+
    +
  • domain_descriptor : The libvirt.virDomain object to set on the instance

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setISOPath(iso_path: str) -> None
+
+
+
+

Set the container ISO path of the instance

+
+

Parameters :

+
    +
  • iso_path : The ISO path to set on the instance

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setMemory(memory: int) -> None
+
+
+
+

Set the memory amount to allocate on the container

+
+

Parameters :

+
    +
  • memory : The memory to allocate on the container, exprimed in Mb

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : The parameter memory must be a value greater than 0

+
+
setVCPUs(vcpus: int) -> None
+
+
+
+

Set the Virtual CPUs amount to allocate on the container

+
+

Parameters :

+
    +
  • vcpus : The amount of VCPUs to allocate on the container

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : The parameter vcpus must be a value greater than 0

+
+
setNATInterfaceName(nat_interface_name: str) -> None
+
+
+
+

Set the NAT interface name of the container

+
+

Parameters :

+
    +
  • nat_interface_name : The NAT interface name that will be used on the container domain

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setBridgeInterfaceName(bridge_interface_name: str) -> None
+
+
+
+

Set the bridge interface name of the container

+
+

Parameters :

+
    +
  • bridge_interface_name : The bridge interface name that will be used on the container domain

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setEndpointSSHAuthenticationCredentials(
+	endpoint_username: str = DEFAULT_CONTAINER_ENDPOINT_USERNAME, 
+    endpoint_password: str = DEFAULT_CONTAINER_ENDPOINT_PASSWORD, 
+    endpoint_listen_port: str = DEFAULT_CONTAINER_ENDPOINT_LISTEN_PORT
+) -> None
+
+
+
+

Set the container endpoint SSH credentials for SSH authentication

+
+

Parameters :

+
    +
  • endpoint_username : The endpoint username to set

  • +
  • endpoint_password : The endpoint password to set

  • +
  • endpoint_listen_port : The endpoint listen port to set

  • +
+

Return value :

+
    +
  • None

  • +
+
+
makeISOChecksum() -> str
+
+
+
+

Get the SHA256 digest of the instance container ISO

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The SHA256 digest of the container ISO

  • +
+
+
createEndpointShell() -> EndpointShellInstance
+
+
+
+

Create a EndpointShellInstance object on the running container

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The EndpointShellInstance object representing the SSH shell on the container domain endpoint

  • +
+
+
startDomain(
+	wait_available: bool = DEFAULT_CONTAINER_WAIT_AVAILABLE,
+    wait_max_tryout: int = DEFAULT_CONTAINER_MAX_TRYOUT,
+    driver_uri: str = DEFAULT_LIBVIRT_DRIVER_URI
+) -> None
+
+
+
+

Start the container domain

+
+

Parameters :

+
    +
  • wait_available : Wait for the network to be available on the domain or not

  • +
  • wait_max_tryout : The amount of attemps to check if the network is available on the domain before raising TimeoutError

  • +
  • driver_uri : The hypervisor driver URI to use.

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
  • TimeoutError

  • +
+

NOTE : By default, the qemu hypervisor URI qemu:///system is used. The container domain must be stopped before being started.

+
+
stopDomain(destroy: bool = DEFAULT_CONTAINER_DESTROY_DOMAIN) -> None
+
+
+
+

Stop the container domain

+
+

Parameters :

+
    +
  • destroy : Destroy the container domain rather than shutting it down or not

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : The container domain must be running before being stopped.

+
+
+
+

VirtualizationInterface

+
+

Definition

+
class VirtualizationInterface(
+	container_iso_path: str,
+    max_allowed_containers: int = DEFAULT_MAX_ALLOWED_CONTAINERS,
+)
+
+
+
+

Provides container management functionality

+
+

Parameters :

+
    +
  • container_iso_path : The container ISO path to set on created containers

  • +
  • max_allowed_containers : The maximum allowed active containers amount on the instance, must be greater than 0

  • +
+
+
+

Methods

+
getIsoPath() -> str
+
+
+
+

Get the instance ISO path

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The instance ISO path

  • +
+
+
getMaxAllowedContainersAmount() -> int
+
+
+
+

Get the maximum allowed active containers amount on the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The maximum allowed active containers amount on the instance, is greater than 0

  • +
+
+
getContainersAmount() -> int
+
+
+
+

Get the active containers amount on the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The amount of active containers on the instance

  • +
+
+
getAvailableContainersAmount() -> int
+
+
+
+

Get the amount of available containers left

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The amount of available containers left

  • +
+

NOTE : This method justs substract the maximum allowed active containers amount on the instance with the actual active containers amount.

+
+
listStoredContainers() -> list
+
+
+
+

List stored containers

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A list enumerating the stored container UUIDs

  • +
+
+
getStoredContainer(container_uuid: str) -> None | ContainerInstance
+
+
+
+

Get a stored container

+
+

Parameters :

+
    +
  • container_uuid : The container UUID to search for

  • +
+

Return value :

+
    +
  • The ContainerInstance object of the crated container if exists, None otherwise

  • +
+
+
setIsoPath(iso_path: str) -> None
+
+
+
+

Set the instance ISO path

+
+

Parameters :

+
    +
  • iso_path : The instance ISO path to set. Must point to a valid ISO image

  • +
+

Return value :

+
    +
  • None

  • +
+
+
setMaxAllowedContainers(max_allowed_containers: int) -> None
+
+
+
+

Set the maximum allowed active container amount on the instance

+
+

Parameters :

+
    +
  • max_allowed_containers : The maximum allowed active containers amount on the instance, must be greater than 0

  • +
+

Return value :

+
    +
  • None

  • +
+
+
addStoredContainer(container_instance: ContainerInstance) -> None
+
+
+
+

Add a container on the instance

+
+

Parameters :

+
    +
  • container_instance : The ContainerInstance object to add

  • +
+

Return value :

+
    +
  • None

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : If the stored container amount become equal to the max_allowed_containers value from the class initialization, a RuntimeError will be raised.

+
+
deleteStoredContainer(container_uuid: str) -> None
+
+
+
+

Delete a stored container

+
+

Parameters :

+ +

Return value :

+
    +
  • None

  • +
+
+
createContainer(store: bool = DEFAULT_STORE_CONTAINER) -> ContainerInstance
+
+
+
+

Create a container

+
+

Parameters :

+
    +
  • store : True to store the created container, False otherwise

  • +
+

Return value :

+
    +
  • The ContainerInstance object of the created container

  • +
+

Possible raise classes :

+
    +
  • RuntimeError

  • +
+

NOTE : If the stored container amount become equal to the max_allowed_containers value from the class initialization, a RuntimeError will be raised.

+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/api_references/tools/accesstk.html b/docs/server/developer_section/api_references/tools/accesstk.html new file mode 100644 index 0000000..3465fb3 --- /dev/null +++ b/docs/server/developer_section/api_references/tools/accesstk.html @@ -0,0 +1,366 @@ + + + + + + + + Access token — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Access token

+
+
+

Access token management features

+
+
+

Package anwdlserver.tools.accesstk

+
+
+

Constants

+

Default values :

+ + + + + + + + + + + + + +

Name

Value

Description

DEFAULT_DISABLE_TOKEN

False

Enable created tokens by default

+
+
+

Classes

+
+

AccessTokenManager

+
+

Definition

+
class AccessTokenManager(auth_token_db_path: str) 
+
+
+
+

Provides access token features

+
+

Parameters :

+
    +
  • auth_token_db_path : The access tokens database file path

  • +
+

NOTE : The database and its cursors will be automatically closed on __del__ method. Also, queries implying modifications on the database are automatically committed, and rollbacks are called if an error occured.

+
+
+

Methods

+
getDatabaseConnection() -> sqlite3.Connection
+
+
+
+

Get the sqlite3.Connection object of the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The sqlite3.Connection object of the instance

  • +
+
+
getCursor() -> sqlite3.Cursor:
+
+
+
+

Get the sqlite3.Cursor object of the instance

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • The sqlite3.Cursor object of the instance

  • +
+
+
getEntryID(access_token: str) -> None | int
+
+
+
+

Get the access token entry ID (similar to the ROWID in sqlite, identifies the row)

+
+

Parameters :

+
    +
  • access_token : The clear access token to search for

  • +
+

Return value :

+
    +
  • The access token entry ID if exists, None otherwise

  • +
+

NOTE : This method must be used for client access verification. If the access token entry is disabled, the method will ignore the entry and return None as a result.

+
+
getEntry(entry_id: int) -> tuple
+
+
+
+

Get an entry content

+
+

Parameters :

+
    +
  • entry_id : The entry ID to get

  • +
+

Return value :

+
    +
  • A tuple representing the entry content :

  • +
+
(
+	entry_id,
+	creation_timestamp,
+	access_token,
+	enabled
+)
+
+
+
    +
  • entry_id : The entry ID

  • +
  • creation_timestamp : The entry creation timestamp

  • +
  • access_token : The hashed access token

  • +
  • enabled : A boolean value (1 or 0) depicting if the entry is enabled or not

  • +
+

NOTE : The access_token value is hashed with SHA256 as described in the Technical specifications.

+
+
addEntry(disable: bool = DEFAULT_DISABLE_TOKEN) -> tuple
+
+
+
+

Create an entry

+
+

Parameters :

+
    +
  • disable : True to disable the token entry by default, False otherwise

  • +
+

Return value :

+
    +
  • A tuple representing the created token entry informations :

  • +
+
(
+	entry_id, 
+	auth_token
+)
+
+
+
    +
  • entry_id : The created entry ID

  • +
  • auth_token : The created access token, in plain text

  • +
+

NOTE : Since tokens are hashed with SHA256 in the database (see the technical specifications Access token section to learn more), there’s no way to see them again in plain text : Store this clear created token somewhere safe in order to use it for further operations.

+
+
listEntries() -> list
+
+
+
+

List entries

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • A list of tuples representing every entries on the database :

  • +
+
(
+	entry_id,
+	creation_timestamp,
+	enabled
+)
+
+
+
    +
  • entry_id : The entry ID

  • +
  • creation_timestamp : The entry creation timestamp

  • +
  • enabled : True if the entry is enabled, False otherwise

  • +
+
+
enableEntry(entry_id: int) -> None
+
+
+
+

Enable the usage of an entry

+
+

Parameters :

+
    +
  • entry_id : The entry ID to enable

  • +
+

Return value :

+
    +
  • None

  • +
+
+
disableEntry(entry_id: int) -> None
+
+
+
+

Disable the usage of an entry

+
+

Parameters :

+
    +
  • entry_id : The entry ID to disable

  • +
+

Return value :

+
    +
  • None

  • +
+
+
deleteEntry(entry_id: int) -> None
+
+
+
+

Delete an entry

+
+

Parameters :

+
    +
  • entry_id : The entry ID to delete on the database

  • +
+

Return value :

+
    +
  • None

  • +
+
+
closeDatabase() -> None
+
+
+
+

Close the database

+
+

Parameters :

+
    +
  • None

  • +
+

Return value :

+
    +
  • None

  • +
+

NOTE : This method is automatically called within the __del__ method, but it is programatically better to call it naturally

+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/cli/json_output.html b/docs/server/developer_section/cli/json_output.html new file mode 100644 index 0000000..3be8d0a --- /dev/null +++ b/docs/server/developer_section/cli/json_output.html @@ -0,0 +1,333 @@ + + + + + + + + CLI JSON output — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

CLI JSON output

+
+

In order to communicate with other programs in an easy way, the Anweddol server CLI provides a JSON output feature that allows [inter-program communication](https://clig.dev/#simple-parts-that-work-together.

+
+

Global structure

+

Each commands with the --json parameter results on a single JSON structure printed on stdout :

+
{
+	"status": STATUS
+	"message": MESSAGE
+	"data": DATA
+}
+
+
+
    +
  • STATUS : The result status, it can be "OK" if there was no errors during the process, "ERROR" otherwise.

  • +
  • MESSAGE : The message according to the command purpose.

  • +
  • DATA : A dictionary containing every exploitable informations that a command can generate.

  • +
+

The DATA dictionary content changes according to the command context (see below).

+

NOTE : Configuration file related errors arent produced in a JSON format.

+
+
+

Specific result JSON structures

+
+

Errors

+

When an error is raised during the process with any --json parameter set with subcommands, the output JSON will be :

+
{
+	"status": "ERROR",
+	"message": "An error occured",
+	"data": {
+		"error": ERROR
+	}
+}
+
+
+
    +
  • ERROR : The error message that occured

  • +
+
+
+

start sub-command

+

anwdlserver start with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Server is started",
+	"data": {}
+}
+
+
+

NOTE : If the option -d is set, no output will be displayed.

+

If the option -c is set :

+
{
+	"status": "OK",
+	"message": "Check done",
+	"data": {
+		"errors_recorded": ERRORS_RECORDED, 
+		"errors_list": ERRORS_LIST
+	}
+}
+
+
+

If an error occured during server environment verification, the JSON structure looks like this :

+
{
+	"status": "ERROR",
+	"message": "Errors detected on server environment",
+	"data": {
+		"errors_recorded": ERRORS_RECORDED, 
+		"errors_list": ERRORS_LIST
+	}
+}
+
+
+
    +
  • ERRORS_RECORDED : The amount of errors recorded

  • +
  • ERRORS_LIST : The errors messages list

  • +
+
+
+

stop sub-command

+

anwdlserver stop with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Server is stopped",
+	"data": {}
+}
+
+
+
+
+

restart sub-command

+

anwdlserver restart with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Server is started",
+	"data": {}
+}
+
+
+
+
+

dl-iso sub-command

+

anwdlserver dl-iso with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "ISO image was successfully downloaded",
+	"data": {
+		"checksums": CHECKSUMS,
+		"version": VERSION,
+		"file_path": FILE_PATH
+	}
+}
+
+
+
    +
  • CHECKSUMS : The downloaded ISO MD5 and SHA256 checksums, in a list

  • +
  • VERSION : The downloaded ISO version

  • +
  • FILE_PATH : The downloaded ISO file path

  • +
+

if the option -m is set :

+
{
+	"status": "OK",
+	"message": "Remote ISO metadata",
+	"data": {
+		"checksums": CHECKSUMS,
+		"version": VERSION,
+	}
+}
+
+
+
    +
  • CHECKSUMS : The downloaded ISO MD5 and SHA256 checksums, in a list

  • +
  • VERSION : The downloaded ISO version

  • +
+
+
+

access-tk sub-command

+

anwdlserver access-tk -a with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "New access token created",
+	"data": {
+		"entry_id": ENTRY_ID,
+		"access_token": ACCESS_TOKEN,
+	}
+}
+
+
+
    +
  • ENTRY_ID : The created entry ID

  • +
  • ACCESS_TOKEN : The new access token, in plain text

  • +
+

anwdlserver access-tk -l with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Recorded entries ID",
+	"data": {
+		"entry_list": ENTRY_LIST
+	}
+}
+
+
+
    +
  • ENTRY_LIST : The recorded entries list

  • +
+

anwdlserver access-tk -r with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Entry ID was deleted",
+	"data": {}
+}
+
+
+

anwdlserver access-tk -e with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Entry ID was enabled",
+	"data": {}
+}
+
+
+

anwdlserver access-tk -d with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "Entry ID was disabled",
+	"data": {}
+}
+
+
+
+
+

regen-rsa sub-command

+

anwdlserver regen-rsa with the --json parameter will result in :

+
{
+	"status": "OK",
+	"message": "RSA keys re-generated",
+	"data": {
+		"fingerprint": FINGERPRINT
+	}
+}
+
+
+
    +
  • FINGERPRINT : The new generated public key’s SHA256 digest

  • +
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/examples/basic_server.html b/docs/server/developer_section/examples/basic_server.html new file mode 100644 index 0000000..3a16778 --- /dev/null +++ b/docs/server/developer_section/examples/basic_server.html @@ -0,0 +1,150 @@ + + + + + + + + Basic server — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Basic server

+
+

Here is a simple server stub that can be used as a functional server :

+
from anwdlserver.core.server import ServerInterface
+
+# Replace it with your own path
+CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso"
+
+# Create a new ServerInterface instance
+server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH)
+
+# Print the message when the server is ready
+@server.on_started
+def notify_started(**kwargs):
+	print("Server is started")
+
+# Start the server
+server.startServer()
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/examples/custom_container_capacity.html b/docs/server/developer_section/examples/custom_container_capacity.html new file mode 100644 index 0000000..2cf8334 --- /dev/null +++ b/docs/server/developer_section/examples/custom_container_capacity.html @@ -0,0 +1,166 @@ + + + + + + + + Set custom container capacity — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Set custom container capacity

+
+
from anwdlserver.core.server import ServerInterface
+
+# Replace it with your own path
+CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso"
+
+# Create a new ServerInterface instance
+server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH)
+
+# Print the message when the server is ready
+@server.on_started
+def notify_started(**kwargs):
+	print("Server is started")
+
+# Print a message when a request is received
+@server_interface.on_request
+def notify_request(**kwargs):
+	request_verb = kwargs["client_instance"].getStoredRequest()
+	
+	print("Received {} request".format(request_verb["verb"]))
+
+# Set created containers capacity with 2048 Mb of memory and 2 VCPUs
+@server.on_container_created
+def handle_container_creation(**kwargs):
+	container_instance = kwargs.get("container_instance")
+
+	print(f"New container created : {container_instance.getUUID()}")
+
+	container_instance.setMemory(2048)
+	container_instance.setVCPUs(2)
+
+# Start the server
+server.startServer()
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/examples/custom_verb_handle.html b/docs/server/developer_section/examples/custom_verb_handle.html new file mode 100644 index 0000000..7ebdd72 --- /dev/null +++ b/docs/server/developer_section/examples/custom_verb_handle.html @@ -0,0 +1,167 @@ + + + + + + + + Handle a custom request verb — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Handle a custom request verb

+
+

This stub shows how to implement a custom verb handling, here the server will respond “PONG” in parameters when a client sends a “PING” request :

+
from anwdlserver.core.server import (
+	ServerInterface, 
+	RESPONSE_MSG_OK
+)
+
+# Replace it with your own path
+CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso"
+
+# Sends 'PONG' in response when a 'PING' request is received
+def handle_ping_request(**kwargs):
+	client_instance = kwargs.get("client_instance")
+
+	print("Received PING request")
+
+	client_instance.sendResponse(True, RESPONSE_MSG_OK, data={"answer": "PONG"})
+
+	# Not mandatory, but programatically correct
+	client_instance.closeConnection()
+
+# Create a new ServerInterface instance
+server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH)
+
+# Print the message when the server is ready
+@server.on_started
+def notify_started(**kwargs):
+	print("Server is started")
+	
+# Add the new 'PING' request handler
+server.setRequestHandler("PING", handle_ping_request)
+
+# Start the server
+server.startServer()
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/examples/ip_filtering.html b/docs/server/developer_section/examples/ip_filtering.html new file mode 100644 index 0000000..966e969 --- /dev/null +++ b/docs/server/developer_section/examples/ip_filtering.html @@ -0,0 +1,167 @@ + + + + + + + + Implement IP filtering — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Implement IP filtering

+
+

Here is a server stub that involves an IP filtering feature :

+
+

For performance reasons, it is recommended to implement an IP filtering feature with the on_connection callback since it is faster than a whole client instance.

+
+
from anwdlserver.core.server import ServerInterface
+
+# Replace it with your own path / values
+CONTAINER_ISO_PATH = "/path/of/the/ISO/file.iso"
+DENIED_IP_ARRAY = ["1.1.1.1", "3.3.3.3", "5.5.5.5"]
+
+# Create a new ServerInterface instance
+server = ServerInterface(container_iso_path=CONTAINER_ISO_PATH)
+
+# Print the message when the server is ready
+@server.on_started
+def notify_started(**kwargs):
+	print("Server is started")
+
+# IP filtering implementation
+@server.on_connection
+def handle_connection(**kwargs):
+	client_socket = kwargs.get("client_socket")
+	client_ip = client_socket.getpeername()[0]
+
+	if client_ip in DENIED_IP_ARRAY:
+		# The server process will notice that the client socket is closed
+		# and will pass the session after the end of this routine.
+		print(f"Connection closed for denied IP : {client_ip}")
+
+		client_socket.close()
+
+# Start the server
+server.startServer()
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/index.html b/docs/server/developer_section/index.html new file mode 100644 index 0000000..3fad71e --- /dev/null +++ b/docs/server/developer_section/index.html @@ -0,0 +1,312 @@ + + + + + + + + Developer section — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Developer section

+
+

Hello and welcome to the Anweddol server developer documentation.

+

Here, you’ll find every informations and documentation about the server python API features.

+

NOTE : At the root of anwdlserver, there is the CLI source code : They are not meant to be used on an external program since it’s the server implementation’s code itself.

+
+

Important note

+

The Anweddol server installation requires a specific setup before installation and usage in order to provide a functional service.

+

See the administration guide Installation section to learn more before getting started on the server API.

+
+
+

Examples

+

See basic server stubs that can be used as examples.

+
+ +
+ + + +
+
+

API references

+

Learn about every features that the Anweddol server can provide. +You can also see the Technical specificatons section to get every informations on how the Anweddol server works.

+
+

Core features

+

The core features, also called core, are every needed functionnalities that an Anweddol server must have in order to provide a valid service.

+ + + + + + + +
+
+

Tools features

+

The tools features are additional functionnalities (authentication utilities, …) coming with the Anweddol server package.

+ +
+
+
+

CLI references

+

The actual Anweddol server CLI provides a JSON output feature that allows inter-program communication.

+ +
+
+

Troubleshooting

+

A troubleshooting page is also available for the Anweddol server API :

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/developer_section/troubleshooting.html b/docs/server/developer_section/troubleshooting.html new file mode 100644 index 0000000..b866c60 --- /dev/null +++ b/docs/server/developer_section/troubleshooting.html @@ -0,0 +1,140 @@ + + + + + + + + Troubleshooting — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Troubleshooting

+
+

Here is a list of non-exhaustive potential problems that can be encountered while using the Anweddol server API :

+
+

libvirt:  error : Cannot get interface MTU on 'virbr0'[…]

+

Description : The libvirt daemon is not started.

+

Solution :

+

Start the libvirtd daemon :

+
$ sudo systemctl start libvirtd.service
+
+
+

Then restart your script.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/genindex.html b/docs/server/genindex.html new file mode 100644 index 0000000..0a1ee18 --- /dev/null +++ b/docs/server/genindex.html @@ -0,0 +1,111 @@ + + + + + + + Index — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/index.html b/docs/server/index.html new file mode 100644 index 0000000..334e971 --- /dev/null +++ b/docs/server/index.html @@ -0,0 +1,255 @@ + + + + + + + + The Anweddol server — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

The Anweddol server

+

made-with-python +build-passing +license +reddit

+
+
+

Global overview

+

Anweddol is a client/server system providing temporary, SSH-controllable virtual machines to enhance anonymity online.

+

It’s usefulness comes when someone wants to use a fully functional computer while being exposed to less dangers by using it remotely on a dedicated server, and by destroying it after use.

+
+
+

Effective scenario

+

terminology :

+
    +
  • Container : A temporary virtual machine hosted on a server that can be controlled from the client side.

  • +
+
    +
  1. Alice wants to have access to a container. She will first send a request asking it to create a new container.

  2. +
  3. The server will start a new container, administer it and send the SSH credentials to Alice for her to be able to securely interact with the container.

  4. +
  5. Once that Alice is done, she will send another request with previously received container identification and credentials to the server.

  6. +
  7. The server identifies the affiliated running container, and destroys it.

  8. +
+
+
+

Contents

+ + + +
+
+

Contribution

+

See the contribution section to know how to contribute :

+ +
+ +
+

Indices and tables

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/objects.inv b/docs/server/objects.inv new file mode 100644 index 0000000..edf669e Binary files /dev/null and b/docs/server/objects.inv differ diff --git a/docs/server/search.html b/docs/server/search.html new file mode 100644 index 0000000..baa1468 --- /dev/null +++ b/docs/server/search.html @@ -0,0 +1,130 @@ + + + + + + + Search — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/searchindex.js b/docs/server/searchindex.js new file mode 100644 index 0000000..e0e970d --- /dev/null +++ b/docs/server/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["administration_guide/access_token", "administration_guide/configuration_file", "administration_guide/container_iso", "administration_guide/index", "administration_guide/installation", "administration_guide/logging", "administration_guide/server_usage", "administration_guide/troubleshooting", "contribute", "developer_section/api_references/core/client", "developer_section/api_references/core/cryptography", "developer_section/api_references/core/database", "developer_section/api_references/core/sanitization", "developer_section/api_references/core/server", "developer_section/api_references/core/utilities", "developer_section/api_references/core/virtualization", "developer_section/api_references/tools/accesstk", "developer_section/cli/json_output", "developer_section/examples/basic_server", "developer_section/examples/custom_container_capacity", "developer_section/examples/custom_verb_handle", "developer_section/examples/ip_filtering", "developer_section/index", "developer_section/troubleshooting", "index", "technical_specifications/core/client_authentication", "technical_specifications/core/communication", "technical_specifications/core/database", "technical_specifications/core/networking", "technical_specifications/core/virtualization", "technical_specifications/index", "technical_specifications/tools/access_token"], "filenames": ["administration_guide/access_token.md", "administration_guide/configuration_file.md", "administration_guide/container_iso.md", "administration_guide/index.md", "administration_guide/installation.md", "administration_guide/logging.md", "administration_guide/server_usage.md", "administration_guide/troubleshooting.md", "contribute.md", "developer_section/api_references/core/client.md", "developer_section/api_references/core/cryptography.md", "developer_section/api_references/core/database.md", "developer_section/api_references/core/sanitization.md", "developer_section/api_references/core/server.md", "developer_section/api_references/core/utilities.md", "developer_section/api_references/core/virtualization.md", "developer_section/api_references/tools/accesstk.md", "developer_section/cli/json_output.md", "developer_section/examples/basic_server.md", "developer_section/examples/custom_container_capacity.md", "developer_section/examples/custom_verb_handle.md", "developer_section/examples/ip_filtering.md", "developer_section/index.md", "developer_section/troubleshooting.md", "index.md", "technical_specifications/core/client_authentication.md", "technical_specifications/core/communication.md", "technical_specifications/core/database.md", "technical_specifications/core/networking.md", "technical_specifications/core/virtualization.md", "technical_specifications/index.md", "technical_specifications/tools/access_token.md"], "titles": ["Access token", "Configuration file", "Container ISO", "Administration guide", "Installation", "Logging", "Server usage", "Troubleshooting", "Contribute", "Client", "Cryptography", "Database", "Sanitization", "Server", "Utilities", "Virtualization", "Access token", "CLI JSON output", "Basic server", "Set custom container capacity", "Handle a custom request verb", "Implement IP filtering", "Developer section", "Troubleshooting", "The Anweddol server", "Client authentication", "Communication", "Database", "Networking", "Virtualization", "Technical specifications", "Access token"], "terms": {"The": [0, 1, 3, 4, 5, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 25, 26, 27, 28, 29], "anweddol": [0, 1, 2, 3, 5, 6, 7, 8, 11, 17, 22, 23, 29, 30], "server": [0, 1, 3, 5, 7, 8, 9, 11, 15, 17, 19, 20, 21, 22, 23, 25, 26, 27, 29, 30, 31], "implement": [0, 1, 3, 12, 20, 22, 24, 26], "can": [0, 2, 4, 6, 7, 8, 10, 11, 13, 17, 18, 22, 23, 24, 26, 29], "restrict": [0, 31], "its": [0, 1, 8, 10, 11, 12, 15, 16, 27, 28, 29, 30], "util": [0, 13, 15, 22, 29], "us": [0, 1, 4, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 18, 22, 23, 24, 25, 26, 27, 28, 29, 31], "tool": [0, 16, 24, 29, 31], "featur": [0, 5, 7, 8, 9, 10, 11, 13, 14, 16, 17, 21, 24, 29, 31], "client": [0, 5, 10, 11, 13, 15, 16, 20, 21, 22, 24, 26, 27, 28, 29, 30, 31], "authent": [0, 3, 5, 10, 13, 15, 22, 24, 26, 30, 31], "an": [0, 3, 9, 10, 11, 12, 13, 14, 15, 16, 17, 21, 22, 24, 26, 28, 29, 31], "i": [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31], "url": [0, 25, 29, 31], "safe": [0, 11, 16, 25, 31], "124": [0, 31], "charact": [0, 5, 9, 25, 26, 29, 31], "long": [0, 10], "These": 0, "ar": [0, 4, 5, 10, 11, 12, 13, 15, 16, 22, 25, 26, 27, 29, 31], "store": [0, 1, 5, 9, 11, 15, 16, 27, 29, 31], "sqlite": [0, 11, 16, 27, 31], "databas": [0, 16, 22, 29, 30], "file": [0, 2, 3, 4, 5, 15, 16, 17, 18, 19, 20, 21, 24, 29, 31], "system": [0, 14, 15, 24, 28, 29, 31], "note": [0, 4, 5, 6, 9, 10, 11, 12, 13, 15, 16, 17, 24, 26], "There": [0, 8, 25, 26], "one": [0, 8, 13, 25, 29, 31], "sinc": [0, 11, 15, 16, 21, 22, 29], "cannot": [0, 22], "2": [0, 5, 10, 13, 15, 19, 25], "same": [0, 13, 29], "see": [0, 4, 5, 7, 9, 11, 12, 13, 15, 16, 17, 22, 24, 26, 27, 28, 29], "usag": [0, 3, 4, 16, 22, 24, 29], "guid": [0, 8, 9, 22, 24, 28], "learn": [0, 1, 2, 4, 7, 9, 11, 12, 15, 16, 22, 26, 27], "more": [0, 2, 4, 5, 7, 8, 9, 11, 12, 15, 16, 22, 26, 27, 28], "thi": [0, 2, 4, 5, 6, 8, 9, 11, 12, 13, 15, 16, 17, 20, 21, 26, 28, 29, 31], "Being": 0, "default": [0, 2, 4, 9, 10, 13, 15, 16, 26, 29], "you": [0, 2, 4, 5, 6, 8, 10, 15, 22, 24, 29, 30], "chang": [0, 17, 24], "field": 0, "valu": [0, 4, 9, 10, 11, 12, 13, 14, 15, 16, 21, 26, 31], "true": [0, 9, 10, 12, 13, 14, 15, 16, 20, 26], "list": [0, 7, 11, 15, 16, 17, 23, 26], "allow": [0, 15, 17, 22], "deni": [0, 3, 21, 24], "ip": [0, 5, 9, 14, 15, 22, 24], "specifi": [0, 2, 4, 9, 10, 11, 12, 13, 26, 29], "ani": [0, 4, 8, 17, 26, 31], "allowed_ip_list": 0, "denied_ip_list": 0, "paramet": [0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 26, 31], "token_authent": 0, "If": [0, 4, 6, 7, 8, 10, 11, 15, 16, 17, 29], "set": [0, 4, 6, 7, 9, 10, 11, 12, 13, 15, 17, 22, 24, 26, 29], "fals": [0, 9, 10, 12, 13, 14, 15, 16, 26], "all": [0, 5, 29], "below": [0, 4, 13, 17, 26, 29], "ignor": [0, 16, 31], "here": [0, 5, 7, 13, 18, 20, 21, 22, 23, 24, 26, 27, 31], "Then": [0, 4, 23, 29], "restart": [0, 4, 13, 22, 23], "ad": 0, "abl": [0, 4, 8, 12, 24, 25], "To": [0, 4, 13], "execut": [0, 4, 6, 13, 15, 26, 29], "anwdlserv": [0, 4, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], "tk": [0, 22], "It": [0, 1, 4, 5, 6, 9, 10, 11, 12, 13, 14, 24, 26, 28, 29, 31], "result": [0, 8, 12, 13, 15, 16, 22], "creat": [0, 4, 5, 7, 11, 13, 15, 16, 17, 18, 19, 20, 21, 24, 25, 26, 28, 29], "entri": [0, 11, 16, 17, 27], "id": [0, 5, 9, 11, 16, 17, 27, 29], "standard": [0, 15], "output": [0, 15, 22, 24], "On": [0, 29], "side": [0, 24, 26], "need": [0, 2, 4, 6, 8, 15, 22, 28, 29], "record": [0, 13, 17], "order": [0, 2, 4, 8, 11, 15, 16, 17, 22, 25, 28, 29, 31], "hash": [0, 11, 16, 27, 31], "technic": [0, 2, 8, 11, 12, 15, 16, 22, 24], "specif": [0, 2, 4, 8, 11, 12, 15, 16, 22, 24, 28, 29], "section": [0, 2, 3, 4, 5, 6, 7, 9, 11, 12, 13, 15, 16, 24, 26], "": [0, 4, 5, 9, 11, 13, 16, 17, 22, 24, 26, 29], "wai": [0, 3, 4, 5, 11, 16, 17], "them": [0, 5, 6, 11, 16, 29, 31], "again": [0, 11, 16], "plain": [0, 11, 16, 17], "text": [0, 10, 11, 16, 17, 26, 29, 31], "somewher": [0, 11, 16], "further": [0, 11, 16], "oper": [0, 11, 12, 13, 16, 29], "want": [0, 15, 24, 25], "r": [0, 17, 24, 26], "entry_id": [0, 11, 16, 17], "have": [0, 4, 5, 13, 22, 24, 28], "possibl": [0, 5, 9, 10, 11, 15, 29], "temporarili": 0, "d": [0, 6, 17], "A": [0, 4, 9, 10, 11, 12, 13, 15, 16, 17, 22, 24, 25, 26, 28, 29, 31], "automat": [0, 9, 10, 11, 13, 15, 16], "option": [0, 6, 13, 17], "And": 0, "re": [0, 7, 17], "e": [0, 4, 17], "yaml": 1, "etc": [1, 2, 4], "config": 1, "after": [1, 15, 21, 24], "instal": [1, 3, 6, 7, 22, 24, 29], "read": [1, 8, 28], "header": 1, "about": [1, 2, 22, 26, 30], "In": [2, 8, 17, 28, 29], "provic": [2, 29], "valid": [2, 4, 9, 12, 13, 14, 15, 22, 26, 29], "servic": [2, 4, 6, 22, 23, 29, 31], "domain": [2, 4, 5, 15, 29], "run": [2, 4, 5, 6, 13, 15, 24, 27, 29], "actual": [2, 4, 6, 13, 15, 22, 25, 29], "custom": [2, 4, 13, 22, 24, 29], "live": [2, 29], "debian": [2, 29], "retriev": [2, 8, 29, 30], "offici": [2, 29], "mirror": [2, 29], "ll": [2, 22, 29, 30], "copi": [2, 29], "anweddol_contain": [2, 29], "folder": [2, 5], "path": [2, 13, 15, 16, 17, 18, 19, 20, 21, 29], "configur": [2, 3, 4, 5, 17, 24, 28], "container_iso_path": [2, 13, 15, 18, 19, 20, 21], "kei": [2, 5, 9, 10, 12, 13, 17, 24, 26, 27, 31], "meant": [2, 22], "replac": [2, 4, 18, 19, 20, 21], "each": [2, 17, 26, 28, 29], "updat": [2, 4, 11, 29], "announc": [2, 9, 12], "o": [2, 4, 26, 30], "hello": [3, 22, 26, 30], "welcom": [3, 22, 30], "follow": [3, 4, 6, 9], "explain": [3, 26], "how": [3, 8, 13, 20, 22, 24, 26, 30], "optim": 3, "environ": [3, 6, 17, 24, 29], "setup": [3, 7, 22, 24], "libvirt": [3, 15, 22, 24, 28, 29], "network": [3, 14, 15, 29, 30], "contain": [3, 5, 11, 13, 15, 17, 22, 24, 26, 27, 30], "iso": [3, 13, 15, 18, 19, 20, 21, 22, 24, 29], "get": [3, 9, 10, 11, 13, 15, 16, 19, 20, 21, 22, 24, 29], "start": [3, 5, 13, 15, 18, 19, 20, 21, 22, 23, 24, 29], "uninstal": [3, 24], "stop": [3, 4, 13, 15, 22, 24], "access": [3, 7, 22, 24, 30], "token": [3, 11, 17, 22, 24, 25, 27, 30], "prerequisit": [3, 4, 24], "add": [3, 4, 6, 11, 15, 20, 24], "delet": [3, 4, 5, 10, 11, 15, 16, 17, 24, 29], "enabl": [3, 4, 6, 7, 16, 17, 24, 29, 31], "disabl": [3, 4, 7, 16, 17, 24, 29], "download": [3, 4, 17, 24], "imag": [3, 4, 13, 15, 17, 24, 29], "log": [3, 9, 24], "rotat": [3, 24], "troubleshoot": [3, 6, 24], "permiss": [3, 4, 24], "unavail": [3, 13, 15, 24, 26], "polkit": [3, 24], "agent": [3, 24], "avail": [3, 13, 15, 22, 24, 26, 29], "action": [3, 24, 26], "org": [3, 24], "unix": [3, 24], "manag": [3, 4, 9, 13, 15, 16, 24, 30], "cover": 4, "befor": [4, 6, 8, 10, 15, 22, 25, 26, 29, 31], "sourc": [4, 22, 29], "git": 4, "clone": 4, "http": [4, 11, 17, 24, 26], "github": [4, 24, 29], "com": [4, 24], "project": [4, 8, 24], "cd": 4, "sudo": [4, 6, 7, 23], "pip": 4, "nessessari": 4, "user": [4, 6, 7, 29, 31], "dure": [4, 5, 9, 13, 17, 26, 27, 29], "launch": 4, "non": [4, 7, 23, 26], "root": [4, 22], "onli": [4, 10, 11, 12, 26, 29], "packag": [4, 9, 10, 11, 12, 13, 14, 15, 16, 22], "full": [4, 13, 28, 29], "skip": 4, "toolkit": 4, "virtual": [4, 22, 24, 28, 30], "platform": [4, 26], "must": [4, 9, 10, 11, 12, 13, 14, 15, 16, 22, 28, 31], "via": [4, 6, 28, 29], "your": [4, 8, 18, 19, 20, 21, 23], "apt": 4, "python": [4, 5, 22, 29], "dnf": 4, "yum": 4, "also": [4, 11, 16, 22, 24], "from": [4, 9, 11, 12, 15, 18, 19, 20, 21, 24], "But": 4, "build": 4, "depend": [4, 13, 26], "manual": [4, 28], "topic": 4, "document": [4, 13, 22, 24, 29, 30], "modifi": 4, "qemu": [4, 15, 29], "conf": 4, "give": [4, 8], "appropri": [4, 8], "right": 4, "edit": 4, "nano": 4, "find": [4, 8, 22], "group": [4, 7], "direct": [4, 6], "By": [4, 15], "both": 4, "some": [4, 6, 26], "exampl": [4, 13, 24, 26], "name": [4, 5, 9, 10, 13, 14, 15, 16, 29], "0": [4, 5, 7, 9, 15, 16, 21, 26, 29, 31], "super": 4, "uid": 4, "100": 4, "process": [4, 10, 12, 13, 15, 17, 21, 26, 29, 31], "instanc": [4, 5, 9, 11, 13, 15, 16, 18, 19, 20, 21], "similar": [4, 11, 16], "uncom": 4, "line": 4, "shown": 4, "libvirtd": [4, 6, 23], "daemon": [4, 6, 23], "systemctl": [4, 6, 23], "ostechnix": 4, "For": [4, 15, 21, 26], "commun": [4, 17, 22, 28, 30], "outsid": 4, "host": [4, 24], "bridg": [4, 15, 28, 29], "interfac": [4, 5, 14, 15, 22, 28, 29], "link": 4, "main": [4, 13], "consist": 4, "slave": [4, 28], "connect": [4, 5, 9, 11, 13, 15, 16, 21, 26, 28, 29], "runtim": [4, 5, 13, 26, 29], "tap": [4, 28], "wi": 4, "fi": 4, "done": [4, 5, 17, 24], "continu": [4, 6, 15], "step": 4, "accord": [4, 10, 12, 17], "first": [4, 5, 6, 8, 9, 24, 29], "anwdlbr0": [4, 15, 28], "master_interface_nam": 4, "master": 4, "brctl": 4, "addbr": 4, "addif": 4, "persist": [4, 7], "printf": 4, "ifac": 4, "inet": 4, "dhcp": 4, "n": 4, "tbridge_port": 4, "ifup": 4, "networkmanag": 4, "chkconfig": 4, "off": [4, 29], "defin": [4, 26, 29], "script": [4, 15, 23, 29], "devic": [4, 29], "nhwaddr": 4, "ifconfig": 4, "grep": 4, "nonboot": 4, "ye": [4, 29], "nbridg": 4, "nnm_control": 4, "sysconfig": 4, "ntype": 4, "nbootproto": 4, "ndelai": 4, "ifcfg": 4, "sysctl": 4, "rule": 4, "packet": [4, 26], "forward": 4, "net": 4, "nf": 4, "call": [4, 9, 11, 13, 15, 16, 22, 29], "ip6tabl": 4, "nnet": 4, "iptabl": 4, "arptabl": 4, "p": 4, "accept": [4, 29], "m": [4, 17], "physdev": 4, "j": 4, "lokkit": 4, "ipv4": [4, 9, 14], "filter": [4, 22, 24], "provid": [4, 8, 10, 11, 13, 15, 16, 17, 22, 24, 26, 28, 31], "function": [4, 10, 15, 18, 22, 24, 29], "At": [4, 22], "point": [4, 13, 15, 26], "should": [4, 10], "do": [4, 7], "so": 4, "everyth": 4, "associ": 4, "cli": [5, 6, 24], "modul": 5, "ensur": [5, 25, 27, 28, 29], "viabl": 5, "var": [5, 15], "txt": [5, 29], "format": [5, 9, 10, 12, 14, 17, 19, 25, 29, 30], "asctim": 5, "levelnam": 5, "messag": [5, 9, 12, 13, 17, 18, 19, 20, 21, 26], "sampl": 5, "gener": [5, 10, 15, 17, 29], "develop": [5, 12, 24, 29], "phase": 5, "2023": 5, "06": 5, "27": 5, "17": 5, "59": 5, "25": 5, "126": 5, "warn": 5, "init": 5, "initi": [5, 9, 10, 15, 26], "info": [5, 28], "load": 5, "rsa": [5, 9, 10, 22, 26], "pair": [5, 10, 11], "700": 5, "711": 5, "bind": [5, 13, 15, 29], "handler": [5, 13, 20], "routin": [5, 13, 21, 29], "readi": [5, 13, 15, 18, 19, 20, 21], "712": 5, "42": 5, "724": 5, "12ca17b": 5, "new": [5, 8, 11, 13, 15, 17, 18, 19, 20, 21, 24, 26, 29], "770": 5, "receiv": [5, 9, 13, 19, 20, 24, 25, 26, 28], "stat": [5, 13, 26], "request": [5, 9, 12, 13, 19, 22, 24, 25, 30, 31], "778": 5, "close": [5, 9, 11, 13, 14, 15, 16, 21], "49": 5, "958": 5, "50": 5, "003": 5, "5d730281": 5, "414d": 5, "4bd8": 5, "ae01": 5, "13ebf302fd17": 5, "wa": [5, 13, 17, 26], "18": 5, "00": 5, "03": 5, "421": 5, "443": 5, "version": [5, 17, 29], "openssh_8": 5, "4p1": 5, "567": 5, "password": [5, 15, 25, 29], "success": [5, 9, 12, 26], "endpoint": [5, 13, 15, 29], "shell": [5, 13, 15], "open": [5, 9, 12, 13, 15], "05": 5, "458": 5, "19": 5, "881": 5, "927": 5, "destroi": [5, 13, 15, 24, 25, 26], "20": [5, 15], "143": 5, "145": 5, "repres": [5, 9, 11, 12, 13, 15, 16], "programmat": 5, "identifi": [5, 11, 13, 16, 24, 29, 31], "other": [5, 17], "than": [5, 15, 21], "hi": 5, "7": [5, 9, 13], "sha256": [5, 9, 11, 15, 16, 17, 27, 29, 31], "archiv": 5, "separ": 5, "withing": 5, "zip": 5, "archived_": 5, "date": 5, "where": [5, 13, 25, 29], "log_rot": 5, "tutori": 6, "mai": [6, 26], "check": [6, 9, 12, 14, 15, 17, 25, 29], "correctli": [6, 13], "up": [6, 29], "c": [6, 17], "error": [6, 8, 11, 12, 13, 15, 16, 22, 30], "occur": [6, 11, 13, 16, 17, 26], "fix": 6, "systemd": 6, "Or": 6, "synchron": [6, 13], "termin": [6, 13], "prefer": 6, "lifetim": 6, "control": [6, 24, 29], "mix": 6, "between": [6, 14, 25, 26, 29], "caus": 6, "hazard": 6, "behaviour": 6, "As": 6, "mention": 6, "previou": [6, 26], "exhaust": [7, 23, 26], "potenti": [7, 8, 23], "problem": [7, 8, 23], "encount": [7, 8, 23], "while": [7, 23, 24], "descript": [7, 9, 10, 13, 15, 16, 23], "api": [7, 15, 23, 24, 30], "fail": 7, "resourc": 7, "due": 7, "lack": 7, "solut": [7, 23], "try": 7, "selinux": 7, "enforc": 7, "setenforc": 7, "forget": 7, "when": [7, 13, 15, 17, 18, 19, 20, 21, 24, 25, 26, 29, 31], "unus": 7, "1": [7, 9, 11, 13, 14, 16, 21, 26, 29, 31], "usermod": 7, "ag": 7, "thank": 8, "take": 8, "time": [8, 27], "guidelin": 8, "bring": [8, 29], "idea": 8, "enhanc": [8, 24], "eas": 8, "know": [8, 13, 24], "work": [8, 17, 22, 30], "softwar": 8, "bug": 8, "code": [8, 22, 26], "doc": 8, "search": [8, 11, 15, 16, 24], "anoth": [8, 24], "kind": [8, 25, 26], "prevent": [8, 29], "duplic": 8, "comment": 8, "import": [8, 18, 19, 20, 21, 24], "scroll": 8, "through": 8, "interest": 8, "assigne": 8, "everyon": 8, "free": 8, "which": [8, 15, 29], "review": 8, "merg": 8, "modif": [8, 11, 16], "proposit": 8, "involv": [8, 21], "core": [8, 9, 10, 11, 12, 13, 14, 15, 18, 19, 20, 21, 24], "alreadi": [8, 11, 29], "well": 8, "default_store_request": 9, "default_auto_exchange_kei": 9, "exchang": [9, 13, 26], "default_receive_first": 9, "public": [9, 10, 17, 24, 26], "message_ok": 9, "transmit": 9, "state": [9, 26, 29], "acknowledg": 9, "message_nok": 9, "socket": [9, 13, 14, 21], "timeout": [9, 13, 15], "int": [9, 10, 11, 13, 14, 15, 16], "none": [9, 10, 11, 12, 13, 14, 15, 16], "rsa_wrapp": 9, "rsawrapp": [9, 13, 22], "aes_wrapp": 9, "aeswrapp": [9, 22], "auto_exchange_kei": 9, "bool": [9, 10, 12, 13, 14, 15, 16], "descriptor": [9, 14], "activ": [9, 15, 29], "appli": [9, 13, 15], "__del__": [9, 11, 13, 15, 16], "isclos": [9, 15], "return": [9, 10, 11, 12, 13, 14, 15, 16, 26], "otherwis": [9, 10, 11, 12, 13, 14, 15, 16, 17], "getsocketdescriptor": 9, "getip": [9, 15], "str": [9, 10, 11, 12, 13, 14, 15, 16], "string": [9, 10, 13, 14, 25, 26, 27, 29, 31], "getid": 9, "administr": [9, 15, 22, 24, 28, 30], "gettimestamp": 9, "creation": [9, 11, 16, 27, 31], "timestamp": [9, 11, 16, 27, 31], "getstoredrequest": [9, 19], "dict": [9, 12, 13, 26], "dictionari": [9, 12, 17], "normal": [9, 12, 13], "getrsawrapp": 9, "getaeswrapp": 9, "setrsawrapp": 9, "setaeswrapp": 9, "sendpublicrsakei": 9, "send": [9, 12, 20, 24, 26, 31], "local": [9, 10, 15], "rais": [9, 10, 11, 13, 15, 17], "runtimeerror": [9, 10, 15], "recvpublicrsakei": 9, "valueerror": 9, "sendaeskei": 9, "ae": [9, 10, 26], "recvaeskei": 9, "exchangekei": 9, "receive_first": 9, "sendrespons": [9, 12, 20], "data": [9, 10, 12, 17, 20, 26, 27, 29, 31], "reason": [9, 12, 21, 26], "respons": [9, 12, 20, 30], "content": [9, 10, 11, 12, 16, 17, 26, 27], "empti": [9, 12], "append": [9, 12], "like": [9, 12, 17, 26, 29], "refus": [9, 12, 13, 26], "specified_reason": [9, 12], "recvrequest": 9, "store_request": 9, "tupl": [9, 11, 12, 13, 15, 16], "closeconnect": [9, 20], "within": [9, 11, 13, 15, 16], "programat": [9, 11, 13, 15, 16, 20], "better": [9, 11, 13, 15, 16], "natur": [9, 11, 13, 15, 16], "encrypt": [10, 30], "crypto": 10, "default_rsa_key_s": 10, "4096": [10, 26], "size": [10, 26], "default_aes_key_s": 10, "256": [10, 26], "default_pem_format": 10, "pem": 10, "default_generate_key_pair": 10, "default_derivate_public_kei": 10, "deriv": 10, "out": 10, "privat": [10, 25], "key_siz": 10, "generate_key_pair": 10, "exprim": [10, 13, 15], "bit": 10, "multipl": 10, "generatekeypair": 10, "self": 10, "getkeys": 10, "byte": [10, 26], "getpublickei": 10, "pem_format": 10, "sequenc": 10, "getprivatekei": 10, "getremotepublickei": 10, "setpublickei": 10, "public_kei": 10, "intern": [10, 13, 26], "setprivatekei": 10, "affili": [10, 13, 24, 25, 27, 30, 31], "private_kei": 10, "derivate_public_kei": 10, "setremotepublickei": 10, "remote_public_kei": 10, "remot": [10, 15, 17, 24], "encryptdata": 10, "encod": 10, "use_local_public_kei": 10, "being": [10, 15, 24], "mu": 10, "instead": 10, "decryptdata": 10, "cipher": 10, "decod": 10, "decrypt": 10, "signdata": 10, "sign": 10, "block": [10, 13], "peturn": 10, "verifydatasignatur": 10, "signatur": 10, "verifi": [10, 26], "boolean": [10, 13, 15, 16, 26, 31], "16": 10, "24": 10, "32": 10, "integ": [10, 11, 14, 26, 27, 29, 31], "getkei": 10, "getiv": 10, "initialis": 10, "vector": [10, 26], "iv": 10, "setkei": 10, "memori": [11, 15, 19, 27, 29], "sqlalchemi": [11, 27], "engin": [11, 30], "queri": [11, 16], "impli": [11, 16], "commit": [11, 16], "rollback": [11, 16], "getengin": 11, "object": [11, 13, 15, 16], "getengineconnect": 11, "getruntimetableobject": 11, "schema": [11, 26], "tabl": [11, 30], "getentryid": [11, 16], "container_uuid": [11, 15, 26, 29], "client_token": [11, 26], "credenti": [11, 13, 15, 24, 26, 29, 30], "clear": [11, 16], "uuid": [11, 15, 25, 27, 29], "exist": [11, 12, 14, 15, 16], "verif": [11, 12, 16, 17], "getcontaineruuidentryid": 11, "containeruuid": [11, 27], "column": [11, 27, 31], "concern": 11, "getentri": [11, 16], "creation_timestamp": [11, 16], "describ": [11, 16], "addentri": [11, 16], "infom": 11, "readthedoc": 11, "io": 11, "en": 11, "latest": 11, "technical_specif": 11, "client_authent": 11, "html": 11, "session": [11, 13, 21, 26, 30], "lookuperror": 11, "listentri": [11, 16], "partial": 11, "inform": [11, 16, 17, 22, 26], "updateentri": 11, "THe": 11, "deleteentri": [11, 16], "closedatabas": [11, 16], "rowid": [11, 16], "row": [11, 16, 31], "verifyrequestcont": 12, "request_dict": 12, "sanitized_request_dictionari": 12, "errors_dictionari": 12, "depict": [12, 13, 16, 26, 31], "detect": [12, 13, 17, 29], "cerberu": [12, 13, 26], "doe": 12, "strict": 12, "requir": [12, 22, 26], "correct": [12, 20], "unknown": [12, 13, 26], "structur": [12, 22, 26], "own": [12, 18, 19, 20, 21, 25], "mechan": 12, "makerespons": 12, "make": [12, 24], "response_dictionari": 12, "succeed": 12, "method": 12, "clientinst": [12, 13, 22], "wrap": 12, "default_server_bind_address": 13, "address": [13, 15, 29], "default_server_listen_port": 13, "6150": 13, "listen": [13, 15, 25, 29], "port": [13, 14, 15, 25, 29], "default_client_timeout": 13, "10": 13, "default_asychron": 13, "request_verb_cr": 13, "verb": [13, 19, 22, 24, 26], "indic": 13, "intent": [13, 26], "request_verb_destroi": 13, "request_verb_stat": 13, "statist": 13, "response_msg_ok": [13, 20], "ok": [13, 17, 26], "handl": [13, 22, 24, 25, 30], "response_msg_bad_auth": 13, "bad": [13, 26], "invalid": [13, 26], "were": 13, "given": [13, 25], "response_msg_bad_req": 13, "malform": [13, 26], "response_msg_refused_req": 13, "response_msg_unavail": 13, "current": [13, 26, 28], "response_msg_internal_error": 13, "experienc": [13, 26], "event_cli": 13, "on_client": 13, "event_client_clos": 13, "on_client_clos": 13, "event_connect": 13, "3": [13, 21, 25, 26, 29], "on_connect": [13, 21], "event_created_contain": 13, "4": [13, 26], "on_created_contain": 13, "event_created_endpoint_shel": 13, "5": [13, 21, 26], "on_created_endpoint_shel": 13, "event_destroyed_contain": 13, "6": [13, 15], "on_destroyed_contain": 13, "event_malformed_request": 13, "on_malformed_request": 13, "event_request": 13, "8": [13, 26], "on_request": [13, 19], "event_runtime_error": 13, "9": [13, 26], "on_runtime_error": 13, "event_start": 13, "on_start": [13, 18, 19, 20, 21], "event_stop": 13, "11": [13, 26], "on_stop": 13, "event_unknown_verb": 13, "12": [13, 26], "on_unknown_verb": 13, "bind_address": 13, "listen_port": [13, 15], "client_timeout": 13, "runtime_virtualization_interfac": 13, "virtualizationinterfac": [13, 22], "runtime_database_interfac": 13, "databaseinterfac": [13, 22], "runtime_rsa_wrapp": 13, "getruntimedatabaseinterfac": 13, "getruntimevirtualizationinterfac": 13, "getruntimersawrapp": 13, "getruntimestatist": 13, "is_run": 13, "recorded_runtime_errors_amount": 13, "uptim": [13, 26], "available_containers_amount": 13, "amount": [13, 15, 17, 29], "second": 13, "setrequesthandl": [13, 20], "callabl": 13, "equal": [13, 15], "seteventhandl": 13, "trigger": 13, "altern": 13, "decor": 13, "refer": [13, 24], "role": 13, "effect": 13, "startserv": [13, 18, 19, 20, 21], "asynchron": [13, 15], "mode": [13, 29], "stopserv": 13, "restartserv": 13, "def": [13, 18, 19, 20, 21], "kwarg": [13, 18, 19, 20, 21], "argument": 13, "differ": [13, 26], "That": 13, "why": [13, 26], "recommend": [13, 21], "pass": [13, 21], "raw": [13, 26, 29], "proper": 13, "basic": [13, 22, 24, 29], "client_inst": [13, 19, 20], "been": 13, "yet": [13, 15], "client_socket": [13, 21], "container_inst": [13, 15, 19], "containerinst": [13, 22], "endpoint_shell_inst": 13, "endpointshellinst": [13, 22], "ha": 13, "cerberus_error_dict": 13, "except": 13, "thread": 13, "ex_class": 13, "traceback": 13, "unsupport": [13, 26], "miscellan": 14, "isportbind": 14, "bindabl": [14, 25, 29], "65535": [14, 26], "issocketclos": 14, "socket_descriptor": 14, "isvalidip": 14, "isinterfaceexist": 14, "interface_nam": 14, "default_libvirt_driver_uri": 15, "default_container_endpoint_usernam": 15, "default_container_endpoint_password": 15, "default_container_endpoint_listen_port": 15, "22": 15, "default_bridge_interface_nam": 15, "default_nat_interface_nam": 15, "virbr0": [15, 22, 28], "nat": [15, 28, 29], "default_container_max_tryout": 15, "maximum": [15, 26], "attemp": 15, "default_max_allowed_contain": 15, "default_container_memori": 15, "2048": [15, 19], "mb": [15, 19], "alloc": 15, "default_container_vcpu": 15, "vcpu": [15, 19, 29], "default_container_port_rang": 15, "rang": 15, "10000": [15, 25, 29], "15000": [15, 25, 29], "assign": [15, 25, 29], "ssh": [15, 24, 25, 29], "default_container_wait_avail": 15, "wait": [15, 29], "default_container_destroy_domain": 15, "rather": 15, "shut": [15, 29], "down": [15, 29], "default_store_contain": 15, "ssh_client": 15, "paramiko": 15, "sshclient": 15, "getsshclient": 15, "getstoredcontainersshcredenti": 15, "usernam": [15, 25, 29], "setcontainersshcredenti": 15, "sent": [15, 26], "executecommand": 15, "command": [15, 22], "stdout": [15, 17], "stderr": 15, "stream": 15, "generatecontainersshcredenti": 15, "port_rang": 15, "random": [15, 25, 29], "chosen": 15, "anweddol_container_setup": 15, "sh": 15, "ot": 15, "thu": [15, 26, 28], "closeshel": 15, "complet": 15, "iso_path": [15, 29], "nat_interface_nam": [15, 29], "bridge_interface_nam": [15, 29], "endpoint_usernam": 15, "endpoint_password": 15, "endpoint_listen_port": 15, "isdomainrun": 15, "getnatinterfacenam": 15, "getbridgeinterfacenam": 15, "getdomaindescriptor": 15, "virdomain": 15, "getuuid": [15, 19], "getisopath": 15, "storag": 15, "getmac": 15, "mac": 15, "fetch": 15, "dnsmasq": 15, "statu": [15, 17, 26], "locat": 15, "lib": 15, "getmemori": 15, "getvcpu": 15, "cpu": [15, 29], "setdomaindescriptor": 15, "domain_descriptor": 15, "setisopath": 15, "setmemori": [15, 19], "greater": 15, "setvcpu": [15, 19], "setnatinterfacenam": 15, "setbridgeinterfacenam": 15, "setendpointsshauthenticationcredenti": 15, "makeisochecksum": 15, "digest": [15, 17], "createendpointshel": 15, "startdomain": 15, "wait_avail": 15, "wait_max_tryout": 15, "driver_uri": 15, "timeouterror": 15, "hypervisor": 15, "driver": [15, 29], "uri": 15, "stopdomain": 15, "max_allowed_contain": 15, "getmaxallowedcontainersamount": 15, "getcontainersamount": 15, "getavailablecontainersamount": 15, "left": 15, "just": 15, "substract": 15, "liststoredcontain": 15, "enumer": 15, "getstoredcontain": 15, "crate": 15, "setmaxallowedcontain": 15, "addstoredcontain": 15, "becom": 15, "deletestoredcontain": 15, "createcontain": 15, "accesstk": 16, "default_disable_token": 16, "auth_token_db_path": 16, "cursor": 16, "getdatabaseconnect": 16, "sqlite3": 16, "getcursor": 16, "access_token": [16, 17], "auth_token": 16, "everi": [16, 17, 22, 26, 29], "enableentri": 16, "disableentri": 16, "program": [17, 22], "easi": 17, "inter": [17, 22], "clig": 17, "dev": [17, 29], "simpl": [17, 18], "part": 17, "togeth": 17, "singl": 17, "print": [17, 18, 19, 20, 21], "purpos": 17, "exploit": 17, "context": 17, "relat": 17, "arent": 17, "produc": 17, "subcommand": 17, "displai": 17, "errors_record": 17, "errors_list": 17, "look": 17, "successfulli": [17, 26], "checksum": [17, 29], "file_path": 17, "md5": [17, 29], "metadata": 17, "l": 17, "entry_list": 17, "fingerprint": 17, "stub": [18, 20, 21, 22], "serverinterfac": [18, 19, 20, 21, 22], "notify_start": [18, 19, 20, 21], "server_interfac": 19, "notify_request": 19, "request_verb": 19, "on_container_cr": 19, "handle_container_cr": 19, "f": [19, 21, 26], "show": 20, "respond": 20, "pong": 20, "ping": 20, "handle_ping_request": 20, "answer": 20, "Not": 20, "mandatori": 20, "perform": 21, "callback": 21, "faster": 21, "whole": 21, "denied_ip_arrai": 21, "handle_connect": 21, "client_ip": 21, "getpeernam": 21, "notic": 21, "end": [21, 26], "thei": 22, "extern": 22, "itself": 22, "capac": [22, 24], "specificaton": 22, "functionn": 22, "constant": 22, "class": 22, "cryptographi": 22, "sanit": [22, 30], "addit": [22, 26], "come": [22, 24, 26], "accesstokenmanag": 22, "json": [22, 24, 26], "global": 22, "sub": 22, "dl": 22, "regen": 22, "page": [22, 24], "mtu": 22, "temporari": 24, "machin": [24, 29], "anonym": 24, "onlin": 24, "someon": 24, "fulli": 24, "comput": 24, "expos": 24, "less": 24, "danger": 24, "dedic": [24, 29], "terminologi": 24, "alic": 24, "she": 24, "ask": 24, "administ": 24, "her": 24, "secur": [24, 30], "interact": [24, 25, 29], "onc": [24, 29], "previous": [24, 26], "identif": 24, "issu": 24, "solv": 24, "pull": 24, "social": 24, "www": 24, "reddit": 24, "zulipchat": 24, "mail": 24, "proton": 24, "me": 24, "consid": 24, "pgp": 24, "index": 24, "nativ": [25, 26], "member": 25, "receipt": 25, "who": 25, "question": 25, "255": [25, 26], "guarante": 25, "minimum": 25, "usurp": 25, "user_num": [25, 29], "num": [25, 29], "number": [25, 29], "90000": [25, 29], "120": [25, 29], "whose": [25, 29], "wide": 26, "cross": 26, "easili": 26, "manipul": 26, "anyth": 26, "pad": 26, "length": 26, "world": 26, "theoric": 26, "99": 26, "999": 26, "typic": [26, 31], "support": 26, "gather": 26, "reserv": 26, "satisfi": 26, "along": 26, "what": 26, "happen": 26, "wrong": 26, "mean": 26, "sender": 26, "explan": 26, "integr": 26, "cbc": 26, "visual": 26, "bold": 26, "symbol": 26, "b": 26, "connexion": 26, "upon": 26, "scheme": 26, "type": [26, 29], "regex": 26, "z": 26, "check_with": 26, "__check_container_uuid": 26, "__check_client_token": 26, "9a": 26, "container_iso_sha256": 26, "container_usernam": 26, "container_password": 26, "container_listen_port": 26, "za": 26, "_": 26, "f0": 26, "64": 26, "user_": 26, "z0": 26, "min": 26, "max": 26, "base": [27, 28], "orm": 27, "volatil": 27, "websit": [27, 29], "sql": [27, 31], "entryid": [27, 31], "creationtimestamp": [27, 31], "clienttoken": 27, "primari": [27, 31], "written": [27, 31], "let": 28, "physic": 28, "two": 28, "traffic": 28, "abov": 28, "wrapper": 29, "xml": 29, "definit": 29, "kvm": 29, "unit": 29, "mib": 29, "currentmemori": 29, "placement": 29, "static": 29, "arch": 29, "x86_64": 29, "pc": 29, "hvm": 29, "boot": 29, "hd": 29, "cdrom": 29, "acpi": 29, "apic": 29, "vmport": 29, "clock": 29, "offset": 29, "utc": 29, "timer": 29, "rtc": 29, "tickpolici": 29, "catchup": 29, "pit": 29, "delai": 29, "hpet": 29, "present": 29, "pm": 29, "suspend": 29, "mem": 29, "disk": 29, "target": 29, "hda": 29, "bu": 29, "drive": 29, "onboot": 29, "model": 29, "virtio": 29, "memballoon": 29, "pci": 29, "0x0000": 29, "0x00": 29, "slot": 29, "0x07": 29, "0x0": 29, "littl": 29, "reman": 29, "editor": 29, "pre": 29, "comfort": 29, "factori": 29, "repositori": 29, "tree": 29, "md5sum": 29, "sha256sum": 29, "increment": 29, "coordin": 29, "presenc": 29, "ghost": 29, "uuid4": 29, "insid": 29, "bash": 29, "Its": 29, "convent": 29, "80": 29, "task": 29, "newli": 29, "represent": 30, "case": 31, "match": 31, "accesstoken": 31, "NOT": 31, "null": 31}, "objects": {}, "objtypes": {}, "objnames": {}, "titleterms": {"access": [0, 16, 17, 31], "token": [0, 16, 31], "prerequisit": 0, "add": 0, "delet": 0, "enabl": 0, "disabl": 0, "configur": 1, "file": 1, "contain": [2, 4, 19, 25, 28, 29], "iso": [2, 4, 17], "download": 2, "imag": 2, "administr": [3, 29], "guid": 3, "instal": 4, "anweddol": [4, 24], "server": [4, 6, 13, 18, 24, 28], "environ": 4, "setup": 4, "libvirt": [4, 7, 23], "network": [4, 28], "debian": 4, "base": 4, "system": 4, "redhat": 4, "get": [4, 8, 23], "start": [4, 6, 8, 17], "uninstal": 4, "log": 5, "rotat": 5, "usag": 6, "stop": [6, 17], "troubleshoot": [7, 22, 23], "permiss": 7, "deni": 7, "authent": [7, 25], "unavail": 7, "polkit": 7, "agent": 7, "avail": 7, "action": 7, "org": 7, "unix": 7, "manag": [7, 29], "contribut": [8, 24], "issu": 8, "creat": 8, "an": 8, "solv": 8, "make": 8, "chang": 8, "pull": 8, "request": [8, 20, 26], "document": 8, "client": [9, 25], "constant": [9, 10, 11, 12, 13, 14, 15, 16], "class": [9, 10, 11, 13, 15, 16], "clientinst": 9, "definit": [9, 10, 11, 13, 15, 16], "method": [9, 10, 11, 13, 15, 16], "cryptographi": 10, "rsawrapp": 10, "aeswrapp": 10, "databas": [11, 27, 31], "databaseinterfac": 11, "sanit": [12, 26], "function": [12, 14], "serverinterfac": 13, "callback": 13, "event": 13, "util": 14, "virtual": [15, 29], "endpointshellinst": 15, "containerinst": 15, "virtualizationinterfac": 15, "accesstokenmanag": 16, "cli": [17, 22], "json": 17, "output": 17, "global": [17, 24], "structur": 17, "specif": [17, 30], "result": 17, "error": [17, 23, 26], "sub": 17, "command": 17, "restart": 17, "dl": 17, "tk": 17, "regen": 17, "rsa": 17, "basic": 18, "set": 19, "custom": [19, 20], "capac": 19, "handl": [20, 26], "verb": 20, "implement": 21, "ip": 21, "filter": 21, "develop": 22, "section": 22, "import": 22, "note": 22, "exampl": 22, "api": [22, 29], "refer": 22, "core": [22, 30], "featur": [22, 30], "tool": [22, 30], "cannot": 23, "interfac": 23, "mtu": 23, "virbr0": 23, "The": 24, "overview": 24, "effect": 24, "scenario": 24, "content": 24, "link": 24, "indic": 24, "tabl": [24, 27, 31], "session": 25, "credenti": 25, "commun": 26, "format": 26, "respons": 26, "secur": [26, 27, 31], "encrypt": 26, "engin": [27, 31], "represent": [27, 31], "o": 29, "technic": 30}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 57}, "alltitles": {"Access token": [[0, "access-token"], [16, "access-token"], [31, "access-token"]], "Prerequisites": [[0, "prerequisites"]], "Add / delete a token": [[0, "add-delete-a-token"]], "Enable / Disable a token": [[0, "enable-disable-a-token"]], "Configuration file": [[1, "configuration-file"]], "Container ISO": [[2, "container-iso"], [4, "container-iso"]], "Download the ISO image": [[2, "download-the-iso-image"]], "Administration guide": [[3, "administration-guide"]], "Installation": [[4, "installation"], [4, "id1"]], "Anweddol server installation": [[4, "anweddol-server-installation"]], "Environment setup": [[4, "environment-setup"]], "Libvirt": [[4, "libvirt"]], "Setup": [[4, "setup"]], "Networking": [[4, "networking"], [28, "networking"]], "Debian-based systems": [[4, "debian-based-systems"]], "Redhat-based systems": [[4, "redhat-based-systems"]], "Getting started": [[4, "getting-started"], [8, "getting-started"]], "Anweddol server uninstallation": [[4, "anweddol-server-uninstallation"]], "Logging": [[5, "logging"]], "Log rotation": [[5, "log-rotation"]], "Server usage": [[6, "server-usage"]], "Start the server": [[6, "start-the-server"]], "Stop the server": [[6, "stop-the-server"]], "Troubleshooting": [[7, "troubleshooting"], [22, "troubleshooting"], [23, "troubleshooting"]], "[\u2026]Libvirt : Permission denied": [[7, "libvirt-permission-denied"]], "[\u2026] authentication unavailable: no polkit agent available to authenticate action 'org.libvirt.unix.manage'": [[7, "authentication-unavailable-no-polkit-agent-available-to-authenticate-action-org-libvirt-unix-manage"]], "Contribute": [[8, "contribute"]], "Issues": [[8, "issues"]], "Create an issue": [[8, "create-an-issue"]], "Solve / contribute to an issue": [[8, "solve-contribute-to-an-issue"]], "Make changes": [[8, "make-changes"]], "Pull requests": [[8, "pull-requests"]], "Documentation": [[8, "documentation"]], "Client": [[9, "client"]], "Constants": [[9, "constants"], [10, "constants"], [11, "constants"], [12, "constants"], [13, "constants"], [14, "constants"], [15, "constants"], [16, "constants"]], "Classes": [[9, "classes"], [10, "classes"], [11, "classes"], [13, "classes"], [15, "classes"], [16, "classes"]], "ClientInstance": [[9, "clientinstance"]], "Definition": [[9, "definition"], [10, "definition"], [10, "id1"], [11, "definition"], [13, "definition"], [15, "definition"], [15, "id1"], [15, "id3"], [16, "definition"]], "Methods": [[9, "methods"], [10, "methods"], [10, "id2"], [11, "methods"], [13, "methods"], [15, "methods"], [15, "id2"], [15, "id4"], [16, "methods"]], "Cryptography": [[10, "cryptography"]], "RSAWrapper": [[10, "rsawrapper"]], "AESWrapper": [[10, "aeswrapper"]], "Database": [[11, "database"], [27, "database"], [31, "database"]], "DatabaseInterface": [[11, "databaseinterface"]], "Sanitization": [[12, "sanitization"], [26, "sanitization"]], "Functions": [[12, "functions"], [14, "functions"]], "Server": [[13, "server"], [28, "server"]], "ServerInterface": [[13, "serverinterface"]], "Callback events": [[13, "callback-events"]], "Utilities": [[14, "utilities"]], "Virtualization": [[15, "virtualization"], [29, "virtualization"]], "EndpointShellInstance": [[15, "endpointshellinstance"]], "ContainerInstance": [[15, "containerinstance"]], "VirtualizationInterface": [[15, "virtualizationinterface"]], "AccessTokenManager": [[16, "accesstokenmanager"]], "CLI JSON output": [[17, "cli-json-output"]], "Global structure": [[17, "global-structure"]], "Specific result JSON structures": [[17, "specific-result-json-structures"]], "Errors": [[17, "errors"]], "start sub-command": [[17, "start-sub-command"]], "stop sub-command": [[17, "stop-sub-command"]], "restart sub-command": [[17, "restart-sub-command"]], "dl-iso sub-command": [[17, "dl-iso-sub-command"]], "access-tk sub-command": [[17, "access-tk-sub-command"]], "regen-rsa sub-command": [[17, "regen-rsa-sub-command"]], "Basic server": [[18, "basic-server"]], "Set custom container capacity": [[19, "set-custom-container-capacity"]], "Handle a custom request verb": [[20, "handle-a-custom-request-verb"]], "Implement IP filtering": [[21, "implement-ip-filtering"]], "Developer section": [[22, "developer-section"]], "Important note": [[22, "important-note"]], "Examples": [[22, "examples"]], "API references": [[22, "api-references"]], "Core features": [[22, "core-features"], [30, "core-features"]], "Tools features": [[22, "tools-features"], [30, "tools-features"]], "CLI references": [[22, "cli-references"]], "libvirt: error : Cannot get interface MTU on 'virbr0'[\u2026]": [[23, "libvirt-error-cannot-get-interface-mtu-on-virbr0"]], "The Anweddol server": [[24, "the-anweddol-server"]], "Global overview": [[24, "global-overview"]], "Effective scenario": [[24, "effective-scenario"]], "Contents": [[24, "contents"]], "Contribution": [[24, "contribution"]], "Links": [[24, "links"]], "Indices and tables": [[24, "indices-and-tables"]], "Client authentication": [[25, "client-authentication"]], "Session credentials": [[25, "session-credentials"]], "Container credentials": [[25, "container-credentials"]], "Communication": [[26, "communication"]], "Format": [[26, "format"]], "Request format": [[26, "request-format"]], "Response format": [[26, "response-format"]], "Error handling": [[26, "error-handling"]], "Security": [[26, "security"], [27, "security"], [31, "security"]], "Encryption": [[26, "encryption"]], "Engine": [[27, "engine"], [31, "engine"]], "Table representation": [[27, "table-representation"], [31, "table-representation"]], "Container": [[28, "container"]], "API": [[29, "api"]], "Container OS": [[29, "container-os"]], "Management": [[29, "management"]], "Administration": [[29, "administration"]], "Technical specifications": [[30, "technical-specifications"]], "Tokens": [[31, "tokens"]]}, "indexentries": {}}) \ No newline at end of file diff --git a/docs/server/technical_specifications/core/client_authentication.html b/docs/server/technical_specifications/core/client_authentication.html new file mode 100644 index 0000000..ed628de --- /dev/null +++ b/docs/server/technical_specifications/core/client_authentication.html @@ -0,0 +1,152 @@ + + + + + + + + Client authentication — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Client authentication

+
+

Natively, the server handles 2 kinds of credentials :

+
    +
  • The session credentials ;

  • +
  • The container credentials ;

  • +
+
+

Session credentials

+

The session credentials are credentials used to authenticate a client when a DESTROY request is received. +There is 2 affiliated members :

+
    +
  • The container UUID ;

  • +
  • The client token ;

  • +
+

The container UUID is the UUID of the container that the client wants to destroy.

+

The client token is a private token given to the client when a container is created on receipt of a CREATE request, to ensure that the client who wants to destroy the container in question is actually the one who owns it. A token is a 255-character url-safe string, to guarantee minimum usurpability.

+
+
+

Container credentials

+

The container credentials are the SSH credentials to use with the container in order to be able to interact with it. +There is 3 affiliated members :

+
    +
  • The SSH username ;

  • +
  • The SSH password ;

  • +
  • The SSH listen port ;

  • +
+

The SSH username is in the format user_NUM, where NUM is a random number between 10000 and 90000.

+

The SSH password is a 120 character string.

+

The SSH listen port is a random port between 10000 and 15000, whose bindability is checked before assignation.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/core/communication.html b/docs/server/technical_specifications/core/communication.html new file mode 100644 index 0000000..a78222b --- /dev/null +++ b/docs/server/technical_specifications/core/communication.html @@ -0,0 +1,424 @@ + + + + + + + + Communication — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Communication

+
+
+

Format

+

Requests and responses sent between the client and the server are JSON structures. +-> A widely used data format, cross-platform and easily manipulable.

+

Before sending anything, the size of the packet is sent in an 8 byte message, padded with ‘=’ characters :

+ + + + + + + + + + + + + +

Message

Length

Padded message length

"hello world"

11 characters

"11======" (8 characters)

+

Thus, the native theorical maximum packet size is 99 999 999 bytes.

+
+
+

Request format

+

Here is a typical request structure that a server will receive :

+
{
+	"verb": VERB;
+	"parameters": PARAMETERS
+}
+
+
+
    +
  • VERB : Like an HTTP request, the verb depicts the action to execute on the server side. There is 3 natively supported verbs :

    +
      +
    • "CREATE" : Defines the intent to create a new container.

    • +
    • "DESTROY" : Defines the intent to destroy a previously created container.

    • +
    • "STAT" : Defines the intent to gather information about a server runtime.

    • +
    +
  • +
  • PARAMETERS : This is the section reserved for any kind of parameters used to provide additional information in a request.

  • +
+
+

Only the "DESTROY" request requires some parameters to authenticate the client (see the Authentication section to learn more).

+
+
+
+

Response format

+

Here is a typical response structure that a client will receive :

+
{
+	"success": SUCCESS;
+	"message": MESSAGE;
+	"data": DATA
+}
+
+
+
    +
  • SUCCESS : The success is a boolean value that defines the current state of the request on the server.

    +
      +
    • True : The request was successfully satisfied.

    • +
    • False : There was an error during the processing of the request.

    • +
    +
  • +
  • MESSAGE : The additional information coming along with the success of the response. +It can be anything that explains what happened on the server side (see the ‘Error handling’ point below).

  • +
  • DATA : The data section, reserved for returned parameters.

  • +
+
+
+

Error handling

+

When an error occurs during the processing of a request, a message is set in the response explaining what’s wrong.

+

Here is a non-exhaustive list of status codes and their messages :

+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Message

Meaning

"OK"

The request was successfully satisfied

"Bad authentication"

The sender specified invalid credentials

"Bad request"

The previous request was malformed

"Refused request"

The request was refused

"Unavailable"

The server is currently unavailable

"Internal error"

The server is experiencing an internal error

+

Note that messages depicting an error may come with an additional explanation of why the error occurred :

+
"Bad request (reason : Unsupported or unknown verb)"
+
+
+
+
+

Security

+
+

Encryption

+

For security and integrity reasons, requests and responses are encrypted in AES 256 CBC. +Each AES key and Initialization Vectors are different for every client connection session.

+

RSA keys length is 4096 bytes by default. The RSA implementation is used to send the connection session AES key to the client securely.

+

Here is a visual example of how the keys are exchanged with a client :

+
+

Bold text mean RSA encrypted text

+
+
+

‘>’/’<’ symbol means ‘send’ and ‘o’ symbol means ‘receive’

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

A

packet content

B

>

connexion

o

>

A RSA public key

o

o

validation

<

o

B RSA public Key

<

>

validation

o

>

A AES Key

o

o

validation

<

o

B AES Key

<

>

validation

o

+
+
+

Sanitization

+

Requests and responses are sanitized upon sending and receiving at each end. +Here is the raw cerberus validation scheme used to verify the format and content :

+

Requests

+
{
+	"verb": {
+		"type": "string",
+		"regex": r"^[A-Z]{1,}$",
+		"required": True,
+	},
+	"parameters": {
+		"type": "dict",
+		"required": True,
+		"schema": {
+			"container_uuid": {
+				"type": "string",
+				"required": False,
+				"check_with": __check_container_uuid,
+				"dependencies": ["client_token"]
+			},
+			"client_token": {
+				"type": "string",
+				"required": False,
+				"check_with": __check_client_token,
+				"dependencies": ["container_uuid"]
+			}
+		}
+	}
+}
+
+
+

Responses

+
{
+	"success": {
+		"type": "boolean",
+		"required": True
+	},
+	"message": {
+		"type": "string",
+		"required": True
+	},
+	"data": {
+		"type": "dict",
+		"required": True,
+		"schema": {
+			"container_uuid": {
+				"type": "string",
+				"regex": r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
+				"required": False,
+				"dependencies": [
+					"client_token",
+					"container_iso_sha256",
+					"container_username",
+					"container_password",
+					"container_listen_port"
+				]
+			},
+			"client_token": {
+				"type": "string",
+				"regex": r"^[0-9a-zA-Z-_]{255}$",
+				"required": False,
+				"dependencies": [
+					"container_uuid",
+					"container_iso_sha256",
+					"container_username",
+					"container_password",
+					"container_listen_port"
+				]
+			},
+			"container_iso_sha256": {
+				"type": "string",
+				"regex": r"^[a-f0-9]{64}$",
+				"required": False,
+				"dependencies": [
+					"container_uuid",
+					"client_token",
+					"container_username",
+					"container_password",
+					"container_listen_port"
+				]
+			},
+			"container_username": {
+				"type": "string",
+				"regex": r"^user_[0-9]{5}$",
+				"required": False,
+				"dependencies": [
+					"container_uuid",
+					"client_token",
+					"container_iso_sha256",
+					"container_password",
+					"container_listen_port"
+				]
+			},
+			"container_password": {
+				"type": "string",
+				"regex": r"^[a-zA-Z0-9]{1,}$",
+				"required": False,
+				"dependencies": [
+					"container_uuid",
+					"client_token",
+					"container_iso_sha256",
+					"container_username",
+					"container_listen_port"
+				]
+			},
+			"container_listen_port": {
+				"type": "integer",
+				"required": False,
+				"min": 1,
+				"max": 65535,
+				"dependencies": [
+					"container_uuid",
+					"client_token",
+					"container_iso_sha256",
+					"container_username",
+					"container_password"
+				]
+			},
+			"uptime": {
+				"type": "integer",
+				"required": False,
+				"min": 0,
+				"dependencies": ["available"]
+			},
+			"available": {
+				"type": "integer",
+				"required": False,
+				"min": 0,
+				"dependencies": ["uptime"]
+			}
+		}
+	}
+}
+
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/core/database.html b/docs/server/technical_specifications/core/database.html new file mode 100644 index 0000000..88bf4ab --- /dev/null +++ b/docs/server/technical_specifications/core/database.html @@ -0,0 +1,158 @@ + + + + + + + + Database — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Database

+
+
+

Engine

+

The server is using a sqlite-based SQLAlchemy ORM memory database engine to ensure its content volatility. +See the SQLAlchemy website to learn more.

+
+
+

Table representation

+

Here is a representation of the SQL table used during the server run time :

+ + + + + + + + + + + + + + + +

EntryID

CreationTimestamp

ContainerUUID

ClientToken

Integer primary key

Integer

String

String

+
    +
  • EntryID : Store the entry ID

  • +
  • CreationTimestamp : Store the creation timestamp of the entry

  • +
  • ContainerUUID : Store the container UUID

  • +
  • ClientToken : Store the affiliated client token

  • +
+
+
+

Security

+

The data written in the ContainerUUID and the ClientToken columns are hashed with SHA256.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/core/networking.html b/docs/server/technical_specifications/core/networking.html new file mode 100644 index 0000000..e757298 --- /dev/null +++ b/docs/server/technical_specifications/core/networking.html @@ -0,0 +1,143 @@ + + + + + + + + Networking — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Networking

+
+
+

Server

+

In order to let clients and containers communicate, the server needs a specific interface.

+

A bridge interface anwdlbr0 must be created on the server, with its current physical network interface configured as a slave on it. Containers will connect via TAP on this bridge, ensuring full-bridged networking for the container.

+
+

Read the ‘Administration guide’ for more info.

+
+
+
+

Container

+

Each container have two network interfaces :

+
    +
  • A NAT interface ;

  • +
  • A bridged interface ;

  • +
+

-> The NAT interface is used for server administration.
+It is an interface based on the virbr0 libvirt virtual bridge.

+

-> The bridged interface is used to receive client traffic. +It is based on the manually created bridge on the system (see above), thus providing full-bridged networking.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/core/virtualization.html b/docs/server/technical_specifications/core/virtualization.html new file mode 100644 index 0000000..1a960a1 --- /dev/null +++ b/docs/server/technical_specifications/core/virtualization.html @@ -0,0 +1,231 @@ + + + + + + + + Virtualization — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Virtualization

+
+
+

API

+

The Anweddol server uses the Libvirt API to manage containers. +You’ll retrieve the full documentation on the libvirt python binding documentation website.

+

A container is a libvirt domain wrapper utility running with a live debian image (see below).

+

The domain XML used for container domain definition is :

+
<domain type='kvm'>
+	<name>container_uuid</name>
+	<memory unit='MiB'>memory</memory>
+	<currentMemory unit='MiB'>memory</currentMemory>
+	<vcpu placement='static'>vcpus/vcpu>
+	<uuid>container_uuid</uuid>
+	<os>
+		<type arch='x86_64' machine='pc'>hvm</type>
+		<boot dev='hd'/>
+		<boot dev='cdrom'/>
+	</os>
+	<features>
+		<acpi/>
+		<apic/>
+		<vmport state='off'/>
+	</features>
+	<clock offset='utc'>
+		<timer name='rtc' tickpolicy='catchup'/>
+		<timer name='pit' tickpolicy='delay'/>
+		<timer name='hpet' present='no'/>
+	</clock>
+	<pm>
+		<suspend-to-mem enabled='yes'/>
+		<suspend-to-disk enabled='yes'/>
+	</pm>
+	<devices>
+		<disk type='file' device='cdrom'>
+			<driver name='qemu' type='raw'/>
+			<source file='iso_path'/>
+			<target dev='hda' bus='ide'/>
+			<address type='drive' controller='0' bus='0' target='0' unit='0'/>
+		</disk>
+		<interface type='bridge'>
+	        <start mode='onboot'/>
+	        <source bridge='nat_interface_name'/> 
+	        <model type='virtio'/>
+	    </interface>
+        <interface type='bridge'>
+            <start mode='onboot'/>
+            <source bridge='bridge_interface_name'/>
+            <model type='virtio'/>
+        </interface>
+        <memballoon model='virtio'>
+			<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
+		</memballoon>
+	</devices>
+</domain>
+
+
+
    +
  • container_uuid : The container UUID

  • +
  • memory : The container domain memory amount

  • +
  • vcpus : The container domain virtual CPUs amount

  • +
  • iso_path : The ISO file path that will be used by the container domain

  • +
  • nat_interface_name : The NAT interface name that will be used by the container domain

  • +
  • bridge_interface_name : The bridge interface name that will be used by the container domain

  • +
+
+
+

Container OS

+

In order to provice a valid service, containers domains needs to run with a specific ISO image.

+

The operating system running on containers is a custom live Debian to ensure as little data remanence as possible on the virtual domain : All basic tools like text editors, network interactions, development environment are already pre-installed to bring comfortable usage for the client.

+

See the ISO factory repository to get the script that creates these ISO images. +You can retrieve a copy of this container ISO on the official mirror.

+

This mirror URL contains this file tree :

+
.
+├── anweddol_container.iso
+├── md5sum.txt
+├── sha256sum.txt
+└── version.txt
+
+
+
    +
  • anweddol_container.iso is the actual container ISO image.

  • +
  • md5sum.txt contains the MD5 checksum of anweddol_container.iso

  • +
  • sha256sum.txt contains the SHA256 checksum of anweddol_container.iso

  • +
  • version.txt is the version of the ISO. It contains an integer which is incremented by 1 each updates.

  • +
+
+
+

Management

+

During a server runtime, running container coordinates created for clients are stored in memory to manage them.

+

If a container is shut down, a routine will detect that its domain is not active, and will delete the container’s presence in memory and database to prevent ghosted credentials.

+

Containers are identified with an UUID4 format string, and their libvirt domains UUID are the same one.

+
+
+

Administration

+

When a container is started, the server first waits for the network to be available inside. +Then, the server generates new user coordinates and a new listen port that the container will use for client service.

+

On every container a specific user is defined called endpoint.

+

This is a user dedicated to container administration that the server will interact via SSH to administrate it (it is actually calling a bash script inside that you can retrieve on the ISO factory github repository). Its default password is endpoint, and the container default SSH server is listening on the conventional port (80).

+

The bash script executes 3 tasks :

+
    +
  • Create the new specified user that the client will use ;

  • +
  • Set up the new SSH listen port ;

  • +
  • Disabling the endpoint user once its task is executed ;

  • +
+

The username is in the format user_NUM, where NUM is a random number between 10000 and 90000. +The user password is a 120 character string. +The new listen port is a random port between 10000 and 15000, whose bindability is checked before assignation.

+

When the script will be called, the container will only accept SSH connections with the newly created user, since the endpoint user will be disabled in the process.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/index.html b/docs/server/technical_specifications/index.html new file mode 100644 index 0000000..6fc0dd6 --- /dev/null +++ b/docs/server/technical_specifications/index.html @@ -0,0 +1,193 @@ + + + + + + + + Technical specifications — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Technical specifications

+
+

Hello and welcome to the Anweddol server technical specifications.

+

You’ll retrieve documentation about how the Anweddol server and its affiliated features works.

+
+

Core features

+ + + + + +
+
+

Tools features

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/server/technical_specifications/tools/access_token.html b/docs/server/technical_specifications/tools/access_token.html new file mode 100644 index 0000000..b03cfac --- /dev/null +++ b/docs/server/technical_specifications/tools/access_token.html @@ -0,0 +1,167 @@ + + + + + + + + Access token — The Anweddol server beta-1.2.8 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Access token

+
+

An access token system is provided in the tools features. +It is used to restrict users of the service by providing tokens to authenticate them before processing the request.

+

A typical use case is when a client sends a request on a server, it must send an access token in the request parameters that must match one of the tokens in the database in order to be processed by the server.

+
+

Tokens

+

Tokens used are 124 characters url-safe strings.

+
+
+

Database

+
+

Engine

+

This feature is using a SQLite file to store data.

+
+
+

Table representation

+

Here is a representation of the used SQL table :

+ + + + + + + + + + + + + + + +

EntryID

CreationTimestamp

AccessToken

Enabled

INTEGER NOT NULL PRIMARY KEY

INTEGER NOT NULL

TEXT NOT NULL

INTEGER NOT NULL

+
    +
  • EntryID : Identifies the row

  • +
  • CreationTimestamp : Store the row creation timestamp

  • +
  • AccessToken : Store the affiliated access token

  • +
  • Enabled : Store a boolean value (1 / 0) depicting if the row must be used or ignored

  • +
+
+
+

Security

+

Any tokens written in the AccessToken column are hashed with SHA256.

+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file