diff options
author | David Timber <dxdt@dev.snart.me> | 2022-05-16 17:22:33 +0800 |
---|---|---|
committer | David Timber <dxdt@dev.snart.me> | 2022-05-16 17:58:25 +0800 |
commit | 38b7a8e28bc5c102c226333769ed8f2b44a8ce76 (patch) | |
tree | 21a2078849d91796804ff2bf5760eab61b5b0611 | |
parent | 990a7a560c98dcbaa9c9e8deb0968819b646a664 (diff) |
Doc and licence ...
- Add Apache Licence Notice
- Doc "boot-report" and "check-dnssec"
- Change doc: upper (required)
-rw-r--r-- | .vscode/settings.json | 3 | ||||
-rw-r--r-- | COPYING | 19 | ||||
-rw-r--r-- | README.md | 112 | ||||
-rw-r--r-- | doc/config-fmt.md | 55 | ||||
-rw-r--r-- | src/conf/crontab | 2 | ||||
-rw-r--r-- | src/conf/py-sample/sample.jsonc | 14 | ||||
-rwxr-xr-x | src/palhm-dnssec-check.sh | 20 | ||||
-rwxr-xr-x | src/palhm.py | 19 | ||||
-rw-r--r-- | src/palhm/__init__.py | 19 | ||||
-rw-r--r-- | src/palhm/exceptions.py | 19 | ||||
-rw-r--r-- | src/palhm/mod/aws.py | 19 |
11 files changed, 273 insertions, 28 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d17ced8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.defaultInterpreterPath": "python3.9" +}
\ No newline at end of file @@ -0,0 +1,19 @@ +Copyright (c) 2022 David Timber <dxdt@dev.snart.me> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. @@ -19,7 +19,7 @@ resources for a backup task is not cost-efficient. This is where this script comes in to play. ## TL;DR -Goto [#Examples](#examples). +Goto [#Getting Started](#getting-started). ## Routine Task The Routine Task is a set of routines that are executed sequentially. It can @@ -122,7 +122,7 @@ integers. "type": "backup", "backend": "localfs", "backend-param": { - "root": "/media/backup/localhost", // (required) + "root": "/media/backup/localhost", // (REQUIRED) "dmode": "755", // (optional) mode for new directories "fmode": "644", // (optional) mode for new files "nb-copy-limit": "Infinity", // (optional) @@ -145,8 +145,8 @@ integers. "backend": "aws-s3", "backend-param": { "profile": "default", // (optional) AWS client profile. Defaults to "default" - "bucket": "palhm.test", // (required) S3 bucket name - "root": "/palhm/backup", // (required) + "bucket": "palhm.test", // (REQUIRED) S3 bucket name + "root": "/palhm/backup", // (REQUIRED) "sink-storage-class": "STANDARD", // (optional) storage class for new uploads "rot-storage-class": "STANDARD", // (optional) storage class for final uploads "nb-copy-limit": "Infinity", // (optional) @@ -202,17 +202,111 @@ On start, the objects in "root" and "http" groups will be built simultanesouly. On completion of all the objects in "http", the objects in the group "sql" and "ldap" will be built in order. +## Boot Report Mail +PALHM supports sending the "Boot Report Mail", which contains information about +the current boot. The mail is meant to be sent on boot up for system admins to +ensure no unexpected reboot event will go uninvestigated. This feature is used +in conjunction with [the systemd service](src/conf/palhm-boot-report.service) +or a rc.d script on SysVinit based systems. + +```jsonc +{ + "boot-report": { + // (REQUIRED) MUA for sending mail + /* stdout MUA + * For testing. Print contents to stdout. Doesn't actually send mail + */ + // "mua": "stdout", + "mua": "mailx", // mailx command MUA + // (REQUIRED) List of recipients + "mail-to": [ "root" ], + // The mail subject (optional) + "subject": "Custom Boot Report Subject from {hostname}", + /* + * The mail body header(leading yaml comments). Use line break(\n) for + * multi-line header (optional) + */ + "header": "Custom header content with {hostname} substitution.", + "uptime-since": true, // Include output of `uptime --since` (optional) + "uptime": true, // Include output of `uptime -p` (optional) + "bootid": true // Include kernel boot_id (optional) + } +} +``` + +## DNSSEC Check +If your domain is configured with DNSSEC[^2], PALHM can be used to check the +reachability of your RRs. Your domain will become unavailable when the keys are +misconfigured or you have missed the mandatory key rollover event. + +The DNSSEC Check task can be fabricated as backup tasks. This replaces the +original [palhm-dnssec-check.sh](src/palhm-dnssec-check.sh) script. The upstream +name servers must support DNSSEC. The task can be run from crontab. PALHM will +produce stderr output and return non-zero exit code, causing crond to send mail. + +```jsonc +{ + "tasks": [ + { + "id": "check-dnssec", + "type": "backup", + "backend": "null", + "objects": [ + { + "path": "example.com", // Placeholder + "pipeline": [ + /* + * Check if dig can query the record with the DNSSEC + * validation flag. Empty stdout with zero return code + * means SERVFAIL. + */ + { + "type": "exec-append", + "exec-id": "dig-dnssec", + "argv": [ "ANY", "example.com" ] + }, + /* + * Trap for empty dig output grep will return non-zero if + * dig have not produced any output + */ + { "type": "exec", "exec-id": "grep-any" } + ] + } + ] + } + ] +} +``` + +Here's the example crontab. + +```crontab +0 * * * * root systemd-run -qP -p User=palhm -p Nice=15 -p ProtectSystem=strict -p ReadOnlyPaths=/ -p PrivateDevices=true --wait /var/lib/PALHM/src/palhm.py -q run check-dnssec +``` + ## Config JSON Format See [doc/config-fmt.md](doc/config-fmt.md). ## Getting Started +The tasks can be run with the "run" subcommand. Run +'[src/palhm.py](src/palhm.py)' help for more. + +```sh +palhm.py run +# For crontab job +palhm.py -q run +palhm.py -q run check-dnssec +``` + ### Prerequisites -* Python 3.7 or higher +* Python 3.9 or higher * `json_reformat` command provided by **yajl** for jsonc support (optional) * **awscli** and **boto3** for aws-s3 backup backend (optional) ### Examples * [sample.jsonc](src/conf/py-sample/sample.jsonc) +* [crontab](src/conf/crontab) +* [systemd service](src/conf/palhm-boot-report.service) for Boot Report ## Files | Path | Desc | @@ -246,11 +340,13 @@ Also, you can always do a dry run of your backup task by setting the backend to To prepare for very unlikely events of [disasters](https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-options-in-the-cloud.html) affecting an entire AWS region, you may wish to implement cross-region -replication of S3 objects. The replication the S3 provides does not work on very -large objects. So replication of large objects across AWS regions has to be done -manually by a client - another implementation is required. +replication of S3 objects. Contrary to the document's recommendation, the +replication the S3 provides does not work on very large objects. So replication +of large objects across AWS regions has to be done manually by a client - +another implementation is required. Cross-region data transfer is costly, so this idea came to a halt. ## Footnotes [^1]: Even with SSDs, disrupting sequential reads decreases overall performance +[^2]: You really should if it's not diff --git a/doc/config-fmt.md b/doc/config-fmt.md index cdf8a93..0032f8e 100644 --- a/doc/config-fmt.md +++ b/doc/config-fmt.md @@ -181,8 +181,8 @@ is required, simply increase the verbosity with the `-v` option. | Include | MERGE | #### Predefined Pipeline Exec Object -* "type": "exec" **(required)** -* "exec-id": id of the Exec Definition Object **(required)** +* "type": "exec" **(REQUIRED)** +* "exec-id": id of the Exec Definition Object **(REQUIRED)** ```jsonc { @@ -192,9 +192,9 @@ is required, simply increase the verbosity with the `-v` option. ``` #### Appended Pipeline Exec Object -* "type": "exec-inline" **(required)** -* "exec-id": id of the Exec Definition Object **(required)** -* "argv": array of string, which is the argument vector to append **(required)** +* "type": "exec-inline" **(REQUIRED)** +* "exec-id": id of the Exec Definition Object **(REQUIRED)** +* "argv": array of string, which is the argument vector to append **(REQUIRED)** * "env": environment variable mapping object. See [#Exec Definition Object](#exec-definition-object) @@ -219,10 +219,10 @@ object does not require the "id" member. ``` #### Backup Task Definition Object -* "id": id string **(required)** -* "type": "backup" **(required)** +* "id": id string **(REQUIRED)** +* "type": "backup" **(REQUIRED)** * "backend": see [README.md#Backend-param](../README.md#Backend-param) - **(required)** + **(REQUIRED)** * "backend-param": see [README.md#Backend-param](../README.md#Backend-param) * "object-groups": array of [Backup Object Group Definition Objects](#backup-object-group-definition-object) @@ -241,7 +241,7 @@ object does not require the "id" member. ``` ##### Backup Object Group Definition Object -* "id": id string. Valid within the backup task **(required)** +* "id": id string. Valid within the backup task **(REQUIRED)** * "depends": array of other object group id strings on which the object group is dependent. The other groups must appear before the group definition. @@ -257,7 +257,7 @@ object does not require the "id" member. ``` ##### Backup Object Definition Object -* "path": path to the backup output on the backend **(required)** +* "path": path to the backup output on the backend **(REQUIRED)** * "group": the id of a [Backup Object Group Definition Object](#backup-object-group-definition-object) * "pipeline": array of @@ -291,8 +291,8 @@ before raising the exception. In this case, the exit code from the rest of child processes are not processed[^1]. #### Routine Task Definition Object -* "id": id string **(required)** -* "type": "routine" **(required)** +* "id": id string **(REQUIRED)** +* "type": "routine" **(REQUIRED)** * "routine": array of the id strings of * [Predefined Pipeline Exec Objects](#predefined-pipeline-exec-object) * [Appended Pipeline Exec Objects](#appended-pipeline-exec-object) @@ -365,5 +365,36 @@ be used on Unix systems. or without "SIG" prefix are accepted. Valid values include "TERM", "SIGTERM", 15, "INT", "SIGINT" and "2" +### boot-report +| ATTR | DESC | +| - | - | +| Key | "boot-report" | +| Required | NO | +| Include | MERGE except "mua" | + +The contents of the mail is in yaml format. The entirety of the body can be fed +into a yaml parser for machine processing. The "header" attribute defines the +header contents of the yaml document for humans. + +* "mua": mail user agent(MUA) front-end. Can only be specified once throughout + the config files **(required)** + * "stdout": prints the contents of the mail to stdout. Does not actually send + mail. The "mail-to" attribute is not used. For testing + * "mailx": use the mailx command to send mail +* "mail-to": array of boot report mail recipients. The values must be + recognisable by the MUA **(required)** +* "subject": title for mail. [Content Substitution + Variables](#content-substitution-variables) can be used +* "header": header content in mail body. The header is transformed to yaml + comments and prepended to the start of the yaml document. [Content + Substitution Variables](#content-substitution-variables) can be used +* "uptime-since": include output of `uptime --since` +* "uptime": include output of `uptime -p` +* "bootid": include boot_id(`/proc/sys/kernel/random/boot_id`) + +#### Content Substitution Variables +* {hostname}: The hostname. See + [platform.node()](https://docs.python.org/3/library/platform.html#platform.node) + ## Footnotes [^1]: they're most likely 141(terminated by SIGPIPE) diff --git a/src/conf/crontab b/src/conf/crontab index 35f52ce..d0eeda6 100644 --- a/src/conf/crontab +++ b/src/conf/crontab @@ -3,4 +3,4 @@ MAILTO="root" # Run default task every Sunday at midnight 0 0 * * sun root /var/lib/PALHM/src/palhm.py -q run # Check dnssec validity every hour -# 0 * * * * root systemd-run -qP -p Nice=15 -p ProtectSystem=strict -p ReadOnlyPaths=/ -p PrivateDevices=true --wait /var/lib/PALHM/src/palhm.py -q run check-dnssec +# 0 * * * * root systemd-run -qP -p User=palhm -p Nice=15 -p ProtectSystem=strict -p ReadOnlyPaths=/ -p PrivateDevices=true --wait /var/lib/PALHM/src/palhm.py -q run check-dnssec diff --git a/src/conf/py-sample/sample.jsonc b/src/conf/py-sample/sample.jsonc index 0da72a6..9aa4fa0 100644 --- a/src/conf/py-sample/sample.jsonc +++ b/src/conf/py-sample/sample.jsonc @@ -133,19 +133,19 @@ "path": "example.com", // Placeholder "pipeline": [ /* - * Check if dig can query the record with the DNSSEC - * validation flag. Empty stdout with zero return code - * means SERVFAIL. - */ + * Check if dig can query the record with the DNSSEC + * validation flag. Empty stdout with zero return code + * means SERVFAIL. + */ { "type": "exec-append", "exec-id": "dig-dnssec", "argv": [ "ANY", "example.com" ] }, /* - * Trap for empty dig output grep will return non-zero if - * dig have not produced any output - */ + * Trap for empty dig output grep will return non-zero if + * dig have not produced any output + */ { "type": "exec", "exec-id": "grep-any" } ] } diff --git a/src/palhm-dnssec-check.sh b/src/palhm-dnssec-check.sh index 122e51d..215b30f 100755 --- a/src/palhm-dnssec-check.sh +++ b/src/palhm-dnssec-check.sh @@ -3,6 +3,26 @@ # This script is a legacy. The same functionality can be implemented by setting # up a back up task. See [conf/py-sample/sample.jsonc] +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + do_query () { # dig returns 0 upon successful reception and parse of the response message. # All the other exit codes other than 0 will cause the script to terminate diff --git a/src/palhm.py b/src/palhm.py index f008736..b0ce2d4 100755 --- a/src/palhm.py +++ b/src/palhm.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import importlib import logging import os diff --git a/src/palhm/__init__.py b/src/palhm/__init__.py index 9f2ff4f..50f92ca 100644 --- a/src/palhm/__init__.py +++ b/src/palhm/__init__.py @@ -1,3 +1,22 @@ +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. import platform import sys import time diff --git a/src/palhm/exceptions.py b/src/palhm/exceptions.py index f63f2f9..e3d572a 100644 --- a/src/palhm/exceptions.py +++ b/src/palhm/exceptions.py @@ -1,2 +1,21 @@ +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. class InvalidConfigError (Exception): ... class APIFailError (Exception): ... diff --git a/src/palhm/mod/aws.py b/src/palhm/mod/aws.py index 01fb8bc..dbffe3b 100644 --- a/src/palhm/mod/aws.py +++ b/src/palhm/mod/aws.py @@ -1,3 +1,22 @@ +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. from concurrent.futures import ThreadPoolExecutor, Future from decimal import Decimal from enum import Enum |