Refactor to server subdirectory
A client library will be included in this repository as well, prepare by moving all server components to a subdirectory. As part of this, the server script is refactored to a generated entrypoint script. Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
parent
792149bae6
commit
1e84b2e57d
64
README.md
64
README.md
@ -1,67 +1,3 @@
|
|||||||
# RESTful HTTP API for nftables
|
# RESTful HTTP API for nftables
|
||||||
|
|
||||||
Early work in progress.
|
Early work in progress.
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
A configuration file must be passed using the environment variable `NFT-API-CONFIG`. The file contains a mapping of bcrypt hashed tokens to API paths and methods. Requests to the API are only authorized if the token passed using the header `X-NFT-API-Token` and the requested path and method match an entry in the configuration.
|
|
||||||
|
|
||||||
### Sample configuration:
|
|
||||||
|
|
||||||
```
|
|
||||||
nft-api:
|
|
||||||
tokens:
|
|
||||||
$2y$05$ZifkrfFg2XZU2ds7Lrcl9usJVyxHro9Ezjo84OMpsBSau4pEu42eS:
|
|
||||||
/set/inet/filter/foo4:
|
|
||||||
- GET
|
|
||||||
- POST
|
|
||||||
$2y$05$ZifkrfFg2XZU2ds7Lrcl9usJVyxHro9Ezjo84OMpsBSau4pEu42eS:
|
|
||||||
/set/inet/filter/*:
|
|
||||||
- GET
|
|
||||||
```
|
|
||||||
|
|
||||||
Any elements can contain a wildcard in the form of a `*` character.
|
|
||||||
|
|
||||||
Generate token hashes using any bcrypt hashing tool, such as `htpasswd` from the `apache-utils` suite:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ htpasswd -Bn x
|
|
||||||
```
|
|
||||||
|
|
||||||
The username (here `x`) is not used, ignore the relevant part before and including the colon in the output.
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
The application can be served using any Python WSGI server. Easiest is to use `waitress` using the script found in the repository root:
|
|
||||||
|
|
||||||
```
|
|
||||||
./nftables-api.py
|
|
||||||
```
|
|
||||||
|
|
||||||
It will bind on `*:9090`.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Currently `GET` and `POST` methods are implemented, allowing to query or append data respectively.
|
|
||||||
|
|
||||||
All requests must contain a header `X-NFT-API-Token` containing a token authorized for the requested path and method. All `POST` requests must contain a JSON payload.
|
|
||||||
|
|
||||||
By default, the response body will be output deemed most "useful" - in case of `GET` requests, that is only the data found for the given query, in case of `POST` requests the result of the change. Optionally, a query parameter `raw` can be passed to retrieve a mapping containing all status information, including the raw `libnftables-json(5)` output, instead.
|
|
||||||
|
|
||||||
Below are examples using `curl`.
|
|
||||||
|
|
||||||
### nftables sets
|
|
||||||
|
|
||||||
#### Get a set
|
|
||||||
|
|
||||||
```
|
|
||||||
$ curl -H 'X-NFT-API-Token: foo' localhost:9090/set/inet/filter/foo4
|
|
||||||
["192.168.0.0/24", "192.168.1.1", "192.168.3.0/24", "192.168.4.0/24", "192.168.5.0/26"]⏎
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Append to a set
|
|
||||||
|
|
||||||
```
|
|
||||||
$ curl -H 'X-NFT-API-Token: foo' --json '{"addresses": "fe80::/64"}' localhost:9090/set/inet/filter/foo
|
|
||||||
{"status": true}⏎
|
|
||||||
```
|
|
||||||
|
1
nft_http_api_server/LICENSE
Symbolic link
1
nft_http_api_server/LICENSE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE
|
65
nft_http_api_server/README.md
Normal file
65
nft_http_api_server/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# RESTful HTTP API for nftables - Server
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
A configuration file must be passed using the environment variable `NFT-API-CONFIG`. The file contains a mapping of bcrypt hashed tokens to API paths and methods. Requests to the API are only authorized if the token passed using the header `X-NFT-API-Token` and the requested path and method match an entry in the configuration.
|
||||||
|
|
||||||
|
### Sample configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
nft-api:
|
||||||
|
tokens:
|
||||||
|
$2y$05$ZifkrfFg2XZU2ds7Lrcl9usJVyxHro9Ezjo84OMpsBSau4pEu42eS:
|
||||||
|
/set/inet/filter/foo4:
|
||||||
|
- GET
|
||||||
|
- POST
|
||||||
|
$2y$05$ZifkrfFg2XZU2ds7Lrcl9usJVyxHro9Ezjo84OMpsBSau4pEu42eS:
|
||||||
|
/set/inet/filter/*:
|
||||||
|
- GET
|
||||||
|
```
|
||||||
|
|
||||||
|
Any elements can contain a wildcard in the form of a `*` character.
|
||||||
|
|
||||||
|
Generate token hashes using any bcrypt hashing tool, such as `htpasswd` from the `apache-utils` suite:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ htpasswd -Bn x
|
||||||
|
```
|
||||||
|
|
||||||
|
The username (here `x`) is not used, ignore the relevant part before and including the colon in the output.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
The application can be served using any Python WSGI server. Easiest is to use `waitress` using the script found in the repository root:
|
||||||
|
|
||||||
|
```
|
||||||
|
./nftables-api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
It will bind on `*:9090`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Currently `GET` and `POST` methods are implemented, allowing to query or append data respectively.
|
||||||
|
|
||||||
|
All requests must contain a header `X-NFT-API-Token` containing a token authorized for the requested path and method. All `POST` requests must contain a JSON payload.
|
||||||
|
|
||||||
|
By default, the response body will be output deemed most "useful" - in case of `GET` requests, that is only the data found for the given query, in case of `POST` requests the result of the change. Optionally, a query parameter `raw` can be passed to retrieve a mapping containing all status information, including the raw `libnftables-json(5)` output, instead.
|
||||||
|
|
||||||
|
Below are examples using `curl`.
|
||||||
|
|
||||||
|
### nftables sets
|
||||||
|
|
||||||
|
#### Get a set
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -H 'X-NFT-API-Token: foo' localhost:9090/set/inet/filter/foo4
|
||||||
|
["192.168.0.0/24", "192.168.1.1", "192.168.3.0/24", "192.168.4.0/24", "192.168.5.0/26"]⏎
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Append to a set
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -H 'X-NFT-API-Token: foo' --json '{"addresses": "fe80::/64"}' localhost:9090/set/inet/filter/foo
|
||||||
|
{"status": true}⏎
|
||||||
|
```
|
@ -8,9 +8,8 @@ An English copy of the Licence is shipped in a file called LICENSE along with th
|
|||||||
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from waitress import serve
|
|
||||||
|
|
||||||
from nftables_api.app import app
|
from nftables_api.app import app
|
||||||
|
from waitress import serve
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
serve(app, host='*', port=9090)
|
serve(app, host='*', port=9090)
|
20
nft_http_api_server/nftables_api/server.py
Normal file
20
nft_http_api_server/nftables_api/server.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
A RESTful HTTP API for nftables
|
||||||
|
Copyright 2024, Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
|
||||||
|
|
||||||
|
Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence").
|
||||||
|
You may not use this work except in compliance with the Licence.
|
||||||
|
An English copy of the Licence is shipped in a file called LICENSE along with this applications source code.
|
||||||
|
You may obtain copies of the Licence in any of the official languages at https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from waitress import serve
|
||||||
|
|
||||||
|
from nftables_api.app import app
|
||||||
|
|
||||||
|
|
||||||
|
def server():
|
||||||
|
if __name__ == '__main__':
|
||||||
|
serve(app, host='*', port=9090)
|
||||||
|
else:
|
||||||
|
raise RuntimeError('serve() is intended to be used as an entrypoint.')
|
@ -4,8 +4,8 @@ build-backend = 'setuptools.build_meta'
|
|||||||
|
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = 'nftables_api'
|
name = 'nftables_api-server'
|
||||||
description = 'RESTful HTTP API for nftables'
|
description = 'RESTful HTTP API for nftables (server)'
|
||||||
dynamic = ['license', 'readme', 'version']
|
dynamic = ['license', 'readme', 'version']
|
||||||
authors = [
|
authors = [
|
||||||
{ name='Georg Pfuetzenreuter', email='georg+python@lysergic.dev' },
|
{ name='Georg Pfuetzenreuter', email='georg+python@lysergic.dev' },
|
||||||
@ -30,6 +30,8 @@ requires-python = '>=3.6'
|
|||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
'PyYAML',
|
'PyYAML',
|
||||||
|
'bcrypt',
|
||||||
|
'falcon',
|
||||||
# 'nftables', # cannot by managed by pip
|
# 'nftables', # cannot by managed by pip
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -39,6 +41,9 @@ dev = [
|
|||||||
'ruff',
|
'ruff',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
nftables_api = 'nftables_api.server:server'
|
||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
include-package-data = false
|
include-package-data = false
|
||||||
|
|
||||||
@ -48,4 +53,3 @@ readme = {file = ['README.md']}
|
|||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ['.']
|
where = ['.']
|
||||||
exclude = ['scripts.*', 'tests', 'tests.*', 'ruff.toml']
|
|
@ -1,5 +1,5 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = nftables_api
|
name = nftables_api-server
|
||||||
version = attr: nftables_api.__version__
|
version = attr: nftables_api.__version__
|
||||||
author = Georg Pfuetzenreuter
|
author = Georg Pfuetzenreuter
|
||||||
author_email = georg+python@lysergic.dev
|
author_email = georg+python@lysergic.dev
|
||||||
@ -14,8 +14,4 @@ packages=find:
|
|||||||
python_requires = >=3.6
|
python_requires = >=3.6
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
exclude=
|
|
||||||
scripts*
|
|
||||||
tests*
|
|
||||||
*.toml
|
|
||||||
where=.
|
where=.
|
10
ruff.toml
10
ruff.toml
@ -37,7 +37,11 @@ preview = true
|
|||||||
explicit-preview-rules = true
|
explicit-preview-rules = true
|
||||||
|
|
||||||
[lint.per-file-ignores]
|
[lint.per-file-ignores]
|
||||||
"nftables_api/__init__.py" = ["PLC0414"] # allow explicit re-exports / avoid conflict with F401
|
"nft_http_api_client/nftables_api_client/__init__.py" = ["PLC0414"] # allow explicit re-exports / avoid conflict with F401
|
||||||
|
"nft_http_api_server/nftables_api/__init__.py" = ["PLC0414"] # ^
|
||||||
|
"nft_http_api_server/nftables-http-api.py" = [
|
||||||
|
"INP001", # entrypoint script
|
||||||
|
]
|
||||||
"tests/*.py" = [
|
"tests/*.py" = [
|
||||||
"INP001", # tests do not need to be part of a package
|
"INP001", # tests do not need to be part of a package
|
||||||
"S101", # allow "assert" in test suites
|
"S101", # allow "assert" in test suites
|
||||||
@ -47,5 +51,5 @@ explicit-preview-rules = true
|
|||||||
[lint.pydocstyle]
|
[lint.pydocstyle]
|
||||||
convention = "pep257"
|
convention = "pep257"
|
||||||
|
|
||||||
[lint.isort]
|
#[lint.isort]
|
||||||
force-wrap-aliases = true
|
#force-wrap-aliases = true
|
||||||
|
@ -8,5 +8,5 @@ podman run \
|
|||||||
-it \
|
-it \
|
||||||
-v .:"$wd" \
|
-v .:"$wd" \
|
||||||
registry.opensuse.org/home/crameleon/containers/containers/crameleon/pytest-nftables:latest \
|
registry.opensuse.org/home/crameleon/containers/containers/crameleon/pytest-nftables:latest \
|
||||||
env NFT-API-CONFIG="$wd"/tests/config.yaml PYTHONPATH="$wd" pytest --pdb --pdbcls=IPython.terminal.debugger:Pdb -rA -s -v -x "$wd"/tests
|
env NFT-API-CONFIG="$wd"/tests/config.yaml PYTHONPATH="$wd/nft_http_api_client:$wd/nft_http_api_server" pytest --pdb --pdbcls=IPython.terminal.debugger:Pdb -rA -s -v -x "$wd"/tests
|
||||||
|
|
||||||
|
@ -10,9 +10,8 @@ You may obtain copies of the Licence in any of the official languages at https:/
|
|||||||
|
|
||||||
from falcon import testing
|
from falcon import testing
|
||||||
from nftables import Nftables
|
from nftables import Nftables
|
||||||
from pytest import exit, fixture
|
|
||||||
|
|
||||||
from nftables_api.app import app
|
from nftables_api.app import app
|
||||||
|
from pytest import exit, fixture
|
||||||
|
|
||||||
|
|
||||||
def run_nft(nft, cmd):
|
def run_nft(nft, cmd):
|
||||||
|
Loading…
Reference in New Issue
Block a user