mirror of
https://gitea.publichub.eu/oscar.krause/fastapi-dls.git
synced 2024-11-22 14:28:48 +00:00
Merge branch 'dev' into 'main'
v0.1 See merge request oscar.krause/fastapi-dls!1
This commit is contained in:
commit
a6cf5e0ac1
7
.codeclimate.yml
Normal file
7
.codeclimate.yml
Normal file
@ -0,0 +1,7 @@
|
||||
plugins:
|
||||
bandit:
|
||||
enabled: true
|
||||
sonar-python:
|
||||
enabled: true
|
||||
pylint:
|
||||
enabled: true
|
28
.editorconfig
Normal file
28
.editorconfig
Normal file
@ -0,0 +1,28 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.{js,py}]
|
||||
charset = utf-8
|
||||
|
||||
# 4 space indentation
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Indentation override for all JS under lib directory
|
||||
[lib/**.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.DS_Store
|
||||
venv/
|
||||
.idea/
|
||||
app/cert/*.*
|
30
.gitlab-ci.yml
Normal file
30
.gitlab-ci.yml
Normal file
@ -0,0 +1,30 @@
|
||||
cache:
|
||||
key: one-key-to-rule-them-all
|
||||
|
||||
build:
|
||||
image: docker:dind
|
||||
interruptible: true
|
||||
stage: build
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
tags: [ docker ]
|
||||
script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker build . --tag ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:${CI_BUILD_REF}
|
||||
- docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_BUILD_REF_NAME}:${CI_BUILD_REF}
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Nothing to do ..."
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
script:
|
||||
- docker login -u $PUBLIC_REGISTRY_USER -p $PUBLIC_REGISTRY_TOKEN
|
||||
- docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:${CI_BUILD_REF}
|
||||
- docker build . --tag $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:latest
|
||||
- docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:${CI_BUILD_REF}
|
||||
- docker push $PUBLIC_REGISTRY_USER/${CI_PROJECT_PATH}:latest
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
FROM python:3.10-alpine
|
||||
|
||||
COPY requirements.txt /tmp/requirements.txt
|
||||
|
||||
RUN apk update \
|
||||
&& apk add --no-cache --virtual build-deps gcc g++ python3-dev musl-dev \
|
||||
&& apk add --no-cache postgresql postgresql-dev mariadb-connector-c-dev \
|
||||
&& pip install --no-cache-dir --upgrade uvicorn \
|
||||
&& pip install --no-cache-dir psycopg2==2.9.3 mysqlclient==2.1.1 pysqlite3==0.4.7 \
|
||||
&& pip install --no-cache-dir -r /tmp/requirements.txt \
|
||||
&& apk del build-deps
|
||||
|
||||
COPY app /app
|
||||
|
||||
HEALTHCHECK --start-period=30s --interval=10s --timeout=5s --retries=3 CMD curl --fail http://localhost/status || exit 1
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "443", "--app-dir", "/app", "--proxy-headers", "--ssl-keyfile", "/app/cert/webserver.key", "--ssl-certfile", "/app/cert/webserver.crt"]
|
77
README.md
77
README.md
@ -1,3 +1,78 @@
|
||||
# FastAPI-DLS
|
||||
|
||||
Minimal Licencing Servie.
|
||||
Minimal Delegated License Service (DLS).
|
||||
|
||||
# Setup (Docker)
|
||||
|
||||
**Run this on the Docker-Host**
|
||||
|
||||
```shell
|
||||
WORKING_DIR=/opt/docker/fastapi-dls/cert
|
||||
mkdir -p $WORKING_DIR
|
||||
cd $WORKING_DIR
|
||||
openssl genrsa -out $WORKING_DIR/instance.private.pem 2048
|
||||
openssl rsa -in $WORKING_DIR/instance.private.pem -outform PEM -pubout -out $WORKING_DIR/instance.public.pem
|
||||
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout $WORKING_DIR/webserver.key -out $WORKING_DIR/webserver.crt
|
||||
docker run -e DLS_URL=`hostname -i` -e DLS_PORT=443 -p 443:443 -v $WORKING_DIR:/app/cert collinwebdesigns/fastapi-dls:latest
|
||||
```
|
||||
|
||||
# Installation
|
||||
|
||||
**The token file has to be copied! It's not enough to C&P file contents, because there can be special characters.**
|
||||
|
||||
## Linux
|
||||
|
||||
```shell
|
||||
curl --insecure -X GET https://<dls-hostname-or-ip>/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token.tok
|
||||
service nvidia-gridd restart
|
||||
nvidia-smi -q | grep "License"
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
Download file and place it into `C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken`.
|
||||
Now restart `NvContainerLocalSystem` service.
|
||||
|
||||
# Troubleshoot
|
||||
|
||||
## Linux
|
||||
|
||||
Logs are available with `journalctl -u nvidia-gridd -f`.
|
||||
|
||||
## Windows
|
||||
|
||||
Logs are available in `C:\Users\Public\Documents\Nvidia\LoggingLog.NVDisplay.Container.exe.log`.
|
||||
|
||||
# Known Issues
|
||||
|
||||
## Linux
|
||||
|
||||
Currently, there are no known issues.
|
||||
|
||||
## Windows
|
||||
|
||||
On Windows there is currently a problem returning the license. As you can see the license is installed successfully
|
||||
after
|
||||
a few minutes. About the time of the first *lease period* the driver gets a *Mismatch between client and server with
|
||||
respect to licenses held*.
|
||||
|
||||
<details>
|
||||
<summary>Log</summary>
|
||||
|
||||
```
|
||||
Tue Dec 20 05:55:52 2022:<2>:NLS initialized
|
||||
Tue Dec 20 05:55:57 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
|
||||
Tue Dec 20 05:55:58 2022:<2>:License returned successfully. (Info: 192.168.178.33)
|
||||
Tue Dec 20 05:56:20 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
|
||||
Tue Dec 20 05:56:21 2022:<2>:License returned successfully. (Info: 192.168.178.33)
|
||||
Tue Dec 20 05:56:46 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
|
||||
Tue Dec 20 05:56:47 2022:<2>:License returned successfully. (Info: 192.168.178.33)
|
||||
Tue Dec 20 05:56:54 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:11:54 GMT)
|
||||
Tue Dec 20 05:57:17 2022:<2>:Mismatch between client and server with respect to licenses held. Returning the licenses
|
||||
Tue Dec 20 05:57:18 2022:<2>:License returned successfully. (Info: 192.168.178.33)
|
||||
Tue Dec 20 05:59:20 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:14:20 GMT)
|
||||
Tue Dec 20 06:01:45 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:16:45 GMT)
|
||||
Tue Dec 20 06:04:10 2022:<1>:License renewed successfully. (Info: 192.168.178.33, NVIDIA RTX Virtual Workstation; Expiry: 2022-12-20 5:19:10 GMT)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
9
ToDo.md
Normal file
9
ToDo.md
Normal file
@ -0,0 +1,9 @@
|
||||
# FastAPI-DLS Server
|
||||
|
||||
|
||||
## ToDo
|
||||
|
||||
- Create Client Token (`.tok`-file)
|
||||
- Docker Image
|
||||
- Create Certificates if not exist
|
||||
- Keep track of clients and Leases
|
20
app/helper.py
Normal file
20
app/helper.py
Normal file
@ -0,0 +1,20 @@
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.PublicKey.RSA import RsaKey
|
||||
|
||||
|
||||
def load_file(filename) -> bytes:
|
||||
with open(filename, 'rb') as file:
|
||||
content = file.read()
|
||||
return content
|
||||
|
||||
|
||||
def load_key(filename) -> RsaKey:
|
||||
return RSA.import_key(extern_key=load_file(filename), passphrase=None)
|
||||
|
||||
|
||||
def private_bytes(rsa: RsaKey) -> bytes:
|
||||
return rsa.export_key(format='PEM', passphrase=None, protection=None)
|
||||
|
||||
|
||||
def public_key(rsa: RsaKey) -> bytes:
|
||||
return rsa.public_key().export_key(format='PEM')
|
295
app/main.py
Normal file
295
app/main.py
Normal file
@ -0,0 +1,295 @@
|
||||
from base64 import b64encode
|
||||
from hashlib import sha256
|
||||
from uuid import uuid4
|
||||
from os.path import join, dirname
|
||||
from os import getenv
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.requests import Request
|
||||
import json
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from calendar import timegm
|
||||
from jose import jws, jwk, jwt
|
||||
from jose.constants import ALGORITHMS
|
||||
from starlette.responses import StreamingResponse, JSONResponse
|
||||
|
||||
from helper import load_key, private_bytes, public_key
|
||||
|
||||
# todo: initialize certificate (or should be done by user, and passed through "volumes"?)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
LEASE_EXPIRE_DELTA = relativedelta(minutes=15) # days=90
|
||||
|
||||
DLS_URL = str(getenv('DLS_URL', 'localhost'))
|
||||
DLS_PORT = int(getenv('DLS_PORT', '443'))
|
||||
SITE_KEY_XID = getenv('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000')
|
||||
INSTANCE_KEY_RSA = load_key(join(dirname(__file__), 'cert/instance.private.pem'))
|
||||
INSTANCE_KEY_PUB = load_key(join(dirname(__file__), 'cert/instance.public.pem'))
|
||||
|
||||
|
||||
@app.get('/')
|
||||
async def index():
|
||||
return JSONResponse({'hello': 'world'})
|
||||
|
||||
|
||||
@app.get('/status')
|
||||
async def status(request: Request):
|
||||
return JSONResponse({'status': 'up'})
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_core_service_instance/service_instance_token_manager.py
|
||||
@app.get('/client-token')
|
||||
async def client_token():
|
||||
cur_time = datetime.utcnow()
|
||||
exp_time = cur_time + relativedelta(years=12)
|
||||
|
||||
service_instance_public_key_configuration = {
|
||||
"service_instance_public_key_me": {
|
||||
"mod": hex(INSTANCE_KEY_PUB.public_key().n)[2:],
|
||||
"exp": INSTANCE_KEY_PUB.public_key().e,
|
||||
},
|
||||
"service_instance_public_key_pem": INSTANCE_KEY_PUB.export_key().decode('utf-8'),
|
||||
"key_retention_mode": "LATEST_ONLY"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"jti": str(uuid4()),
|
||||
"iss": "NLS Service Instance",
|
||||
"aud": "NLS Licensed Client",
|
||||
"iat": timegm(cur_time.timetuple()),
|
||||
"nbf": timegm(cur_time.timetuple()),
|
||||
"exp": timegm(exp_time.timetuple()),
|
||||
"update_mode": "ABSOLUTE",
|
||||
"scope_ref_list": [str(uuid4())],
|
||||
"fulfillment_class_ref_list": [],
|
||||
"service_instance_configuration": {
|
||||
"nls_service_instance_ref": "00000000-0000-0000-0000-000000000000",
|
||||
"svc_port_set_list": [
|
||||
{
|
||||
"idx": 0,
|
||||
"d_name": "DLS",
|
||||
"svc_port_map": [
|
||||
{"service": "auth", "port": DLS_PORT},
|
||||
{"service": "lease", "port": DLS_PORT}
|
||||
]
|
||||
}
|
||||
],
|
||||
"node_url_list": [{"idx": 0, "url": DLS_URL, "url_qr": DLS_URL, "svc_port_set_idx": 0}]
|
||||
},
|
||||
"service_instance_public_key_configuration": service_instance_public_key_configuration,
|
||||
}
|
||||
|
||||
key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256)
|
||||
data = jws.sign(payload, key=key, headers=None, algorithm='RS256')
|
||||
|
||||
response = StreamingResponse(iter([data]), media_type="text/plain")
|
||||
filename = f'client_configuration_token_{datetime.now().strftime("%d-%m-%y-%H-%M-%S")}'
|
||||
response.headers["Content-Disposition"] = f'attachment; filename={filename}'
|
||||
return response
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_origins_controller.py
|
||||
@app.post('/auth/v1/origin')
|
||||
async def auth_origin(request: Request):
|
||||
body = await request.body()
|
||||
body = body.decode('utf-8')
|
||||
j = json.loads(body)
|
||||
# {"candidate_origin_ref":"00112233-4455-6677-8899-aabbccddeeff","environment":{"fingerprint":{"mac_address_list":["ff:ff:ff:ff:ff:ff"]},"hostname":"my-hostname","ip_address_list":["192.168.178.123","fe80::","fe80::1%enp6s18"],"guest_driver_version":"510.85.02","os_platform":"Debian GNU/Linux 11 (bullseye) 11","os_version":"11 (bullseye)"},"registration_pending":false,"update_pending":false}
|
||||
print(f'> [ origin ]: {j}')
|
||||
|
||||
cur_time = datetime.utcnow()
|
||||
response = {
|
||||
"origin_ref": j['candidate_origin_ref'],
|
||||
"environment": j['environment'],
|
||||
"svc_port_set_list": None,
|
||||
"node_url_list": None,
|
||||
"node_query_order": None,
|
||||
"prompts": None,
|
||||
"sync_timestamp": cur_time.isoformat()
|
||||
}
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py
|
||||
# venv/lib/python3.9/site-packages/nls_core_auth/auth.py - CodeResponse
|
||||
@app.post('/auth/v1/code')
|
||||
async def auth_code(request: Request):
|
||||
body = await request.body()
|
||||
body = body.decode('utf-8')
|
||||
j = json.loads(body)
|
||||
# {"code_challenge":"...","origin_ref":"00112233-4455-6677-8899-aabbccddeeff"}
|
||||
print(f'> [ code ]: {j}')
|
||||
|
||||
cur_time = datetime.utcnow()
|
||||
expires = cur_time + relativedelta(days=1)
|
||||
|
||||
payload = {
|
||||
'iat': timegm(cur_time.timetuple()),
|
||||
'exp': timegm(expires.timetuple()),
|
||||
'challenge': j['code_challenge'],
|
||||
'origin_ref': j['code_challenge'],
|
||||
'key_ref': SITE_KEY_XID,
|
||||
'kid': SITE_KEY_XID
|
||||
}
|
||||
|
||||
headers = None
|
||||
kid = payload.get('kid')
|
||||
if kid:
|
||||
headers = {'kid': kid}
|
||||
key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
|
||||
auth_code = jws.sign(payload, key, headers=headers, algorithm='RS256')
|
||||
|
||||
response = {
|
||||
"auth_code": auth_code,
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
"prompts": None
|
||||
}
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_services_auth/test/test_auth_controller.py
|
||||
# venv/lib/python3.9/site-packages/nls_core_auth/auth.py - TokenResponse
|
||||
@app.post('/auth/v1/token')
|
||||
async def auth_token(request: Request):
|
||||
body = await request.body()
|
||||
body = body.decode('utf-8')
|
||||
j = json.loads(body)
|
||||
# {"auth_code":"...","code_verifier":"..."}
|
||||
|
||||
# payload = self._security.get_valid_payload(req.auth_code) # todo
|
||||
key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
|
||||
payload = jwt.decode(token=j['auth_code'], key=key)
|
||||
|
||||
# validate the code challenge
|
||||
if payload['challenge'] != b64encode(sha256(j['code_verifier'].encode('utf-8')).digest()).rstrip(b'=').decode('utf-8'):
|
||||
raise HTTPException(status_code=403, detail='expected challenge did not match verifier')
|
||||
|
||||
cur_time = datetime.utcnow()
|
||||
access_expires_on = cur_time + relativedelta(days=1)
|
||||
|
||||
new_payload = {
|
||||
'iat': timegm(cur_time.timetuple()),
|
||||
'nbf': timegm(cur_time.timetuple()),
|
||||
'iss': 'https://cls.nvidia.org',
|
||||
'aud': 'https://cls.nvidia.org',
|
||||
'exp': timegm(access_expires_on.timetuple()),
|
||||
'origin_ref': payload['origin_ref'],
|
||||
'key_ref': SITE_KEY_XID,
|
||||
'kid': SITE_KEY_XID,
|
||||
}
|
||||
|
||||
headers = None
|
||||
kid = payload.get('kid')
|
||||
if kid:
|
||||
headers = {'kid': kid}
|
||||
key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS512)
|
||||
auth_token = jwt.encode(new_payload, key=key, headers=headers, algorithm='RS256')
|
||||
|
||||
response = {
|
||||
"expires": access_expires_on.isoformat(),
|
||||
"auth_token": auth_token,
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
}
|
||||
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
@app.post('/leasing/v1/lessor')
|
||||
async def leasing_lessor(request: Request):
|
||||
body = await request.body()
|
||||
body = body.decode('utf-8')
|
||||
j = json.loads(body)
|
||||
# {'fulfillment_context': {'fulfillment_class_ref_list': []}, 'lease_proposal_list': [{'license_type_qualifiers': {'count': 1}, 'product': {'name': 'NVIDIA RTX Virtual Workstation'}}], 'proposal_evaluation_mode': 'ALL_OF', 'scope_ref_list': ['00112233-4455-6677-8899-aabbccddeeff']}
|
||||
print(f'> [ lessor ]: {j}')
|
||||
|
||||
cur_time = datetime.utcnow()
|
||||
# todo: keep track of leases, to return correct list on '/leasing/v1/lessor/leases'
|
||||
lease_result_list = []
|
||||
for scope_ref in j['scope_ref_list']:
|
||||
lease_result_list.append({
|
||||
"ordinal": 0,
|
||||
"lease": {
|
||||
"ref": scope_ref,
|
||||
"created": cur_time.isoformat(),
|
||||
"expires": (cur_time + LEASE_EXPIRE_DELTA).isoformat(),
|
||||
"recommended_lease_renewal": 0.15,
|
||||
"offline_lease": "true",
|
||||
"license_type": "CONCURRENT_COUNTED_SINGLE"
|
||||
}
|
||||
})
|
||||
|
||||
response = {
|
||||
"lease_result_list": lease_result_list,
|
||||
"result_code": "SUCCESS",
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
"prompts": None
|
||||
}
|
||||
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_services_lease/test/test_lease_multi_controller.py
|
||||
@app.get('/leasing/v1/lessor/leases')
|
||||
async def leasing_lessor_lease(request: Request):
|
||||
cur_time = datetime.utcnow()
|
||||
# venv/lib/python3.9/site-packages/nls_dal_service_instance_dls/schema/service_instance/V1_0_21__product_mapping.sql
|
||||
response = {
|
||||
# GRID-Virtual-WS 2.0 CONCURRENT_COUNTED_SINGLE
|
||||
"active_lease_list": [
|
||||
"BE276D7B-2CDB-11EC-9838-061A22468B59"
|
||||
],
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
"prompts": None
|
||||
}
|
||||
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
# venv/lib/python3.9/site-packages/nls_core_lease/lease_single.py
|
||||
@app.put('/leasing/v1/lease/{lease_ref}')
|
||||
async def leasing_lease_renew(request: Request, lease_ref: str):
|
||||
print(f'> [ renew ]: lease: {lease_ref}')
|
||||
|
||||
cur_time = datetime.utcnow()
|
||||
response = {
|
||||
"lease_ref": lease_ref,
|
||||
"expires": (cur_time + LEASE_EXPIRE_DELTA).isoformat(),
|
||||
"recommended_lease_renewal": 0.16,
|
||||
"offline_lease": True,
|
||||
"prompts": None,
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
}
|
||||
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
@app.delete('/leasing/v1/lessor/leases')
|
||||
async def leasing_lessor_lease_remove(request: Request):
|
||||
cur_time = datetime.utcnow()
|
||||
response = {
|
||||
"released_lease_list": None,
|
||||
"release_failure_list": None,
|
||||
"sync_timestamp": cur_time.isoformat(),
|
||||
"prompts": None
|
||||
}
|
||||
return JSONResponse(response)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
|
||||
###
|
||||
#
|
||||
# Running `python app/main.py` assumes that the user created a keypair, e.g. with openssl.
|
||||
#
|
||||
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout app/cert/webserver.key -out app/cert/webserver.crt
|
||||
#
|
||||
###
|
||||
|
||||
print(f'> Starting dev-server ...')
|
||||
|
||||
ssl_keyfile = join(dirname(__file__), 'cert/webserver.key')
|
||||
ssl_certfile = join(dirname(__file__), 'cert/webserver.crt')
|
||||
|
||||
uvicorn.run('main:app', host='0.0.0.0', port=443, ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, reload=True)
|
174
doc/Reverse Engineering Notes.md
Normal file
174
doc/Reverse Engineering Notes.md
Normal file
@ -0,0 +1,174 @@
|
||||
# Reverse Engineering Notes
|
||||
|
||||
# Usefully commands
|
||||
|
||||
## Check licensing status
|
||||
|
||||
- `nvidia-smi -q | grep "License"`
|
||||
|
||||
**Output**
|
||||
|
||||
```
|
||||
vGPU Software Licensed Product
|
||||
License Status : Licensed (Expiry: 2023-1-14 12:59:52 GMT)
|
||||
```
|
||||
|
||||
## Track licensing progress
|
||||
|
||||
- NVIDIA Grid Log: `journalctl -u nvidia-gridd -f`
|
||||
|
||||
```
|
||||
systemd[1]: Started NVIDIA Grid Daemon.
|
||||
nvidia-gridd[2986]: Configuration parameter ( ServerAddress ) not set
|
||||
nvidia-gridd[2986]: vGPU Software package (0)
|
||||
nvidia-gridd[2986]: Ignore service provider and node-locked licensing
|
||||
nvidia-gridd[2986]: NLS initialized
|
||||
nvidia-gridd[2986]: Acquiring license. (Info: license.nvidia.space; NVIDIA RTX Virtual Workstation)
|
||||
nvidia-gridd[2986]: License acquired successfully. (Info: license.nvidia.space, NVIDIA RTX Virtual Workstation; Expiry: 2023-1-29 22:3:0 GMT)
|
||||
```
|
||||
|
||||
# DLS-Container File-System (Docker)
|
||||
|
||||
## Configuration data
|
||||
|
||||
Most variables and configs are stored in `/var/lib/docker/volumes/configurations/_data`.
|
||||
|
||||
## Dive / Docker image inspector
|
||||
|
||||
- `dive dls:appliance`
|
||||
|
||||
The source code is stored in `/venv/lib/python3.9/site-packages/nls_*`.
|
||||
|
||||
Image-Reference:
|
||||
|
||||
```
|
||||
Tags: (unavailable)
|
||||
Id: d1c7976a5d2b3681ff6c5a30f8187e4015187a83f3f285ba4a37a45458bd6b98
|
||||
Digest: sha256:311223c5af7a298ec1104f5dc8c3019bfb0e1f77256dc3d995244ffb295a97
|
||||
1f
|
||||
Command:
|
||||
#(nop) ADD file:c1900d3e3a29c29a743a8da86c437006ec5d2aa873fb24e48033b6bf492bb37b in /
|
||||
```
|
||||
|
||||
## Private Key (Site-Key)
|
||||
|
||||
- `/etc/dls/config/decryptor/decryptor`
|
||||
|
||||
```shell
|
||||
docker exec -it <container-id> /etc/dls/config/decryptor/decryptor > /tmp/private-key.pem
|
||||
```
|
||||
|
||||
```
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
```
|
||||
|
||||
## Site Key Uri - `/etc/dls/config/site_key_uri.bin`
|
||||
|
||||
```
|
||||
base64-content...
|
||||
```
|
||||
|
||||
## DB Password - `/etc/dls/config/dls_db_password.bin`
|
||||
|
||||
```
|
||||
base64-content...
|
||||
```
|
||||
|
||||
**Decrypt database password**
|
||||
|
||||
```
|
||||
cd /var/lib/docker/volumes/configurations/_data
|
||||
cat dls_db_password.bin | base64 -d > dls_db_password.bin.raw
|
||||
openssl rsautl -decrypt -inkey /tmp/private-key.pem -in dls_db_password.bin.raw
|
||||
```
|
||||
|
||||
# Database
|
||||
|
||||
- It's enough to manipulate database licenses. There must not be changed any line of code to bypass licensing
|
||||
validations.
|
||||
|
||||
# Logging / Stack Trace
|
||||
|
||||
- https://docs.nvidia.com/license-system/latest/nvidia-license-system-user-guide/index.html#troubleshooting-dls-instance
|
||||
|
||||
**Failed licensing log**
|
||||
|
||||
```
|
||||
{
|
||||
"activity": 100,
|
||||
"context": {
|
||||
"SERVICE_INSTANCE_ID": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38",
|
||||
"SERVICE_INSTANCE_NAME": "DEFAULT_2022-12-14_12:48:30",
|
||||
"description": "borrow failed: NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)",
|
||||
"event_type": null,
|
||||
"function_name": "_evt",
|
||||
"lineno": 54,
|
||||
"module_name": "nls_dal_lease_dls.event",
|
||||
"operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24",
|
||||
"origin_ref": "3f7f5a50-a26b-425b-8d5e-157f63e72b1c",
|
||||
"service_name": "nls_services_lease"
|
||||
},
|
||||
"detail": {
|
||||
"oc": {
|
||||
"license_allotment_xid": "10c4317f-7c4c-11ed-a524-0e4252a7e5f1",
|
||||
"origin_ref": "3f7f5a50-a26b-425b-8d5e-157f63e72b1c",
|
||||
"service_instance_xid": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38"
|
||||
},
|
||||
"operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24"
|
||||
},
|
||||
"id": "0cc9e092-3b92-4652-8d9e-7622ef85dc79",
|
||||
"metadata": {},
|
||||
"ts": "2022-12-15T10:25:36.827661Z"
|
||||
}
|
||||
|
||||
{
|
||||
"activity": 400,
|
||||
"context": {
|
||||
"SERVICE_INSTANCE_ID": "b43d6e46-d6d0-4943-8b8d-c66a5f6e0d38",
|
||||
"SERVICE_INSTANCE_NAME": "DEFAULT_2022-12-14_12:48:30",
|
||||
"description": "lease_multi_create failed: no pool features found for: NVIDIA RTX Virtual Workstation",
|
||||
"event_by": "system",
|
||||
"function_name": "lease_multi_create",
|
||||
"level": "warning",
|
||||
"lineno": 157,
|
||||
"module_name": "nls_services_lease.controllers.lease_multi_controller",
|
||||
"operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24",
|
||||
"service_name": "nls_services_lease"
|
||||
},
|
||||
"detail": {
|
||||
"_msg": "lease_multi_create failed: no pool features found for: NVIDIA RTX Virtual Workstation",
|
||||
"exec_info": ["NotFoundError", "NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)", " File \"/venv/lib/python3.9/site-packages/nls_services_lease/controllers/lease_multi_controller.py\", line 127, in lease_multi_create\n data = _leaseMulti.lease_multi_create(event_args)\n File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 208, in lease_multi_create\n raise e\n File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 184, in lease_multi_create\n self._try_proposals(oc, mlr, results, detail)\n File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 219, in _try_proposals\n lease = self._leases.create(creator)\n File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 230, in create\n features = self._get_features(creator)\n File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 148, in _get_features\n self._explain_not_available(cur, creator)\n File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 299, in _explain_not_available\n raise NotFoundError(f'no pool features found for: {lcc.product_name}')\n"],
|
||||
"operation_id": "e72a8ca7-34cc-4e11-b80c-273592085a24"
|
||||
},
|
||||
"id": "282801b9-d612-40a5-9145-b56d8e420dac",
|
||||
"metadata": {},
|
||||
"ts": "2022-12-15T10:25:36.831673Z"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Stack Trace**
|
||||
|
||||
```
|
||||
"NotFoundError", "NotFoundError(no pool features found for: NVIDIA RTX Virtual Workstation)", " File \"/venv/lib/python3.9/site-packages/nls_services_lease/controllers/lease_multi_controller.py\", line 127, in lease_multi_create
|
||||
data = _leaseMulti.lease_multi_create(event_args)
|
||||
File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 208, in lease_multi_create
|
||||
raise e
|
||||
File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 184, in lease_multi_create
|
||||
self._try_proposals(oc, mlr, results, detail)
|
||||
File \"/venv/lib/python3.9/site-packages/nls_core_lease/lease_multi.py\", line 219, in _try_proposals
|
||||
lease = self._leases.create(creator)
|
||||
File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 230, in create
|
||||
features = self._get_features(creator)
|
||||
File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 148, in _get_features
|
||||
self._explain_not_available(cur, creator)
|
||||
File \"/venv/lib/python3.9/site-packages/nls_dal_lease_dls/leases.py\", line 299, in _explain_not_available
|
||||
raise NotFoundError(f'no pool features found for: {lcc.product_name}')
|
||||
"
|
||||
```
|
||||
|
||||
# Nginx
|
||||
|
||||
- NGINX uses `/opt/certs/cert.pem` and `/opt/certs/key.pem`
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
fastapi==0.88.0
|
||||
uvicorn[standard]==0.20.0
|
||||
python-jose==3.3.0
|
||||
pycryptodome==3.16.0
|
||||
python-dateutil==2.8.2
|
Loading…
Reference in New Issue
Block a user