Documentation: add dotenv.example and better instructions
All checks were successful
ci/woodpecker/pr/linters Pipeline was successful
ci/woodpecker/push/linters Pipeline was successful

Improvement upon basework at !5, closes #8
This commit is contained in:
Evilham 2023-12-08 00:29:33 +01:00
parent 8500bca052
commit b8c81cdb61
Signed by: evilham
GPG key ID: AE3EE30D970886BF
5 changed files with 235 additions and 43 deletions

View file

@ -29,3 +29,11 @@ steps:
- <<: *pysteps
- pipenv run mypy -m adlermanager
environment: *pyenv
dotenv:
image: *pyimage
group: linters
commands:
- <<: *pysteps
- pipenv run python -m adlermanager.Config dotenv.example.gen
- diff -q dotenv.example dotenv.example.gen
environment: *pyenv

View file

@ -1,4 +1,4 @@
# prometheus-adlermanager (Working title)
# prometheus-adlermanager
## What?
@ -16,58 +16,94 @@ a fashion suitable for user-facing status pages.
## I want it!
### Development environment
### Dependencies
You need to have installed pipenv (on debian stable this is `sudo apt install pipenv`), and then, do
The easiest way to manage dependencies for deployment and development is with
pipenv:
- On Debian-based systems: `sudo apt install pipenv`
- On FreeBSD: `pkg install py39-pipenv` (or `devel/py-pipenv` from ports)
The actual dependencies are:
- attrs
- twisted[conch]
- service-identity
- pyyaml
- klein
- jinja2
- markdown
Using Pipenv you can install them for development with:
```sh
pipenv install --dev
```
you need an `.env` in the root path of this git repo with the suggested env vars
And for deployment with:
```sh
cat > .env <<END
DATA_DIR=./example-data
SSH_KEYS_DIR=./example-data/ssh
PYTHONPATH=./src
WEB_ENDPOINT="tcp6:interface=\:\::port=8080"
SSH_ENABLED="YES"
pipenv install
```
### Configuration
This is done via environment variables, Pipenv will import them from a `.env`
file in the root of this repo.
You can use `dotenv.example` as a base for your settings:
```sh
cp dotenv.example .env
END
```
To get working ssh interface, add your ssh public key in the following location `example-data/ssh/users/myuser.key`
**Review** the available settings and their descriptions, particularly you will
want to check `DATA_DIR` and `SSH_KEYS_DIR`.
Finally, to run the server, use the following command
### SSH access
In order to access AdlerManager via SSH, you will need to add your public SSH
key in `authorized_keys` format to:
`${SSH_KEYS_DIR:-data}/ssh/users/myuser.key`
And give yourself access to the given site, by adding your username to its
`ssh_users` list.
### Running
To run the server for development you can use the following command:
```sh
pipenv run twistd -ny app.py
```
After that you have the public status web visible in http://localhost:8080 and the ssh interface in localhost port 2222
And for deployment, you can use [`twistd`][twistd] itself to run the process
in the background or any other daemon watching strategy of your liking
(including e.g. `runit` or `systemd`).
[twistd]: https://docs.twisted.org/en/stable/core/howto/basics.html#twistd
### Deployment instructions
### Using
TODO: Maybe make this available / add deployment instructions.
After that with the defaults you will have the public status web visible in
http://localhost:8080 and the ssh interface in localhost port 2222
which you can access with `ssh -p 2222 USER@localhost`.
## How does it work?
We aim to solve that by using the same source of information to publish
only the desired state/statistics.
### 1. Pretend to be an AlertManager
This is done by accepting `POST` requests from Prometheus on
`/api/v1/alerts`, see
[https://prometheus.io/docs/alerting/clients/](AlertManager) docs.
### 2. Filter out non-public Alerts
### 2. Structure Alerts into Services and Components
To mark an Alert as public, whitelist it by using the special labels:
TODO
### 3. Structure Alerts into Services and Components
### 3. Web only lists alerts configured for AdlerManager (public!)
### 4. Keep track of incidents
### 5. Allow for public updates / accountability
### 5. Allow for public updates / accountability (via SSH!)

70
dotenv.example Normal file
View file

@ -0,0 +1,70 @@
#
# Automatically generated, manual changes will be lost
# Run: python -m adlermanager.Config dotenv.example
#
# All settings can be set via Environment variables.
# If you use pipenv, you are encouraged to use the .env file.
# See pipenv's documentation for more information.
#DATA_DIR="../data"
#
# Environment: DATA_DIR.
# Directory to save persistent data in.
# # Web
#WEB_ENDPOINT="tcp6:interface\:\::port=8080"
#
# Environment: WEB_ENDPOINT.
# Where we should listen for HTTP connections.
# This defaults to TCP port 8080, but you can also use a UNIX socket,
# which you can proxy from your web server.
#
# See:
# https://docs.twisted.org/en/stable/core/howto/endpoints.html#servers
# https://klein.readthedocs.io/en/latest/examples/alternativerunning.html#example-ipv6-tls-unix-sockets-endpoints
#WEB_STATIC_DIR="../static"
#
# Environment: WEB_STATIC_DIR.
# Directory to server static files from.
# # SSH
#SSH_ENABLED="YES"
#
# Environment: SSH_ENABLED.
# If this environment variable is anything other than empty,
# SSH_ENDPOINT will be used to listen for SSH connections.
#SSH_ENDPOINT="tcp6:interface=\:\::port=2222"
#
# Environment: SSH_ENDPOINT.
# Where we should listen for SSH connections if SSH_ENABLED is
# anything other than empty.
#
# See:
# https://docs.twisted.org/en/stable/core/howto/endpoints.html#servers
# https://klein.readthedocs.io/en/latest/examples/alternativerunning.html#example-ipv6-tls-unix-sockets-endpoints
#SSH_KEY_SIZE="4096"
#
# Environment: SSH_KEY_SIZE.
# Size for server's auto-generated SSH host key.
#SSH_KEYS_DIR="../data/ssh"
#
# Environment: SSH_KEYS_DIR.
# Directory to save SSH keys in.
# This includes the server private key and users' public keys.
# # Alerts processing
#NEW_INCIDENT_TIMEOUT="60"
#
# Environment: NEW_INCIDENT_TIMEOUT.
# Alerts incoming at roughly the same time are grouped into incidents.
# Use this to configure the timeout after which alerts will be
# considered separate incidents, in minutes.
# Example: If it is set for 30 minutes, and alerts come at 09:00,
# 09:20, 09:40, and 11:00, then the first 3 will be the same incident and
# the last one will begin a new incident.
# Default value: 60 (i.e. 1 hour).

View file

@ -57,3 +57,8 @@ branding:
orange: '#cb4b16'
red: '#dc322f'
# TODO logo
# Note you need to add your keys in authorized_keys format to:
# SSH_KEYS_DIR/USER.key
ssh_users:
- testuser

View file

@ -10,32 +10,81 @@ class ConfigClass(object):
All settings can be set via Environment variables.
If you use pipenv, you are encouraged to use the .env file.
See pipenv's documentation for more information.
"""
data_dir: str = attr.ib(default=os.getenv("DATA_DIR", "../data"))
"""
@param data_dir: Environment: DATA_DIR.
Directory to save persistent data in.
@type data_dir: C{unicode}
"""
# Web
web_endpoint: str = attr.ib(
default=os.getenv("WEB_ENDPOINT", r"tcp6:interface\:\::port=8080")
)
"""
@param web_endpoint: Environment: WEB_ENDPOINT.
Where we should listen for HTTP connections.
@type web_endpoint: C{unicode} -- Endpoint string. See twisted docs.
This defaults to TCP port 8080, but you can also use a UNIX socket,
which you can proxy from your web server.
See:
https://docs.twisted.org/en/stable/core/howto/endpoints.html#servers
https://klein.readthedocs.io/en/latest/examples/alternativerunning.html#example-ipv6-tls-unix-sockets-endpoints
@type web_endpoint: C{unicode} -- Endpoint string
"""
web_static_dir: str = attr.ib(default=os.getenv("WEB_STATIC_DIR", "../static"))
"""
@param web_static_dir: Environment: WEB_STATIC_DIR.
Directory to server static files from.
@type web_static_dir: C{unicode}
"""
# SSH
ssh_enabled: bool = attr.ib(default=os.getenv("SSH_ENABLED", "YES") != "")
"""
@param ssh_enabled: Environment: SSH_ENABLED.
If this environment variable is anything other than empty,
SSH_ENDPOINT will be used to listen for SSH connections.
@type ssh_enabled: C{unicode}
"""
ssh_endpoint: str = attr.ib(
default=os.getenv("SSH_ENDPOINT", r"tcp6:interface=\:\::port=2222")
)
"""
@param ssh_endpoint: Environment: SSH_ENDPOINT.
Where we should listen for SSH connections.
@type ssh_endpoint: C{unicode} -- Endpoint string. See twisted docs.
Where we should listen for SSH connections if SSH_ENABLED is
anything other than empty.
See:
https://docs.twisted.org/en/stable/core/howto/endpoints.html#servers
https://klein.readthedocs.io/en/latest/examples/alternativerunning.html#example-ipv6-tls-unix-sockets-endpoints
@type ssh_endpoint: C{unicode} -- Endpoint string
"""
ssh_key_size: int = attr.ib(default=int(os.getenv("SSH_KEY_SIZE", "4096")))
"""
@param ssh_key_size: Environment: SSH_KEY_SIZE.
Size for server's auto-generated SSH host key.
@type ssh_key_size: C{unicode}
"""
ssh_keys_dir: str = attr.ib(default=os.getenv("SSH_KEYS_DIR", "../data/ssh"))
"""
@param ssh_keys_dir: Environment: SSH_KEYS_DIR.
Directory to save SSH keys in.
This includes the server private key and users' public keys.
@type ssh_keys_dir: C{unicode}
"""
# Alerts processing
new_incident_timeout: timedelta = attr.ib(
default=timedelta(minutes=int(os.getenv("NEW_INCIDENT_TIMEOUT", "60")))
)
"""
@param new_incident_timeout: Environment: NEW_INCIDENT_TIMEOUT.
Alerts incoming at roughly the same time are grouped into incidents.
Use this to configure the timeout after which alerts will be
@ -46,24 +95,48 @@ class ConfigClass(object):
Default value: 60 (i.e. 1 hour).
"""
data_dir: str = attr.ib(default=os.getenv("DATA_DIR", "../data"))
# Web
web_endpoint: str = attr.ib(default=os.getenv("WEB_ENDPOINT", "unix:adler.socket"))
web_static_dir: str = attr.ib(default=os.getenv("WEB_STATIC_DIR", "../static"))
# SSH
ssh_enabled: bool = attr.ib(default=os.getenv("SSH_ENABLED", "") != "")
ssh_endpoint: str = attr.ib(
default=os.getenv("SSH_ENDPOINT", r"tcp6:interface=\:\::port=2222")
)
ssh_key_size: int = attr.ib(default=int(os.getenv("SSH_KEY_SIZE", "4096")))
ssh_keys_dir: str = attr.ib(default=os.getenv("SSH_KEYS_DIR", "../data/ssh"))
# Alerts processing
new_incident_timeout: timedelta = attr.ib(
default=timedelta(minutes=int(os.getenv("NEW_INCIDENT_TIMEOUT", "60")))
)
Config = ConfigClass()
if __name__ == "__main__":
import inspect
import sys
target = "dotenv.example"
if len(sys.argv) > 1:
target = sys.argv[1]
sc = inspect.getsource(ConfigClass).split("\n")[2:]
with open(target, "w") as f:
f.write(
"\n".join(
[
"#",
"# Automatically generated, manual changes will be lost",
"# Run: python -m adlermanager.Config dotenv.example",
"#",
"",
"",
]
)
)
in_doc = False
for line in sc:
if line.strip() == '"""':
in_doc = not in_doc
continue
if "@type" in line:
continue
if in_doc or line.strip().startswith("#"):
if "@param" in line:
f.write(f"# {line.split(':', 1)[1].strip()}")
else:
f.write(f"# {line[min(4, len(line)):].strip()}".strip())
f.write("\n")
if not in_doc and "getenv" in line:
parts = line.split('"')
f.write("\n")
f.write(f'#{parts[1]}="{parts[3]}"')
f.write("\n#\n")