Compare commits
3 Commits
792149bae6
...
e46d3a0410
Author | SHA1 | Date | |
---|---|---|---|
e46d3a0410 | |||
49a39e7970 | |||
1e84b2e57d |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
__pycache__
|
||||
dist
|
||||
nftables_api.egg-info
|
||||
nftables_api*.egg-info
|
||||
venv
|
||||
|
64
README.md
64
README.md
@ -1,67 +1,3 @@
|
||||
# RESTful HTTP API for nftables
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from waitress import serve
|
||||
|
||||
from nftables_api.app import app
|
||||
from waitress import serve
|
||||
|
||||
if __name__ == '__main__':
|
||||
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]
|
||||
name = 'nftables_api'
|
||||
description = 'RESTful HTTP API for nftables'
|
||||
name = 'nftables_api-server'
|
||||
description = 'RESTful HTTP API for nftables (Server)'
|
||||
dynamic = ['license', 'readme', 'version']
|
||||
authors = [
|
||||
{ name='Georg Pfuetzenreuter', email='georg+python@lysergic.dev' },
|
||||
@ -30,6 +30,8 @@ requires-python = '>=3.6'
|
||||
|
||||
dependencies = [
|
||||
'PyYAML',
|
||||
'bcrypt',
|
||||
'falcon',
|
||||
# 'nftables', # cannot by managed by pip
|
||||
]
|
||||
|
||||
@ -39,6 +41,9 @@ dev = [
|
||||
'ruff',
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
nftables_api = 'nftables_api.server:server'
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = false
|
||||
|
||||
@ -48,4 +53,3 @@ readme = {file = ['README.md']}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ['.']
|
||||
exclude = ['scripts.*', 'tests', 'tests.*', 'ruff.toml']
|
@ -1,9 +1,9 @@
|
||||
[metadata]
|
||||
name = nftables_api
|
||||
name = nftables_api-server
|
||||
version = attr: nftables_api.__version__
|
||||
author = Georg Pfuetzenreuter
|
||||
author_email = georg+python@lysergic.dev
|
||||
description = RESTful HTTP API for nftables
|
||||
description = RESTful HTTP API for nftables (Server)
|
||||
license = EUPL-1.2
|
||||
long_description = file: README.md, LICENSE
|
||||
|
||||
@ -14,8 +14,4 @@ packages=find:
|
||||
python_requires = >=3.6
|
||||
|
||||
[options.packages.find]
|
||||
exclude=
|
||||
scripts*
|
||||
tests*
|
||||
*.toml
|
||||
where=.
|
10
ruff.toml
10
ruff.toml
@ -37,7 +37,11 @@ preview = true
|
||||
explicit-preview-rules = true
|
||||
|
||||
[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" = [
|
||||
"INP001", # tests do not need to be part of a package
|
||||
"S101", # allow "assert" in test suites
|
||||
@ -47,5 +51,5 @@ explicit-preview-rules = true
|
||||
[lint.pydocstyle]
|
||||
convention = "pep257"
|
||||
|
||||
[lint.isort]
|
||||
force-wrap-aliases = true
|
||||
#[lint.isort]
|
||||
#force-wrap-aliases = true
|
||||
|
@ -8,5 +8,5 @@ podman run \
|
||||
-it \
|
||||
-v .:"$wd" \
|
||||
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 nftables import Nftables
|
||||
from pytest import exit, fixture
|
||||
|
||||
from nftables_api.app import app
|
||||
from pytest import exit, fixture
|
||||
|
||||
|
||||
def run_nft(nft, cmd):
|
||||
|
Loading…
x
Reference in New Issue
Block a user