Compare commits
3 Commits
792149bae6
...
e46d3a0410
Author | SHA1 | Date | |
---|---|---|---|
e46d3a0410 | |||
49a39e7970 | |||
1e84b2e57d |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
dist
|
dist
|
||||||
nftables_api.egg-info
|
nftables_api*.egg-info
|
||||||
venv
|
venv
|
||||||
|
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_client/LICENSE
Symbolic link
1
nft_http_api_client/LICENSE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE
|
1
nft_http_api_client/README.md
Normal file
1
nft_http_api_client/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# RESTful HTTP API for nftables - Client
|
11
nft_http_api_client/nftables_api_client/__init__.py
Normal file
11
nft_http_api_client/nftables_api_client/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
A RESTful HTTP API client 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 .__version__ import __version__ as __version__
|
11
nft_http_api_client/nftables_api_client/__version__.py
Normal file
11
nft_http_api_client/nftables_api_client/__version__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
A RESTful HTTP API client 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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = '0.0.1'
|
46
nft_http_api_client/nftables_api_client/client.py
Normal file
46
nft_http_api_client/nftables_api_client/client.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"""
|
||||||
|
A RESTful HTTP API client 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 os import getenv
|
||||||
|
|
||||||
|
import urllib3
|
||||||
|
|
||||||
|
|
||||||
|
class NftablesRemote:
|
||||||
|
def __init__(self, endpoint, token=None):
|
||||||
|
if token is None:
|
||||||
|
token = getenv('NFT-API-TOKEN')
|
||||||
|
|
||||||
|
if token is None:
|
||||||
|
raise ValueError('Missing token, pass one as an argument or via NFT-API-TOKEN.')
|
||||||
|
|
||||||
|
self.endpoint = endpoint
|
||||||
|
|
||||||
|
self.auth_headers = {
|
||||||
|
'X-NFT-API-Token': token,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get(self, path):
|
||||||
|
retmap = {
|
||||||
|
'status': -1,
|
||||||
|
'data': {},
|
||||||
|
}
|
||||||
|
|
||||||
|
response = urllib3.request(
|
||||||
|
'GET',
|
||||||
|
f'{self.endpoint}/{path}',
|
||||||
|
headers=self.auth_headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
retmap['status'] = response.status
|
||||||
|
retmap['data'] = response.json()
|
||||||
|
|
||||||
|
return retmap
|
49
nft_http_api_client/pyproject.toml
Normal file
49
nft_http_api_client/pyproject.toml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ['setuptools', 'wheel']
|
||||||
|
build-backend = 'setuptools.build_meta'
|
||||||
|
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = 'nftables_api-client'
|
||||||
|
description = 'RESTful HTTP API for nftables (client)'
|
||||||
|
dynamic = ['license', 'readme', 'version']
|
||||||
|
authors = [
|
||||||
|
{ name='Georg Pfuetzenreuter', email='georg+python@lysergic.dev' },
|
||||||
|
]
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 3 - Alpha',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.12',
|
||||||
|
'Topic :: Software Development',
|
||||||
|
'Typing :: Typed',
|
||||||
|
'Operating System :: POSIX :: Linux',
|
||||||
|
]
|
||||||
|
requires-python = '>=3.6'
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
'urllib3',
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
'pytest',
|
||||||
|
'ruff',
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
include-package-data = false
|
||||||
|
|
||||||
|
[tool.setuptools.dynamic]
|
||||||
|
version = {attr = 'nftables_api_client.__version__'}
|
||||||
|
readme = {file = ['README.md']}
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ['.']
|
17
nft_http_api_client/setup.cfg
Normal file
17
nft_http_api_client/setup.cfg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[metadata]
|
||||||
|
name = nftables_api-client
|
||||||
|
version = attr: nftables_api_client.__version__
|
||||||
|
author = Georg Pfuetzenreuter
|
||||||
|
author_email = georg+python@lysergic.dev
|
||||||
|
description = RESTful HTTP API for nftables (Client)
|
||||||
|
license = EUPL-1.2
|
||||||
|
long_description = file: README.md, LICENSE
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir=
|
||||||
|
=.
|
||||||
|
packages=find:
|
||||||
|
python_requires = >=3.6
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where=.
|
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,9 +1,9 @@
|
|||||||
[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
|
||||||
description = RESTful HTTP API for nftables
|
description = RESTful HTTP API for nftables (Server)
|
||||||
license = EUPL-1.2
|
license = EUPL-1.2
|
||||||
long_description = file: README.md, LICENSE
|
long_description = file: README.md, LICENSE
|
||||||
|
|
||||||
@ -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…
x
Reference in New Issue
Block a user