55 Commits
1.3 ... 1.3.5

Author SHA1 Message Date
Oscar Krause
e1bbd42b50 Merge branch 'dev' into 'main'
1.3.5

See merge request oscar.krause/fastapi-dls!23
2023-02-13 17:30:11 +01:00
Oscar Krause
c1d541f7c6 bump version to 1.3.5 2023-02-13 08:09:37 +01:00
Oscar Krause
4b58fe6e20 added openSUSE Leap 15.4 support 2023-02-13 08:09:21 +01:00
Oscar Krause
b36b49df11 fixed missing mkdir for config file on manual installation method 2023-02-01 07:55:12 +01:00
Oscar Krause
a42b1c8cfb added note to be logged in as root using manual install method (git) 2023-01-30 12:34:46 +01:00
Oscar Krause
59152f95e6 fixed - The `declarative_base()` function is now available as sqlalchemy.orm.declarative_base() 2023-01-30 10:23:09 +01:00
Oscar Krause
62d347510d fixed - sqlalchemy.exc.ArgumentError: Textual SQL expression '\nCREATE TABLE origin (\n\to...' should be explicitly declared as text('\nCREATE TABLE origin (\n\to...') 2023-01-30 10:22:18 +01:00
Oscar Krause
f540c4b25b requirements.txt updated 2023-01-30 09:19:03 +01:00
Oscar Krause
70212e0edd improved docker-compose examples 2023-01-30 09:18:57 +01:00
Oscar Krause
616e8fba5e README - improvements 2023-01-30 08:37:34 +01:00
Oscar Krause
b905ab9dd9 Merge branch 'dev' into 'main'
1.3.4

See merge request oscar.krause/fastapi-dls!22
2023-01-26 07:56:34 +01:00
Oscar Krause
9edc93653e bump version to 1.3.4 2023-01-26 07:38:48 +01:00
Oscar Krause
f30e9237a5 requirements.txt updated 2023-01-26 07:18:01 +01:00
Oscar Krause
f12dc28c42 fixed overriding config file on update / reinstall 2023-01-26 07:17:16 +01:00
Oscar Krause
02276d5440 disabled openapi endpoints 2023-01-23 07:33:54 +01:00
Oscar Krause
9ebff8d6ca typos 2023-01-23 07:29:13 +01:00
Oscar Krause
48eb6d6c64 typos 2023-01-23 07:23:42 +01:00
Oscar Krause
f7ef8d76b6 fixed Origin.delete() 2023-01-23 07:12:02 +01:00
Oscar Krause
bed24b56ce styling 2023-01-19 08:26:35 +01:00
Oscar Krause
95427d430e added startup script 2023-01-19 07:26:22 +01:00
Oscar Krause
c3ea0aa48c added variable for client-token-expire-delta 2023-01-19 07:26:07 +01:00
Oscar Krause
91be7b226c added some comments for default values 2023-01-19 07:25:44 +01:00
Oscar Krause
7045692958 added official links 2023-01-19 07:25:24 +01:00
Oscar Krause
38177fa259 styling 2023-01-18 14:29:48 +01:00
Oscar Krause
9411759f6d added system requirements and preparements 2023-01-18 14:23:34 +01:00
Oscar Krause
48c37987b2 fixed logging and added current timezone info 2023-01-18 14:23:25 +01:00
Oscar Krause
e3745d7fa8 Merge branch 'dev' into 'main'
1.3.3

See merge request oscar.krause/fastapi-dls!21
2023-01-18 08:13:42 +01:00
Oscar Krause
5bb8f17679 improvements 2023-01-18 08:07:55 +01:00
Oscar Krause
de17b0f1b5 fixes 2023-01-18 08:03:02 +01:00
Oscar Krause
0ab5969d3a fixes 2023-01-18 06:56:16 +01:00
Oscar Krause
059a51fe74 refactored commands 2023-01-17 17:25:48 +01:00
Oscar Krause
bf858b38f4 fixes 2023-01-17 17:09:13 +01:00
Oscar Krause
f60f08d543 run powershell as administrator 2023-01-17 16:57:15 +01:00
Oscar Krause
b2e6fab294 fixes 2023-01-17 16:49:15 +01:00
Oscar Krause
b09bb091a5 bump version to 1.3.3 2023-01-17 16:29:32 +01:00
Oscar Krause
651af4cc82 fixed client-token url and added wget als alternative to curl 2023-01-17 16:29:21 +01:00
Oscar Krause
70f7d3f483 mark Let's Encrypt section as optional 2023-01-17 15:36:38 +01:00
Oscar Krause
1e4070a1ba added remove "/usr/share/fastapi-dls" to "postrm" 2023-01-17 14:57:54 +01:00
Oscar Krause
d69d833923 migrated "[[ ]]" if statements to "[ ]" 2023-01-17 14:57:39 +01:00
Oscar Krause
7ef071f92b removed fastapi-dls.service from conffiles 2023-01-17 14:57:09 +01:00
Oscar Krause
3c19fc9d5b implemented "lease_renewal" attribute as calculated value within what period of time the license must be renewed 2023-01-17 11:49:56 +01:00
Oscar Krause
164b5ebc44 Merge branch 'dev' into 'main'
1.3.2

See merge request oscar.krause/fastapi-dls!20
2023-01-17 11:36:23 +01:00
Oscar Krause
742fa07ed4 bump version to 1.3.2 2023-01-17 11:18:25 +01:00
Oscar Krause
a758d93970 main.py - fixed empty lease origin response 2023-01-17 11:18:07 +01:00
Oscar Krause
70250f1fca Merge branch 'dev' into 'main'
1.3.1

See merge request oscar.krause/fastapi-dls!19
2023-01-16 13:08:57 +01:00
Oscar Krause
a65687a082 bump version to 1.3.1 2023-01-16 10:34:20 +01:00
Oscar Krause
3e445c80aa fixes 2023-01-16 10:33:52 +01:00
Oscar Krause
20cc984799 FAQ.md 2023-01-16 10:30:55 +01:00
Oscar Krause
3495cc3af5 typos 2023-01-16 10:30:40 +01:00
Oscar Krause
ed13577e82 Dockerfile - updated to python 3.11 2023-01-16 10:30:21 +01:00
Oscar Krause
ca8a9df54c requirements.txt updated 2023-01-16 10:24:08 +01:00
Oscar Krause
5425eec545 .gitlab-ci.yml simplified 2023-01-16 10:23:58 +01:00
Oscar Krause
2f3c7d5433 Merge branch 'main' into dev 2023-01-16 07:00:20 +01:00
Oscar Krause
b551b0e7f9 README.md - added sunsupoorted ubuntu version 2023-01-15 19:47:50 +01:00
Oscar Krause
50dea9ac4e fixes 2023-01-05 14:08:08 +01:00
14 changed files with 311 additions and 77 deletions

View File

@@ -1,2 +1 @@
/etc/fastapi-dls/env
/etc/systemd/system/fastapi-dls.service

View File

@@ -3,7 +3,7 @@
WORKING_DIR=/usr/share/fastapi-dls
CONFIG_DIR=/etc/fastapi-dls
if [[ ! -f $CONFIG_DIR/instance.private.pem ]]; then
if [ ! -f $CONFIG_DIR/instance.private.pem ]; then
echo "> Create dls-instance keypair ..."
openssl genrsa -out $CONFIG_DIR/instance.private.pem 2048
openssl rsa -in $CONFIG_DIR/instance.private.pem -outform PEM -pubout -out $CONFIG_DIR/instance.public.pem
@@ -12,8 +12,8 @@ else
fi
while true; do
[[ -f $CONFIG_DIR/webserver.key ]] && default_answer="N" || default_answer="Y"
[[ $default_answer == "Y" ]] && V="Y/n" || V="y/N"
[ -f $CONFIG_DIR/webserver.key ] && default_answer="N" || default_answer="Y"
[ $default_answer == "Y" ] && V="Y/n" || V="y/N"
read -p "> Do you wish to create self-signed webserver certificate? [${V}]" yn
yn=${yn:-$default_answer} # ${parameter:-word} If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
case $yn in
@@ -27,7 +27,7 @@ while true; do
esac
done
if [[ -f $CONFIG_DIR/webserver.key ]]; then
if [ -f $CONFIG_DIR/webserver.key ]; then
echo "> Starting service ..."
systemctl start fastapi-dls.service

View File

@@ -1,8 +1,9 @@
#!/bin/bash
if [[ -f /etc/systemd/system/fastapi-dls.service ]]; then
echo "> Removing service file."
rm /etc/systemd/system/fastapi-dls.service
fi
# is removed automatically
#if [ "$1" = purge ] && [ -d /usr/share/fastapi-dls ]; then
# echo "> Removing app."
# rm -r /usr/share/fastapi-dls
#fi
# todo
echo -e "> Done."

View File

@@ -1,5 +1,3 @@
#!/bin/bash
echo -e "> Starting uninstallation of 'fastapi-dls'!"
# todo

View File

@@ -11,6 +11,7 @@ license=('MIT')
depends=('python' 'python-jose' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python-pycryptodome' 'uvicorn' 'python-markdown' 'openssl')
provider=("$pkgname")
install="$pkgname.install"
backup=('etc/default/fastapi-dls')
source=('git+file:///builds/oscar.krause/fastapi-dls' # https://gitea.publichub.eu/oscar.krause/fastapi-dls.git
"$pkgname.default"
"$pkgname.service"

View File

@@ -97,7 +97,7 @@ build:pacman:
- "*.pkg.tar.zst"
test:
image: python:3.10-slim-bullseye
image: python:3.11-slim-bullseye
stage: test
rules:
- if: $CI_COMMIT_BRANCH
@@ -113,6 +113,9 @@ test:
- cd test
script:
- pytest main.py
artifacts:
reports:
dotenv: version.env
.test:linux:
stage: test
@@ -271,24 +274,11 @@ deploy:pacman:
- 'echo "EXPORT_NAME: ${EXPORT_NAME}"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file ${EXPORT_NAME} "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/${EXPORT_NAME}"'
release:prepare:
stage: .pre
rules:
- if: $CI_COMMIT_TAG
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- source version.env
- echo $VERSION
artifacts:
reports:
dotenv: version.env
release:
image: registry.gitlab.com/gitlab-org/release-cli:latest
stage: .post
needs:
- job: release:prepare
- job: test
artifacts: true
rules:
- if: $CI_COMMIT_TAG
@@ -297,7 +287,7 @@ release:
script:
- echo "Running release-job for $VERSION"
release:
name: $CI_PROJECT_TITLE $version
name: $CI_PROJECT_TITLE $VERSION
description: Release of $CI_PROJECT_TITLE version $VERSION
tag_name: $VERSION
ref: $CI_COMMIT_SHA

View File

@@ -1,4 +1,4 @@
FROM python:3.10-alpine
FROM python:3.11-alpine
COPY requirements.txt /tmp/requirements.txt

17
FAQ.md Normal file
View File

@@ -0,0 +1,17 @@
# FAQ
## `Failed to acquire license from <ip> (Info: <license> - Error: The allowed time to process response has expired)`
- Did your timezone settings are correct on fastapi-dls **and your guest**?
- Did you download the client-token more than an hour ago?
Please download a new client-token. The guest have to register within an hour after client-token was created.
## `jose.exceptions.JWTError: Signature verification failed.`
- Did you recreated `instance.public.pem` / `instance.private.pem`?
Then you have to download a **new** client-token on each of your guests.

229
README.md
View File

@@ -7,11 +7,31 @@ Compatibility tested with official DLS 2.0.1.
This service can be used without internet connection.
Only the clients need a connection to this service on configured port.
**Official Links**
- https://git.collinwebdesigns.de/oscar.krause/fastapi-dls
- https://gitea.publichub.eu/oscar.krause/fastapi-dls
- Docker Image `collinwebdesigns/fastapi-dls:latest`
*All other repositories are forks! (which is no bad - just for information and bug reports)*
---
[[_TOC_]]
# Setup (Service)
**System requirements**
- 256mb ram
- 4gb hdd
Tested with Ubuntu 22.10 (from Proxmox templates), actually its consuming 100mb ram and 750mb hdd.
**Prepare your system**
- Make sure your timezone is set correct on you fastapi-dls server and your client
## Docker
Docker-Images are available here:
@@ -49,10 +69,12 @@ Goto [`docker-compose.yml`](docker-compose.yml) for more advanced example (with
version: '3.9'
x-dls-variables: &dls-variables
TZ: Europe/Berlin # REQUIRED, set your timezone correctly on fastapi-dls AND YOUR CLIENTS !!!
DLS_URL: localhost # REQUIRED, change to your ip or hostname
DLS_PORT: 443
LEASE_EXPIRE_DAYS: 90
LEASE_EXPIRE_DAYS: 90 # 90 days is maximum
DATABASE: sqlite:////app/database/db.sqlite
DEBUG: false
services:
dls:
@@ -65,15 +87,22 @@ services:
volumes:
- /opt/docker/fastapi-dls/cert:/app/cert
- dls-db:/app/database
logging: # optional, for those who do not need logs
driver: "json-file"
options:
max-file: 5
max-size: 10m
volumes:
dls-db:
```
## Debian/Ubuntu (manual method using `git clone`)
## Debian/Ubuntu (manual method using `git clone` and python virtual environment)
Tested on `Debian 11 (bullseye)`, Ubuntu may also work.
**Make sure you are logged in as root.**
**Install requirements**
```shell
@@ -98,7 +127,7 @@ chown -R www-data:www-data $WORKING_DIR
```shell
WORKING_DIR=/opt/fastapi-dls/app/cert
mkdir $WORKING_DIR
mkdir -p $WORKING_DIR
cd $WORKING_DIR
# create instance private and public key for singing JWT's
openssl genrsa -out $WORKING_DIR/instance.private.pem 2048
@@ -115,11 +144,14 @@ This is only to test whether the service starts successfully.
```shell
cd /opt/fastapi-dls/app
su - www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app"
# or
sudo -u www-data -c "/opt/fastapi-dls/venv/bin/uvicorn main:app --app-dir=/opt/fastapi-dls/app"
```
**Create config file**
```shell
mkdir /etc/fastapi-dls
cat <<EOF >/etc/fastapi-dls/env
DLS_URL=127.0.0.1
DLS_PORT=443
@@ -164,6 +196,108 @@ EOF
Now you have to run `systemctl daemon-reload`. After that you can start service
with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
## openSUSE Leap (manual method using `git clone` and python virtual environment)
Tested on `openSUSE Leap 15.4`, openSUSE Tumbleweed may also work.
**Install requirements**
```shell
zypper in -y python310 python3-virtualenv python3-pip
```
**Install FastAPI-DLS**
```shell
BASE_DIR=/opt/fastapi-dls
SERVICE_USER=dls
mkdir -p ${BASE_DIR}
cd ${BASE_DIR}
git clone https://git.collinwebdesigns.de/oscar.krause/fastapi-dls .
python3.10 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
deactivate
useradd -r ${SERVICE_USER} -M -d /opt/fastapi-dls
chown -R ${SERVICE_USER} ${BASE_DIR}
```
**Create keypair and webserver certificate**
```shell
CERT_DIR=${BASE_DIR}/app/cert
SERVICE_USER=dls
mkdir ${CERT_DIR}
cd ${CERT_DIR}
# create instance private and public key for singing JWT's
openssl genrsa -out ${CERT_DIR}/instance.private.pem 2048
openssl rsa -in ${CERT_DIR}/instance.private.pem -outform PEM -pubout -out ${CERT_DIR}/instance.public.pem
# create ssl certificate for integrated webserver (uvicorn) - because clients rely on ssl
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ${CERT_DIR}/webserver.key -out ${CERT_DIR}/webserver.crt
chown -R ${SERVICE_USER} ${CERT_DIR}
```
**Test Service**
This is only to test whether the service starts successfully.
```shell
BASE_DIR=/opt/fastapi-dls
SERVICE_USER=dls
cd ${BASE_DIR}
su - ${SERVICE_USER} -c "${BASE_DIR}/venv/bin/uvicorn main:app --app-dir=${BASE_DIR}/app"
```
**Create config file**
```shell
BASE_DIR=/opt/fastapi-dls
cat <<EOF >/etc/fastapi-dls/env
# Adjust DSL_URL as needed (accessing from LAN won't work with 127.0.0.1)
DLS_URL=127.0.0.1
DLS_PORT=443
LEASE_EXPIRE_DAYS=90
DATABASE=sqlite:///${BASE_DIR}/app/db.sqlite
EOF
```
**Create service**
```shell
BASE_DIR=/opt/fastapi-dls
SERVICE_USER=dls
cat <<EOF >/etc/systemd/system/fastapi-dls.service
[Unit]
Description=Service for fastapi-dls vGPU licensing service
After=network.target
[Service]
User=${SERVICE_USER}
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=${BASE_DIR}/app
EnvironmentFile=/etc/fastapi-dls/env
ExecStart=${BASE_DIR}/venv/bin/uvicorn main:app \\
--env-file /etc/fastapi-dls/env \\
--host \$DLS_URL --port \$DLS_PORT \\
--app-dir ${BASE_DIR}/app \\
--ssl-keyfile ${BASE_DIR}/app/cert/webserver.key \\
--ssl-certfile ${BASE_DIR}/app/cert/webserver.crt \\
--proxy-headers
Restart=always
KillSignal=SIGQUIT
Type=simple
NotifyAccess=all
[Install]
WantedBy=multi-user.target
EOF
```
Now you have to run `systemctl daemon-reload`. After that you can start service
with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
## Debian/Ubuntu (using `dpkg`)
Packages are available here:
@@ -175,6 +309,11 @@ Successful tested with:
- Debian 12 (Bookworm) (works but not recommended because it is currently in *testing* state)
- Ubuntu 22.10 (Kinetic Kudu)
Not working with:
- Debian 11 (Bullseye) and lower (missing `python-jose` dependency)
- Ubuntu 22.04 (Jammy Jellyfish) (not supported as for 15.01.2023 due to [fastapi - uvicorn version missmatch](https://bugs.launchpad.net/ubuntu/+source/fastapi/+bug/1970557))
**Run this on your server instance**
First go to [GitLab-Registry](https://git.collinwebdesigns.de/oscar.krause/fastapi-dls/-/packages) and select your
@@ -201,13 +340,17 @@ Packages are available here:
```shell
pacman -Sy
FILENAME=/opt/fastapi-dls.pkg.tar.zst
url -o $FILENAME <download-url>
curl -o $FILENAME <download-url>
# or
wget -O $FILENAME <download-url>
pacman -U --noconfirm fastapi-dls.pkg.tar.zst
```
Start with `systemctl start fastapi-dls.service` and enable autostart with `systemctl enable fastapi-dls.service`.
## Let's Encrypt Certificate
## Let's Encrypt Certificate (optional)
If you're using installation via docker, you can use `traefik`. Please refer to their documentation.
@@ -261,26 +404,67 @@ Successfully tested with this package versions:
## Linux
Download *client-token* and place it into `/etc/nvidia/ClientConfigToken`:
```shell
curl --insecure -L -X GET https://<dls-hostname-or-ip>/-/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok
# or
wget --no-check-certificate -O /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok https://<dls-hostname-or-ip>/-/client-token
```
Restart `nvidia-gridd` service:
```shell
curl --insecure -L -X GET https://<dls-hostname-or-ip>/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok
service nvidia-gridd restart
```
Check licensing status:
```shell
nvidia-smi -q | grep "License"
```
## Windows
Output should be something like:
Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`.
Now restart `NvContainerLocalSystem` service.
**Power-Shell**
```Shell
curl.exe --insecure -L -X GET https://<dls-hostname-or-ip>/client-token -o "C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken\client_configuration_token_$($(Get-Date).tostring('dd-MM-yy-hh-mm-ss')).tok"
Restart-Service NVDisplay.ContainerLocalSystem
'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License"
```text
vGPU Software Licensed Product
License Status : Licensed (Expiry: YYYY-M-DD hh:mm:ss GMT)
```
## Endpoints
Done. For more information check [troubleshoot section](#troubleshoot).
## Windows
**Power-Shell** (run as administrator!)
Download *client-token* and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`:
```shell
curl.exe --insecure -L -X GET https://<dls-hostname-or-ip>/-/client-token -o "C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken\client_configuration_token_$($(Get-Date).tostring('dd-MM-yy-hh-mm-ss')).tok"
```
Restart `NvContainerLocalSystem` service:
```Shell
Restart-Service NVDisplay.ContainerLocalSystem
```
Check licensing status:
```shell
& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe' -q | Select-String "License"
```
Output should be something like:
```text
vGPU Software Licensed Product
License Status : Licensed (Expiry: YYYY-M-DD hh:mm:ss GMT)
```
Done. For more information check [troubleshoot section](#troubleshoot).
# Endpoints
### `GET /`
@@ -298,10 +482,6 @@ Shows current runtime environment variables and their values.
HTML rendered README.md.
### `GET /-/docs`, `GET /-/redoc`
OpenAPI specifications rendered from `GET /-/openapi.json`.
### `GET /-/manage`
Shows a very basic UI to delete origins or leases.
@@ -486,5 +666,4 @@ The error message can safely be ignored (since we have no license limitation :P)
Thanks to vGPU community and all who uses this project and report bugs.
Special thanks to @samicrusader who created build file for ArchLinux.
Special thanks to @samicrusader who created build file for ArchLinux and @cyrus who wrote the section for openSUSE.

View File

@@ -9,7 +9,7 @@ from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.requests import Request
from json import loads as json_loads
from datetime import datetime
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from calendar import timegm
from jose import jws, jwk, jwt, JWTError
@@ -22,12 +22,13 @@ from sqlalchemy.orm import sessionmaker
from util import load_key, load_file
from orm import Origin, Lease, init as db_init, migrate
logger = logging.getLogger()
load_dotenv('../version.env')
TZ = datetime.now().astimezone().tzinfo
VERSION, COMMIT, DEBUG = env('VERSION', 'unknown'), env('COMMIT', 'unknown'), bool(env('DEBUG', False))
config = dict(openapi_url='/-/openapi.json', docs_url='/-/docs', redoc_url='/-/redoc')
config = dict(openapi_url=None, docs_url=None, redoc_url=None) # dict(openapi_url='/-/openapi.json', docs_url='/-/docs', redoc_url='/-/redoc')
app = FastAPI(title='FastAPI-DLS', description='Minimal Delegated License Service (DLS).', version=VERSION, **config)
db = create_engine(str(env('DATABASE', 'sqlite:///db.sqlite')))
db_init(db), migrate(db)
@@ -43,6 +44,8 @@ INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__),
TOKEN_EXPIRE_DELTA = relativedelta(days=int(env('TOKEN_EXPIRE_DAYS', 1)), hours=int(env('TOKEN_EXPIRE_HOURS', 0)))
LEASE_EXPIRE_DELTA = relativedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=int(env('LEASE_EXPIRE_HOURS', 0)))
LEASE_RENEWAL_PERIOD = float(env('LEASE_RENEWAL_PERIOD', 0.15))
LEASE_RENEWAL_DELTA = timedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=int(env('LEASE_EXPIRE_HOURS', 0)))
CLIENT_TOKEN_EXPIRE_DELTA = relativedelta(years=12)
CORS_ORIGINS = str(env('CORS_ORIGINS', '')).split(',') if (env('CORS_ORIGINS')) else [f'https://{DLS_URL}']
jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
@@ -57,6 +60,8 @@ app.add_middleware(
allow_headers=['*'],
)
logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG if DEBUG else logging.INFO)
@@ -96,6 +101,7 @@ async def _config():
'LEASE_EXPIRE_DELTA': str(LEASE_EXPIRE_DELTA),
'LEASE_RENEWAL_PERIOD': str(LEASE_RENEWAL_PERIOD),
'CORS_ORIGINS': str(CORS_ORIGINS),
'TZ': str(TZ),
})
@@ -151,7 +157,8 @@ async def _origins(request: Request, leases: bool = False):
for origin in session.query(Origin).all():
x = origin.serialize()
if leases:
x['leases'] = list(map(lambda _: _.serialize(), Lease.find_by_origin_ref(db, origin.origin_ref)))
serialize = dict(renewal_period=LEASE_RENEWAL_PERIOD, renewal_delta=LEASE_RENEWAL_DELTA)
x['leases'] = list(map(lambda _: _.serialize(**serialize), Lease.find_by_origin_ref(db, origin.origin_ref)))
response.append(x)
session.close()
return JSONr(response)
@@ -168,10 +175,12 @@ async def _leases(request: Request, origin: bool = False):
session = sessionmaker(bind=db)()
response = []
for lease in session.query(Lease).all():
x = lease.serialize()
serialize = dict(renewal_period=LEASE_RENEWAL_PERIOD, renewal_delta=LEASE_RENEWAL_DELTA)
x = lease.serialize(**serialize)
if origin:
# assume that each lease has a valid origin record
x['origin'] = session.query(Origin).filter(Origin.origin_ref == lease.origin_ref).first().serialize()
lease_origin = session.query(Origin).filter(Origin.origin_ref == lease.origin_ref).first()
if lease_origin is not None:
x['origin'] = lease_origin.serialize()
response.append(x)
session.close()
return JSONr(response)
@@ -188,7 +197,7 @@ async def _lease_delete(request: Request, lease_ref: str):
@app.get('/-/client-token', summary='* Client-Token', description='creates a new messenger token for this service instance')
async def _client_token():
cur_time = datetime.utcnow()
exp_time = cur_time + relativedelta(years=12)
exp_time = cur_time + CLIENT_TOKEN_EXPIRE_DELTA
payload = {
"jti": str(uuid4()),
@@ -525,6 +534,18 @@ async def leasing_v1_lessor_shutdown(request: Request):
return JSONr(response)
@app.on_event('startup')
async def app_on_startup():
logger.info(f'''
Using timezone: {str(TZ)}. Make sure this is correct and match your clients!
Your clients renew their license every {str(Lease.calculate_renewal(LEASE_RENEWAL_PERIOD, LEASE_RENEWAL_DELTA))}.
If the renewal fails, the license is {str(LEASE_RENEWAL_DELTA)} valid.
Your client-token file (.tok) is valid for {str(CLIENT_TOKEN_EXPIRE_DELTA)}.
''')
if __name__ == '__main__':
import uvicorn

View File

@@ -1,9 +1,9 @@
import datetime
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, VARCHAR, CHAR, ForeignKey, DATETIME, update, and_, inspect, text
from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import sessionmaker, declarative_base
Base = declarative_base()
@@ -56,12 +56,12 @@ class Origin(Base):
session.close()
@staticmethod
def delete(engine: Engine, origins: ["Origin"] = None) -> int:
def delete(engine: Engine, origin_refs: [str] = None) -> int:
session = sessionmaker(bind=engine)()
if origins is None:
if origin_refs is None:
deletions = session.query(Origin).delete()
else:
deletions = session.query(Origin).filter(Origin.origin_ref in origins).delete()
deletions = session.query(Origin).filter(Origin.origin_ref in origin_refs).delete()
session.commit()
session.close()
return deletions
@@ -81,7 +81,10 @@ class Lease(Base):
def __repr__(self):
return f'Lease(origin_ref={self.origin_ref}, lease_ref={self.lease_ref}, expires={self.lease_expires})'
def serialize(self) -> dict:
def serialize(self, renewal_period: float, renewal_delta: timedelta) -> dict:
lease_renewal = int(Lease.calculate_renewal(renewal_period, renewal_delta).total_seconds())
lease_renewal = self.lease_updated + relativedelta(seconds=lease_renewal)
return {
'lease_ref': self.lease_ref,
'origin_ref': self.origin_ref,
@@ -89,6 +92,7 @@ class Lease(Base):
'lease_created': self.lease_created.isoformat(),
'lease_expires': self.lease_expires.isoformat(),
'lease_updated': self.lease_updated.isoformat(),
'lease_renewal': lease_renewal.isoformat(),
}
@staticmethod
@@ -133,7 +137,7 @@ class Lease(Base):
return entity
@staticmethod
def renew(engine: Engine, lease: "Lease", lease_expires: datetime.datetime, lease_updated: datetime.datetime):
def renew(engine: Engine, lease: "Lease", lease_expires: datetime, lease_updated: datetime):
session = sessionmaker(bind=engine)()
x = dict(lease_expires=lease_expires, lease_updated=lease_updated)
session.execute(update(Lease).where(and_(Lease.origin_ref == lease.origin_ref, Lease.lease_ref == lease.lease_ref)).values(**x))
@@ -156,6 +160,28 @@ class Lease(Base):
session.close()
return deletions
@staticmethod
def calculate_renewal(renewal_period: float, delta: timedelta) -> timedelta:
"""
import datetime
LEASE_RENEWAL_PERIOD=0.2 # 20%
delta = datetime.timedelta(days=1)
renew = delta.total_seconds() * LEASE_RENEWAL_PERIOD
renew = datetime.timedelta(seconds=renew)
expires = delta - renew # 19.2
import datetime
LEASE_RENEWAL_PERIOD=0.15 # 15%
delta = datetime.timedelta(days=90)
renew = delta.total_seconds() * LEASE_RENEWAL_PERIOD
renew = datetime.timedelta(seconds=renew)
expires = delta - renew # 76 days, 12:00:00 hours
"""
renew = delta.total_seconds() * renewal_period
renew = timedelta(seconds=renew)
return renew
def init(engine: Engine):
tables = [Origin, Lease]
@@ -163,7 +189,7 @@ def init(engine: Engine):
session = sessionmaker(bind=engine)()
for table in tables:
if not db.dialect.has_table(engine.connect(), table.__tablename__):
session.execute(str(table.create_statement(engine)))
session.execute(text(str(table.create_statement(engine))))
session.commit()
session.close()

View File

@@ -14,6 +14,7 @@ services:
environment:
<<: *dls-variables
volumes:
- /etc/timezone:/etc/timezone:ro
- /opt/docker/fastapi-dls/cert:/app/cert # instance.private.pem, instance.public.pem
- db:/app/database
entrypoint: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--app-dir", "/app", "--proxy-headers"]
@@ -30,6 +31,7 @@ services:
- "80:80" # for "/leasing/v1/lessor/shutdown" used by windows guests, can't be changed!
- "443:443" # first part must match "DLS_PORT"
volumes:
- /etc/timezone:/etc/timezone:ro
- /opt/docker/fastapi-dls/cert:/opt/cert
healthcheck:
test: ["CMD", "curl", "--insecure", "--fail", "https://localhost/-/health"]

View File

@@ -1,8 +1,8 @@
fastapi==0.88.0
fastapi==0.89.1
uvicorn[standard]==0.20.0
python-jose==3.3.0
pycryptodome==3.16.0
pycryptodome==3.17
python-dateutil==2.8.2
sqlalchemy==1.4.46
sqlalchemy==2.0.0
markdown==3.4.1
python-dotenv==0.21.0
python-dotenv==0.21.1

View File

@@ -1 +1 @@
VERSION=1.3
VERSION=1.3.5