Compare commits

...

50 commits

Author SHA1 Message Date
217bacb958
wip: add config file to webnsupdate
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 1m8s
/ test (push) Successful in 1m45s
/ report-size (push) Successful in 9s
2025-03-16 19:30:19 +01:00
08ea3271c1
ci: use nix-fast-build to speedup checks
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ test (push) Successful in 11s
/ report-size (push) Successful in 2s
Should be a noticeable improvement c:
2025-03-16 19:27:05 +01:00
8787adae30
chore(deps): lock file maintenance
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-03-14 22:00:31 +01:00
130c949723
chore(deps): lock file maintenance
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-03-13 22:00:46 +01:00
e236aa424b
fix(deps): update rust crate tokio to v1.44.1
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 0s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | tokio   | 1.44.0 | 1.44.1 |
2025-03-13 09:40:24 +01:00
baeb98a2e6
chore(deps): lock file maintenance
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-03-12 01:00:32 +01:00
9b41d7d2a5
fix(deps): update rust crate http to v1.3.1
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from  | to    |
| ---------- | ------- | ----- | ----- |
| crate      | http    | 1.3.0 | 1.3.1 |
2025-03-11 21:50:30 +01:00
33f8b1570d
fix(deps): update rust crate ring to v0.17.14
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | ring    | 0.17.13 | 0.17.14 |
2025-03-11 21:20:24 +01:00
06995416d2
fix(deps): update rust crate http to v1.3.0
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from  | to    |
| ---------- | ------- | ----- | ----- |
| crate      | http    | 1.2.0 | 1.3.0 |
2025-03-11 18:00:26 +01:00
f8848e669e
chore(deps): lock file maintenance
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-03-10 22:00:53 +01:00
72843b8f52
fix(deps): update rust crate clap to v4.5.32
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | clap    | 4.5.31 | 4.5.32 |
2025-03-10 21:50:28 +01:00
4cdffc20bc chore(config): migrate config .renovaterc.json
All checks were successful
/ check-renovaterc (push) Successful in 3s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 8s
/ check (module-nginx-test) (push) Successful in 10s
/ check (nextest) (push) Successful in 4s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-03-10 08:09:25 +01:00
cf66c77136
chore(deps): lock file maintenance
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-03-09 22:00:32 +01:00
5fc53886f2
chore(deps): pin dependencies
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 0s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-03-09 21:50:22 +01:00
881983dd6c ci: validate renovaterc
All checks were successful
/ check-renovaterc (push) Successful in 2s
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
Also use config:best-practices
2025-03-09 21:38:51 +01:00
632250d544
fix(deps): update rust crate serde to v1.0.219
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | serde   | 1.0.218 | 1.0.219 |
2025-03-09 20:20:28 +01:00
c71a8b418c
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-03-07 22:00:42 +01:00
873dd980ff
fix(deps): update rust crate tokio to v1.44.0
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | tokio   | 1.43.0 | 1.44.0 |
2025-03-07 21:20:23 +01:00
3e1140ffe5
fix(deps): update rust crate ring to v0.17.13
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 1s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | ring    | 0.17.12 | 0.17.13 |
2025-03-07 01:40:27 +01:00
6c78e8d78e
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-03-06 02:20:27 +01:00
3ed76c094c
fix(deps): update rust crate ring to v0.17.12
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | ring    | 0.17.11 | 0.17.12 |
2025-03-06 02:10:27 +01:00
f16c3b9138
fix(typo): typos corrected typ to typo
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
This is wrong for us (we use typ instead of type, not typo, because type
is a rust keyword).
2025-03-04 09:46:38 +01:00
42482574ac
chore(deps): lock file maintenance
Some checks failed
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Failing after 3s
/ report-size (push) Successful in 2s
2025-03-03 22:00:50 +01:00
855963bc85
fix(deps): update rust crate serde_json to v1.0.140
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package    | from    | to      |
| ---------- | ---------- | ------- | ------- |
| crate      | serde_json | 1.0.139 | 1.0.140 |
2025-03-03 10:20:22 +01:00
7cc182b23e
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-03-02 02:10:30 +01:00
bc62fd7c1d
chore(deps): update rust crate insta to v1.42.2
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | insta   | 1.42.1 | 1.42.2 |
2025-03-02 02:00:39 +01:00
cdef8078cc
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-03-01 22:00:54 +01:00
a6f0785dc3
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 8s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-03-01 00:00:35 +01:00
5d4c0fdb70
ci(renovate): don't overlap schedules
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
Branch creation schedule and automerge schedule should not overlap, or
we'll get PRs that don't contain updates, which stop renovate from
creating further PRs.
2025-02-28 23:52:07 +01:00
2a2f9ef06c
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-02-27 23:00:49 +01:00
ef6e955b90
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-02-24 17:00:45 +01:00
b581e2adf1
fix(deps): update rust crate clap to v4.5.31
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | clap    | 4.5.30 | 4.5.31 |
2025-02-24 16:50:22 +01:00
48034ec6e5
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-02-23 23:00:42 +01:00
593bee9024
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 2s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-02-23 00:10:23 +01:00
09345f2193
fix(deps): update rust crate ring to v0.17.11
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | ring    | 0.17.10 | 0.17.11 |
2025-02-22 19:50:25 +01:00
71d1e43ef2
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-02-21 23:00:24 +01:00
528aad1d8e
fix(deps): update rust crate ring to v0.17.10
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
| datasource | package | from   | to      |
| ---------- | ------- | ------ | ------- |
| crate      | ring    | 0.17.9 | 0.17.10 |
2025-02-21 18:30:23 +01:00
cb7e4d554b
fix(deps): update rust crate serde to v1.0.218
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
| datasource | package | from    | to      |
| ---------- | ------- | ------- | ------- |
| crate      | serde   | 1.0.217 | 1.0.218 |
2025-02-20 06:30:30 +01:00
01f53b2bf0
fix(deps): update rust crate serde_json to v1.0.139
All checks were successful
/ build (push) Successful in 2s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package    | from    | to      |
| ---------- | ---------- | ------- | ------- |
| crate      | serde_json | 1.0.138 | 1.0.139 |
2025-02-20 04:10:23 +01:00
60662ff1f0
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-02-19 17:41:04 +01:00
eaed7b2302
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-02-17 23:00:41 +01:00
8a04c2726f
fix(deps): update rust crate clap to v4.5.30
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | clap    | 4.5.29 | 4.5.30 |
2025-02-17 20:20:29 +01:00
1a88dbaeb2
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 4s
/ report-size (push) Successful in 2s
2025-02-16 20:30:52 +01:00
c41008f800
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-02-15 23:00:22 +01:00
3c18f07a2a
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 2s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-02-14 02:10:35 +01:00
0a5348097d
fix(deps): update rust crate ring to v0.17.9
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 6s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | ring    | 0.17.8 | 0.17.9 |
2025-02-14 02:00:26 +01:00
bdb27d7cb1
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 2s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
2025-02-12 23:00:37 +01:00
41c30372fb
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 7s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 2s
/ check (treefmt) (push) Successful in 3s
/ report-size (push) Successful in 2s
2025-02-11 21:40:30 +01:00
e99bc52de2
fix(deps): update rust crate clap to v4.5.29
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 6s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 7s
/ check (module-nginx-test) (push) Successful in 7s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 2s
| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| crate      | clap    | 4.5.28 | 4.5.29 |
2025-02-11 21:20:45 +01:00
338e296683
chore(deps): lock file maintenance
All checks were successful
/ build (push) Successful in 1s
/ check (clippy) (push) Successful in 3s
/ check (module-ipv4-only-test) (push) Successful in 7s
/ check (module-ipv4-test) (push) Successful in 7s
/ check (module-ipv6-only-test) (push) Successful in 6s
/ check (module-ipv6-test) (push) Successful in 6s
/ check (module-nginx-test) (push) Successful in 6s
/ check (nextest) (push) Successful in 3s
/ check (treefmt) (push) Successful in 2s
/ report-size (push) Successful in 1s
2025-02-10 09:20:41 +01:00
18 changed files with 659 additions and 547 deletions

View file

@ -1,40 +1,36 @@
on: [push] on: [push]
jobs: jobs:
check-renovaterc:
runs-on: nixos
steps:
- uses: https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Validate renovaterc
run: |
nix --version
nix shell nixpkgs#renovate --command renovate-config-validator
build: build:
runs-on: nixos runs-on: nixos
steps: steps:
- uses: https://git.salame.cl/actions/checkout@v4 - uses: https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Build Package - name: Build Package
run: | run: |
nix --version nix --version
nix build --print-build-logs .# nix build --print-build-logs .#
check: test:
needs: build # we use the built binaries in the checks needs: build # we use the built binaries in the checks
runs-on: nixos runs-on: nixos
strategy:
matrix:
check:
- treefmt
- clippy
- nextest
- module-ipv4-test
- module-ipv6-test
- module-nginx-test
- module-ipv4-only-test
- module-ipv6-only-test
steps: steps:
- uses: https://git.salame.cl/actions/checkout@v4 - uses: https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Check - name: Run tests
run: | run: |
set -x
nix --version nix --version
# shellcheck disable=SC2016 nix-fast-build --max-jobs 2 --no-nom --skip-cached --no-link \
nix build --print-build-logs '.#checks.x86_64-linux.${{ matrix.check }}' --flake ".#checks.$(nix eval --raw --impure --expr builtins.currentSystem)"
report-size: report-size:
runs-on: nixos runs-on: nixos
needs: build needs: build
steps: steps:
- uses: https://git.salame.cl/actions/checkout@v4 - uses: https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- run: nix --version - run: nix --version
- name: Generate size report - name: Generate size report
uses: https://git.salame.cl/jalil/nix-flake-outputs-size@main uses: https://git.salame.cl/jalil/nix-flake-outputs-size@main

View file

@ -9,7 +9,7 @@
"commitBodyTable": true, "commitBodyTable": true,
"dependencyDashboard": true, "dependencyDashboard": true,
"extends": [ "extends": [
"config:recommended" "config:best-practices"
], ],
"prCreation": "immediate", "prCreation": "immediate",
"cargo": { "cargo": {
@ -31,14 +31,14 @@
"lockFileMaintenance": { "lockFileMaintenance": {
"enabled": true, "enabled": true,
"recreateWhen": "always", "recreateWhen": "always",
"rebaseStalePrs": true, "rebaseWhen": "behind-base-branch",
"branchTopic": "lock-file-maintenance", "branchTopic": "lock-file-maintenance",
"commitMessageAction": "Lock file maintenance", "commitMessageAction": "Lock file maintenance",
"schedule": [ "schedule": [
"* 23 * * *" "* 22 * * *"
] ]
}, },
"automergeSchedule": [ "automergeSchedule": [
"* 0-1 * * *" "* 23 * * *"
] ]
} }

191
Cargo.lock generated
View file

@ -173,21 +173,21 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.8.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.12" version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -200,9 +200,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.28" version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -220,9 +220,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.27" version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -232,9 +232,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.28" version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -256,9 +256,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "console" name = "console"
version = "0.15.10" version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
dependencies = [ dependencies = [
"encode_unicode", "encode_unicode",
"libc", "libc",
@ -304,7 +304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
dependencies = [ dependencies = [
"nonempty", "nonempty",
"thiserror", "thiserror 1.0.69",
] ]
[[package]] [[package]]
@ -365,9 +365,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "http" name = "http"
version = "1.2.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -386,12 +386,12 @@ dependencies = [
[[package]] [[package]]
name = "http-body-util" name = "http-body-util"
version = "0.1.2" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-util", "futures-core",
"http", "http",
"http-body", "http-body",
"pin-project-lite", "pin-project-lite",
@ -399,9 +399,9 @@ dependencies = [
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]] [[package]]
name = "httpdate" name = "httpdate"
@ -446,14 +446,15 @@ dependencies = [
[[package]] [[package]]
name = "insta" name = "insta"
version = "1.42.1" version = "1.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
dependencies = [ dependencies = [
"console", "console",
"linked-hash-map", "linked-hash-map",
"once_cell", "once_cell",
"pin-project", "pin-project",
"serde",
"similar", "similar",
] ]
@ -471,9 +472,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.14" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
@ -483,9 +484,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.169" version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
@ -495,15 +496,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.15" version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.25" version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]] [[package]]
name = "matchers" name = "matchers"
@ -542,8 +543,8 @@ dependencies = [
"supports-unicode", "supports-unicode",
"terminal_size", "terminal_size",
"textwrap", "textwrap",
"thiserror", "thiserror 1.0.69",
"unicode-width", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
@ -565,9 +566,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.3" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
dependencies = [ dependencies = [
"adler2", "adler2",
] ]
@ -610,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.20.3" version = "1.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
[[package]] [[package]]
name = "overload" name = "overload"
@ -622,9 +623,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "owo-colors" name = "owo-colors"
version = "4.1.0" version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
@ -634,18 +635,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.9" version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
dependencies = [ dependencies = [
"pin-project-internal", "pin-project-internal",
] ]
[[package]] [[package]]
name = "pin-project-internal" name = "pin-project-internal"
version = "1.1.9" version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -666,18 +667,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.38" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -728,15 +729,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
"getrandom", "getrandom",
"libc", "libc",
"spin",
"untrusted", "untrusted",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -749,9 +749,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -762,30 +762,30 @@ dependencies = [
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.217" version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.217" version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -794,9 +794,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.138" version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -806,9 +806,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_path_to_error" name = "serde_path_to_error"
version = "0.1.16" version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
dependencies = [ dependencies = [
"itoa", "itoa",
"serde", "serde",
@ -858,9 +858,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
[[package]] [[package]]
name = "socket2" name = "socket2"
@ -872,12 +872,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -907,9 +901,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.98" version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -924,9 +918,9 @@ checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[package]] [[package]]
name = "terminal_size" name = "terminal_size"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
dependencies = [ dependencies = [
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.59.0",
@ -934,12 +928,12 @@ dependencies = [
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.16.1" version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
dependencies = [ dependencies = [
"unicode-linebreak", "unicode-linebreak",
"unicode-width", "unicode-width 0.2.0",
] ]
[[package]] [[package]]
@ -948,7 +942,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
] ]
[[package]] [[package]]
@ -962,6 +965,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.8" version = "1.1.8"
@ -974,9 +988,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.43.0" version = "1.44.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -1107,9 +1121,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.16" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "unicode-linebreak" name = "unicode-linebreak"
@ -1123,6 +1137,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.9.0" version = "0.9.0"
@ -1162,6 +1182,7 @@ dependencies = [
"ring", "ring",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.12",
"tokio", "tokio",
"tower-http", "tower-http",
"tracing", "tracing",

View file

@ -1,5 +1,3 @@
cargo-features = ["codegen-backend"]
[package] [package]
description = "An HTTP server using HTTP basic auth to make secure calls to nsupdate" description = "An HTTP server using HTTP basic auth to make secure calls to nsupdate"
name = "webnsupdate" name = "webnsupdate"
@ -29,13 +27,14 @@ miette = { version = "7", features = ["fancy"] }
ring = { version = "0.17", features = ["std"] } ring = { version = "0.17", features = ["std"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
thiserror = "2"
tokio = { version = "1", features = ["macros", "rt", "process", "io-util"] } tokio = { version = "1", features = ["macros", "rt", "process", "io-util"] }
tower-http = { version = "0.6", features = ["validate-request"] } tower-http = { version = "0.6", features = ["validate-request"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
[dev-dependencies] [dev-dependencies]
insta = "1" insta = { version = "1", features = ["json"] }
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"
@ -46,4 +45,3 @@ codegen-units = 1
[profile.dev] [profile.dev]
debug = 0 debug = 0
codegen-backend = "cranelift"

View file

@ -14,8 +14,38 @@ let
mkPackageOption mkPackageOption
types types
; ;
format = pkgs.formats.json { };
in in
{ {
# imports = [
# (lib.mkRenamedOptionModule
# [ "services" "webnsupdate" "passwordFile" ]
# [ "services" "webnsupdate" "settings" "password_file" ]
# )
# (lib.mkRenamedOptionModule
# [ "services" "webnsupdate" "keyFile" ]
# [ "services" "webnsupdate" "settings" "key_file" ]
# )
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "allowedIPVersion" ] ''
# This option was replaced with 'services.webnsupdate.settings.ip_type' which defaults to Both.
# '')
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "bindIp" ] ''
# This option was replaced with 'services.webnsupdate.settings.address' which defaults to 127.0.0.1:5353.
# '')
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "bindPort" ] ''
# This option was replaced with 'services.webnsupdate.settings.address' which defaults to 127.0.0.1:5353.
# '')
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "records" ] ''
# This option was replaced with 'services.webnsupdate.settings.records' which defaults to [].
# '')
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "recordsFile" ] ''
# This option was replaced with 'services.webnsupdate.settings.records' which defaults to [].
# '')
# (lib.mkRemovedOptionModule [ "services" "webnsupdate" "ttl" ] ''
# This option was replaced with 'services.webnsupdate.settings.ttl' which defaults to 600s.
# '')
# ];
options.services.webnsupdate = mkOption { options.services.webnsupdate = mkOption {
description = "An HTTP server for nsupdate."; description = "An HTTP server for nsupdate.";
default = { }; default = { };
@ -31,34 +61,35 @@ let
example = [ "--ip-source" ]; example = [ "--ip-source" ];
}; };
package = mkPackageOption pkgs "webnsupdate" { }; package = mkPackageOption pkgs "webnsupdate" { };
bindIp = mkOption { settings = mkOption {
description = "The webnsupdate JSON configuration";
default = { };
type = types.submodule {
freeformType = format.type;
options = {
address = mkOption {
description = '' description = ''
IP address to bind to. IP address and port to bind to.
Setting it to anything other than localhost is very insecure as Setting it to anything other than localhost is very
`webnsupdate` only supports plain HTTP and should always be behind a insecure as `webnsupdate` only supports plain HTTP and
reverse proxy. should always be behind a reverse proxy.
''; '';
type = types.str; type = types.str;
default = "localhost"; default = "127.0.0.1:5353";
example = "0.0.0.0"; example = "[::1]:5353";
}; };
bindPort = mkOption { ip_type = mkOption {
description = "Port to bind to.";
type = types.port;
default = 5353;
};
allowedIPVersion = mkOption {
description = ''The allowed IP versions to accept updates from.''; description = ''The allowed IP versions to accept updates from.'';
type = types.enum [ type = types.enum [
"both" "Both"
"ipv4-only" "Ipv4Only"
"ipv6-only" "Ipv6Only"
]; ];
default = "both"; default = "Both";
example = "ipv4-only"; example = "Ipv4Only";
}; };
passwordFile = mkOption { password_file = mkOption {
description = '' description = ''
The file where the password is stored. The file where the password is stored.
@ -67,7 +98,7 @@ let
type = types.path; type = types.path;
example = "/secrets/webnsupdate.pass"; example = "/secrets/webnsupdate.pass";
}; };
keyFile = mkOption { key_file = mkOption {
description = '' description = ''
The TSIG key that `nsupdate` should use. The TSIG key that `nsupdate` should use.
@ -79,34 +110,43 @@ let
}; };
ttl = mkOption { ttl = mkOption {
description = "The TTL that should be set on the zone records created by `nsupdate`."; description = "The TTL that should be set on the zone records created by `nsupdate`.";
type = types.ints.positive; default = {
default = 60; secs = 600;
};
example = {
secs = 600;
nanos = 50000;
};
type = types.submodule {
options = {
secs = mkOption {
description = "The TTL (in seconds) that should be set on the zone records created by `nsupdate`.";
example = 3600; example = 3600;
}; };
nanos = mkOption {
description = "The TTL (in nanoseconds) that should be set on the zone records created by `nsupdate`.";
default = 0;
example = 50000;
};
};
};
};
records = mkOption { records = mkOption {
description = '' description = ''
The fqdn of records that should be updated. The fqdn of records that should be updated.
Empty lines will be ignored, but whitespace will not be. Empty lines will be ignored, but whitespace will not be.
''; '';
type = types.nullOr types.lines; type = types.listOf types.str;
default = null; default = [ ];
example = '' example = [
example.com. "example.com."
"example.org."
example.org. "ci.example.org."
ci.example.org. ];
''; };
};
}; };
recordsFile = mkOption {
description = ''
The fqdn of records that should be updated.
Empty lines will be ignored, but whitespace will not be.
'';
type = types.nullOr types.path;
default = null;
example = "/secrets/webnsupdate.records";
}; };
user = mkOption { user = mkOption {
description = "The user to run as."; description = "The user to run as.";
@ -124,41 +164,14 @@ let
config = config =
let let
recordsFile = configFile = format.generate "webnsupdate.json" cfg.settings;
if cfg.recordsFile != null then cfg.recordsFile else pkgs.writeText "webnsrecords" cfg.records; args = lib.strings.escapeShellArgs ([ "--config=${configFile}" ] ++ cfg.extraArgs);
args = lib.strings.escapeShellArgs (
[
"--records"
recordsFile
"--key-file"
cfg.keyFile
"--password-file"
cfg.passwordFile
"--address"
cfg.bindIp
"--ip-type"
cfg.allowedIPVersion
"--port"
(builtins.toString cfg.bindPort)
"--ttl"
(builtins.toString cfg.ttl)
"--data-dir=%S/webnsupdate"
]
++ cfg.extraArgs
);
cmd = "${lib.getExe cfg.package} ${args}"; cmd = "${lib.getExe cfg.package} ${args}";
in in
lib.mkIf cfg.enable { lib.mkIf cfg.enable {
# FIXME: re-enable once I stop using the patched version of bind
# warnings = # warnings =
# lib.optional (!config.services.bind.enable) "`webnsupdate` is expected to be used alongside `bind`. This is an unsupported configuration."; # lib.optional (!config.services.bind.enable) "`webnsupdate` is expected to be used alongside `bind`. This is an unsupported configuration.";
assertions = [
{
assertion =
(cfg.records != null || cfg.recordsFile != null)
&& !(cfg.records != null && cfg.recordsFile != null);
message = "Exactly one of `services.webnsupdate.records` and `services.webnsupdate.recordsFile` must be set.";
}
];
systemd.services.webnsupdate = { systemd.services.webnsupdate = {
description = "Web interface for nsupdate."; description = "Web interface for nsupdate.";
@ -167,9 +180,10 @@ let
"network.target" "network.target"
"bind.service" "bind.service"
]; ];
preStart = "${cmd} verify"; preStart = "${lib.getExe cfg.package} verify ${configFile}";
path = [ pkgs.dig ]; path = [ pkgs.dig ];
startLimitIntervalSec = 60; startLimitIntervalSec = 60;
environment.DATA_DIR = "%S/webnsupdate";
serviceConfig = { serviceConfig = {
ExecStart = [ cmd ]; ExecStart = [ cmd ];
Type = "exec"; Type = "exec";

View file

@ -9,7 +9,7 @@
lastIPPath = "/var/lib/webnsupdate/last-ip.json"; lastIPPath = "/var/lib/webnsupdate/last-ip.json";
zoneFile = pkgs.writeText "${testDomain}.zoneinfo" '' zoneFile = pkgs.writeText "${testDomain}.zoneinfo" ''
$TTL 60 ; 1 minute $TTL 600 ; 10 minutes
$ORIGIN ${testDomain}. $ORIGIN ${testDomain}.
@ IN SOA ns1.${testDomain}. admin.${testDomain}. ( @ IN SOA ns1.${testDomain}. admin.${testDomain}. (
1 ; serial 1 ; serial
@ -73,20 +73,19 @@
webnsupdate = { webnsupdate = {
enable = true; enable = true;
bindIp = lib.mkDefault "127.0.0.1";
keyFile = "/etc/bind/rndc.key";
# test:test (user:password)
passwordFile = pkgs.writeText "webnsupdate.pass" "FQoNmuU1BKfg8qsU96F6bK5ykp2b0SLe3ZpB3nbtfZA";
package = self'.packages.webnsupdate; package = self'.packages.webnsupdate;
extraArgs = [ extraArgs = [ "-vvv" ]; # debug messages
"-vvv" # debug messages settings = {
"--ip-source=ConnectInfo" address = lib.mkDefault "127.0.0.1:5353";
key_file = "/etc/bind/rndc.key";
password_file = pkgs.writeText "webnsupdate.pass" "FQoNmuU1BKfg8qsU96F6bK5ykp2b0SLe3ZpB3nbtfZA"; # test:test
ip_source = lib.mkDefault "ConnectInfo";
records = [
"test1.${testDomain}."
"test2.${testDomain}."
"test3.${testDomain}."
]; ];
records = '' };
test1.${testDomain}.
test2.${testDomain}.
test3.${testDomain}.
'';
}; };
}; };
}; };
@ -97,7 +96,7 @@
webnsupdate-ipv4-machine webnsupdate-ipv4-machine
]; ];
config.services.webnsupdate.bindIp = "::1"; config.services.webnsupdate.settings.address = "[::1]:5353";
}; };
webnsupdate-nginx-machine = webnsupdate-nginx-machine =
@ -109,26 +108,26 @@
config.services = { config.services = {
# Use default IP Source # Use default IP Source
webnsupdate.extraArgs = lib.mkForce [ "-vvv" ]; # debug messages webnsupdate.settings.ip_source = "RightmostXForwardedFor";
nginx = { nginx = {
enable = true; enable = true;
recommendedProxySettings = true; recommendedProxySettings = true;
virtualHosts.webnsupdate.locations."/".proxyPass = virtualHosts.webnsupdate.locations."/".proxyPass =
"http://${config.services.webnsupdate.bindIp}:${builtins.toString config.services.webnsupdate.bindPort}"; "http://${config.services.webnsupdate.settings.address}";
}; };
}; };
}; };
webnsupdate-ipv4-only-machine = { webnsupdate-ipv4-only-machine = {
imports = [ webnsupdate-nginx-machine ]; imports = [ webnsupdate-nginx-machine ];
config.services.webnsupdate.allowedIPVersion = "ipv4-only"; config.services.webnsupdate.settings.ip_type = "Ipv4Only";
}; };
webnsupdate-ipv6-only-machine = { webnsupdate-ipv6-only-machine = {
imports = [ webnsupdate-nginx-machine ]; imports = [ webnsupdate-nginx-machine ];
config.services.webnsupdate.allowedIPVersion = "ipv6-only"; config.services.webnsupdate.settings.ip_type = "Ipv6Only";
}; };
# "A" for IPv4, "AAAA" for IPv6, "ANY" for any # "A" for IPv4, "AAAA" for IPv6, "ANY" for any
@ -158,9 +157,9 @@
STATIC_DOMAINS: list[str] = ["${testDomain}", "ns1.${testDomain}", "nsupdate.${testDomain}"] STATIC_DOMAINS: list[str] = ["${testDomain}", "ns1.${testDomain}", "nsupdate.${testDomain}"]
DYNAMIC_DOMAINS: list[str] = ["test1.${testDomain}", "test2.${testDomain}", "test3.${testDomain}"] DYNAMIC_DOMAINS: list[str] = ["test1.${testDomain}", "test2.${testDomain}", "test3.${testDomain}"]
def dig_cmd(domain: str, record: str, ip: str | None) -> str: def dig_cmd(domain: str, record: str, ip: str | None) -> tuple[str, str]:
match_ip = "" if ip is None else f"\\s\\+60\\s\\+IN\\s\\+{record}\\s\\+{ip}$" match_ip = "" if ip is None else f"\\s\\+600\\s\\+IN\\s\\+{record}\\s\\+{ip}$"
return f"dig @localhost {record} {domain} +noall +answer | grep '^{domain}.{match_ip}'" return f"dig @localhost {record} {domain} +noall +answer", f"grep '^{domain}.{match_ip}'"
def curl_cmd(domain: str, identity: str, path: str, query: dict[str, str]) -> str: def curl_cmd(domain: str, identity: str, path: str, query: dict[str, str]) -> str:
from urllib.parse import urlencode from urllib.parse import urlencode
@ -168,10 +167,16 @@
return f"{CURL} -u {identity} -X GET 'http://{domain}{"" if NGINX else ":5353"}/{path}{q}'" return f"{CURL} -u {identity} -X GET 'http://{domain}{"" if NGINX else ":5353"}/{path}{q}'"
def domain_available(domain: str, record: str, ip: str | None=None): def domain_available(domain: str, record: str, ip: str | None=None):
machine.succeed(dig_cmd(domain, record, ip)) dig, grep = dig_cmd(domain, record, ip)
rc, output = machine.execute(dig)
print(f"{dig}[{rc}]: {output}")
machine.succeed(f"{dig} | {grep}")
def domain_missing(domain: str, record: str, ip: str | None=None): def domain_missing(domain: str, record: str, ip: str | None=None):
machine.fail(dig_cmd(domain, record, ip)) dig, grep = dig_cmd(domain, record, ip)
rc, output = machine.execute(dig)
print(f"{dig}[{rc}]: {output}")
machine.fail(f"{dig} | {grep}")
def update_records(domain: str="localhost", /, *, path: str="update", **kwargs): def update_records(domain: str="localhost", /, *, path: str="update", **kwargs):
machine.succeed(curl_cmd(domain, "test:test", path, kwargs)) machine.succeed(curl_cmd(domain, "test:test", path, kwargs))

24
flake.lock generated
View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1738652123, "lastModified": 1741481578,
"narHash": "sha256-zdZek5FXK/k95J0vnLF0AMnYuZl4AjARq83blKuJBYY=", "narHash": "sha256-JBTSyJFQdO3V8cgcL08VaBUByEU6P5kXbTJN6R0PFQo=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "c7e015a5fcefb070778c7d91734768680188a9cd", "rev": "bb1c9567c43e4434f54e9481eb4b8e8e0d50f0b5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -22,11 +22,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1738453229, "lastModified": 1741352980,
"narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -37,11 +37,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1738680400, "lastModified": 1741851582,
"narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=", "narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "799ba5bffed04ced7067a91798353d360788b30d", "rev": "6607cf789e541e7873d40d3a8f7815ea92204f32",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -82,11 +82,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1738680491, "lastModified": 1739829690,
"narHash": "sha256-8X7tR3kFGkE7WEF5EXVkt4apgaN85oHZdoTGutCFs6I=", "narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "64dbb922d51a42c0ced6a7668ca008dded61c483", "rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github" "type": "github"
}, },
"original": { "original": {

217
src/config.rs Normal file
View file

@ -0,0 +1,217 @@
use std::{
fs::File,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
path::PathBuf,
time::Duration,
};
use axum_client_ip::SecureClientIpSource;
use miette::{Context, IntoDiagnostic};
#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)]
pub enum IpType {
#[default]
Both,
Ipv4Only,
Ipv6Only,
}
impl IpType {
pub fn valid_for_type(self, ip: IpAddr) -> bool {
match self {
IpType::Both => true,
IpType::Ipv4Only => ip.is_ipv4(),
IpType::Ipv6Only => ip.is_ipv6(),
}
}
}
impl std::fmt::Display for IpType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IpType::Both => f.write_str("both"),
IpType::Ipv4Only => f.write_str("ipv4-only"),
IpType::Ipv6Only => f.write_str("ipv6-only"),
}
}
}
impl std::str::FromStr for IpType {
type Err = miette::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"both" => Ok(Self::Both),
"ipv4-only" => Ok(Self::Ipv4Only),
"ipv6-only" => Ok(Self::Ipv6Only),
_ => miette::bail!("expected one of 'ipv4-only', 'ipv6-only' or 'both', got '{s}'"),
}
}
}
/// Webserver settings
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Server {
/// Ip address and port of the server
#[serde(default = "default_address")]
pub address: SocketAddr,
}
/// Password settings
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Password {
/// File containing password to match against
///
/// Should be of the format `username:password` and contain a single password
#[serde(default, skip_serializing_if = "Option::is_none")]
pub password_file: Option<PathBuf>,
/// Salt to get more unique hashed passwords and prevent table based attacks
#[serde(default = "default_salt")]
pub salt: Box<str>,
}
/// Records settings
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Records {
/// Time To Live (in seconds) to set on the DNS records
#[serde(default = "default_ttl")]
pub ttl: Duration,
/// List of domain names for which to update the IP when an update is requested
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[allow(clippy::struct_field_names)]
pub records: Vec<Box<str>>,
/// If provided, when an IPv6 prefix is provided with an update, this will be used to derive
/// the full IPv6 address of the client
#[serde(default, skip_serializing_if = "Option::is_none")]
pub client_id: Option<Ipv6Addr>,
/// If a client id is provided the ipv6 update will be ignored (only the prefix will be used).
/// This domain will point to the ipv6 address instead of the address derived from the client
/// id (usually this is the router).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub router_domain: Option<Box<str>>,
/// Set client IP source
///
/// see: <https://docs.rs/axum-client-ip/latest/axum_client_ip/enum.SecureClientIpSource.html>
#[serde(default = "default_ip_source")]
pub ip_source: SecureClientIpSource,
/// Set which IPs to allow updating (ipv4, ipv6 or both)
#[serde(default = "default_ip_type")]
pub ip_type: IpType,
/// Keyfile `nsupdate` should use
///
/// If specified, then `webnsupdate` must have read access to the file
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_file: Option<PathBuf>,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct Config {
/// Server Configuration
#[serde(flatten)]
pub server: Server,
/// Password Configuration
#[serde(flatten)]
pub password: Password,
/// Records Configuration
#[serde(flatten)]
pub records: Records,
}
impl Config {
/// Load the configuration without verifying it
pub fn load(path: &std::path::Path) -> miette::Result<Self> {
serde_json::from_reader::<File, Self>(
File::open(path)
.into_diagnostic()
.wrap_err_with(|| format!("failed open {}", path.display()))?,
)
.into_diagnostic()
.wrap_err_with(|| format!("failed to load configuration from {}", path.display()))
}
/// Ensure only a verified configuration is returned
pub fn verified(self) -> miette::Result<Self> {
self.verify()?;
Ok(self)
}
/// Verify the configuration
pub fn verify(&self) -> Result<(), Invalid> {
let mut invalid_records: Vec<miette::Error> = self
.records
.records
.iter()
.filter_map(|record| crate::records::validate_record_str(record).err())
.collect();
invalid_records.extend(
self.records
.router_domain
.as_ref()
.and_then(|domain| crate::records::validate_record_str(domain).err()),
);
let err = Invalid { invalid_records };
if err.invalid_records.is_empty() {
Ok(())
} else {
Err(err)
}
}
}
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
#[error("the configuration was invalid")]
pub struct Invalid {
#[related]
pub invalid_records: Vec<miette::Error>,
}
// --- Default Values (sadly serde doesn't have a way to specify a constant as a default value) ---
fn default_ttl() -> Duration {
super::DEFAULT_TTL
}
fn default_salt() -> Box<str> {
super::DEFAULT_SALT.into()
}
fn default_address() -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5353)
}
fn default_ip_source() -> SecureClientIpSource {
SecureClientIpSource::RightmostXForwardedFor
}
fn default_ip_type() -> IpType {
IpType::Both
}
#[test]
fn default_values_config_snapshot() {
let config: Config = serde_json::from_str("{}").unwrap();
insta::assert_json_snapshot!(config, @r#"
{
"address": "127.0.0.1:5353",
"salt": "UpdateMyDNS",
"ttl": {
"secs": 60,
"nanos": 0
},
"ip_source": "RightmostXForwardedFor",
"ip_type": "Both"
}
"#);
}

View file

@ -10,16 +10,18 @@ use axum::{
routing::get, routing::get,
Router, Router,
}; };
use axum_client_ip::{SecureClientIp, SecureClientIpSource}; use axum_client_ip::SecureClientIp;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use clap_verbosity_flag::Verbosity; use clap_verbosity_flag::Verbosity;
use config::Config;
use http::StatusCode; use http::StatusCode;
use miette::{bail, ensure, Context, IntoDiagnostic, Result}; use miette::{bail, ensure, Context, IntoDiagnostic, Result};
use tracing::{debug, error, info}; use tracing::{debug, error, info};
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
mod auth; mod auth;
mod config;
mod nsupdate; mod nsupdate;
mod password; mod password;
mod records; mod records;
@ -32,120 +34,52 @@ struct Opts {
#[command(flatten)] #[command(flatten)]
verbosity: Verbosity<clap_verbosity_flag::InfoLevel>, verbosity: Verbosity<clap_verbosity_flag::InfoLevel>,
/// Ip address of the server
#[arg(long, default_value = "127.0.0.1")]
address: IpAddr,
/// Port of the server
#[arg(long, default_value_t = 5353)]
port: u16,
/// File containing password to match against
///
/// Should be of the format `username:password` and contain a single password
#[arg(long)]
password_file: Option<PathBuf>,
/// Salt to get more unique hashed passwords and prevent table based attacks
#[arg(long, default_value = DEFAULT_SALT)]
salt: String,
/// Time To Live (in seconds) to set on the DNS records
#[arg(long, default_value_t = DEFAULT_TTL.as_secs())]
ttl: u64,
/// Data directory /// Data directory
#[arg(long, default_value = ".")] #[arg(long, env, default_value = ".")]
data_dir: PathBuf, data_dir: PathBuf,
/// File containing the records that should be updated when an update request is made
///
/// There should be one record per line:
///
/// ```text
/// example.com.
/// mail.example.com.
/// ```
#[arg(long)]
records: PathBuf,
/// Keyfile `nsupdate` should use
///
/// If specified, then `webnsupdate` must have read access to the file
#[arg(long)]
key_file: Option<PathBuf>,
/// Allow not setting a password /// Allow not setting a password
#[arg(long)] #[arg(long)]
insecure: bool, insecure: bool,
/// Set client IP source #[clap(flatten)]
/// config_or_command: ConfigOrCommand,
/// see: <https://docs.rs/axum-client-ip/latest/axum_client_ip/enum.SecureClientIpSource.html> }
#[clap(long, default_value = "RightmostXForwardedFor")]
ip_source: SecureClientIpSource,
/// Set which IPs to allow updating #[derive(clap::Args, Debug)]
#[clap(long, default_value_t = IpType::Both)] #[group(multiple = false)]
ip_type: IpType, struct ConfigOrCommand {
/// Path to the configuration file
#[arg(long, short)]
config: Option<PathBuf>,
#[clap(subcommand)] #[clap(subcommand)]
subcommand: Option<Cmd>, subcommand: Option<Cmd>,
} }
#[derive(Debug, Default, Clone, Copy)] impl ConfigOrCommand {
enum IpType { pub fn take(&mut self) -> (Option<PathBuf>, Option<Cmd>) {
#[default] (self.config.take(), self.subcommand.take())
Both,
IPv4Only,
IPv6Only,
}
impl IpType {
fn valid_for_type(self, ip: IpAddr) -> bool {
match self {
IpType::Both => true,
IpType::IPv4Only => ip.is_ipv4(),
IpType::IPv6Only => ip.is_ipv6(),
}
}
}
impl std::fmt::Display for IpType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IpType::Both => f.write_str("both"),
IpType::IPv4Only => f.write_str("ipv4-only"),
IpType::IPv6Only => f.write_str("ipv6-only"),
}
}
}
impl std::str::FromStr for IpType {
type Err = miette::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"both" => Ok(Self::Both),
"ipv4-only" => Ok(Self::IPv4Only),
"ipv6-only" => Ok(Self::IPv6Only),
_ => bail!("expected one of 'ipv4-only', 'ipv6-only' or 'both', got '{s}'"),
}
} }
} }
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
enum Cmd { enum Cmd {
Mkpasswd(password::Mkpasswd), Mkpasswd(password::Mkpasswd),
/// Verify the records file /// Verify the configuration file
Verify, Verify {
/// Path to the configuration file
config: PathBuf,
},
} }
impl Cmd { impl Cmd {
pub fn process(self, args: &Opts) -> Result<()> { pub fn process(self, args: &Opts) -> Result<()> {
match self { match self {
Cmd::Mkpasswd(mkpasswd) => mkpasswd.process(args), Cmd::Mkpasswd(mkpasswd) => mkpasswd.process(args),
Cmd::Verify => records::load(&args.records).map(drop), Cmd::Verify { config } => config::Config::load(&config) // load config
.and_then(Config::verified) // verify config
.map(drop), // ignore config data
} }
} }
} }
@ -168,7 +102,7 @@ struct AppState<'a> {
last_ips: std::sync::Arc<tokio::sync::Mutex<SavedIPs>>, last_ips: std::sync::Arc<tokio::sync::Mutex<SavedIPs>>,
/// The IP type for which to allow updates /// The IP type for which to allow updates
ip_type: IpType, ip_type: config::IpType,
} }
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
@ -211,33 +145,38 @@ impl SavedIPs {
} }
impl AppState<'static> { impl AppState<'static> {
fn from_args(args: &Opts) -> miette::Result<Self> { fn from_args(args: &Opts, config: &config::Config) -> miette::Result<Self> {
let Opts { let Opts {
verbosity: _, verbosity: _,
address: _,
port: _,
password_file: _,
data_dir, data_dir,
key_file,
insecure, insecure,
subcommand: _, config_or_command: _,
records,
salt: _,
ttl,
ip_source: _,
ip_type,
} = args; } = args;
// Set state let config::Records {
let ttl = Duration::from_secs(*ttl); ttl,
records,
client_id: _,
router_domain: _,
ip_source: _,
ip_type,
key_file,
} = &config.records;
// Use last registered IP address if available // Use last registered IP address if available
let ip_file = Box::leak(data_dir.join("last-ip.json").into_boxed_path()); let ip_file = Box::leak(data_dir.join("last-ip.json").into_boxed_path());
// Leak DNS records
let records: &[&str] = &*Vec::leak(
records
.iter()
.map(|record| &*Box::leak(record.clone()))
.collect(),
);
let state = AppState { let state = AppState {
ttl, ttl: *ttl,
// Load DNS records records,
records: records::load_no_verify(records)?,
// Load keyfile // Load keyfile
key_file: key_file key_file: key_file
.as_deref() .as_deref()
@ -340,34 +279,37 @@ fn main() -> Result<()> {
debug!("{args:?}"); debug!("{args:?}");
let config = match args.config_or_command.take() {
// process subcommand // process subcommand
if let Some(cmd) = args.subcommand.take() { (None, Some(cmd)) => return cmd.process(&args),
return cmd.process(&args); (Some(path), None) => {
let config = config::Config::load(&path)?;
if let Err(err) = config.verify() {
error!("failed to verify configuration: {err}");
} }
config
}
(None, None) | (Some(_), Some(_)) => unreachable!(
"bad state, one of config or subcommand should be available (clap should enforce this)"
),
};
// Initialize state // Initialize state
let state = AppState::from_args(&args)?; let state = AppState::from_args(&args, &config)?;
let Opts { let Opts {
verbosity: _, verbosity: _,
address: ip,
port,
password_file,
data_dir: _, data_dir: _,
key_file: _,
insecure, insecure,
subcommand: _, config_or_command: _,
records: _,
salt,
ttl: _,
ip_source,
ip_type,
} = args; } = args;
info!("checking environment"); info!("checking environment");
// Load password hash // Load password hash
let password_hash = password_file let password_hash = config
.password
.password_file
.map(|path| -> miette::Result<_> { .map(|path| -> miette::Result<_> {
let path = path.as_path(); let path = path.as_path();
let pass = std::fs::read_to_string(path).into_diagnostic()?; let pass = std::fs::read_to_string(path).into_diagnostic()?;
@ -398,11 +340,13 @@ fn main() -> Result<()> {
// Update DNS record with previous IPs (if available) // Update DNS record with previous IPs (if available)
let ips = state.last_ips.lock().await.clone(); let ips = state.last_ips.lock().await.clone();
let actions = ips let mut actions = ips
.ips() .ips()
.filter(|ip| ip_type.valid_for_type(*ip)) .filter(|ip| config.records.ip_type.valid_for_type(*ip))
.flat_map(|ip| nsupdate::Action::from_records(ip, state.ttl, state.records)); .flat_map(|ip| nsupdate::Action::from_records(ip, state.ttl, state.records))
.peekable();
if actions.peek().is_some() {
match nsupdate::nsupdate(state.key_file, actions).await { match nsupdate::nsupdate(state.key_file, actions).await {
Ok(status) => { Ok(status) => {
if !status.success() { if !status.success() {
@ -417,24 +361,30 @@ fn main() -> Result<()> {
.wrap_err("failed to update records with previous IP"); .wrap_err("failed to update records with previous IP");
} }
} }
}
// Create services // Create services
let app = Router::new().route("/update", get(update_records)); let app = Router::new().route("/update", get(update_records));
// if a password is provided, validate it // if a password is provided, validate it
let app = if let Some(pass) = password_hash { let app = if let Some(pass) = password_hash {
app.layer(auth::layer(Box::leak(pass), String::leak(salt))) app.layer(auth::layer(
Box::leak(pass),
Box::leak(config.password.salt),
))
} else { } else {
app app
} }
.layer(ip_source.into_extension()) .layer(config.records.ip_source.into_extension())
.with_state(state); .with_state(state);
let config::Server { address } = config.server;
// Start services // Start services
info!("starting listener on {ip}:{port}"); info!("starting listener on {address}");
let listener = tokio::net::TcpListener::bind(SocketAddr::new(ip, port)) let listener = tokio::net::TcpListener::bind(address)
.await .await
.into_diagnostic()?; .into_diagnostic()?;
info!("listening on {ip}:{port}"); info!("listening on {address}");
axum::serve( axum::serve(
listener, listener,
app.into_make_service_with_connect_info::<SocketAddr>(), app.into_make_service_with_connect_info::<SocketAddr>(),
@ -573,6 +523,15 @@ async fn trigger_update(
state: &AppState<'static>, state: &AppState<'static>,
) -> axum::response::Result<&'static str> { ) -> axum::response::Result<&'static str> {
let actions = nsupdate::Action::from_records(ip, state.ttl, state.records); let actions = nsupdate::Action::from_records(ip, state.ttl, state.records);
if actions.len() == 0 {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
"Nothing to do (e.g. we are ipv4-only but an ipv6 update was requested)",
)
.into());
}
match nsupdate::nsupdate(state.key_file, actions).await { match nsupdate::nsupdate(state.key_file, actions).await {
Ok(status) if status.success() => { Ok(status) if status.success() => {
let ips = { let ips = {

View file

@ -25,7 +25,7 @@ impl<'a> Action<'a> {
to: IpAddr, to: IpAddr,
ttl: Duration, ttl: Duration,
records: &'a [&'a str], records: &'a [&'a str],
) -> impl IntoIterator<Item = Self> + 'a { ) -> impl IntoIterator<Item = Self> + std::iter::ExactSizeIterator + 'a {
records records
.iter() .iter()
.map(move |&domain| Action::Reassign { domain, to, ttl }) .map(move |&domain| Action::Reassign { domain, to, ttl })
@ -37,14 +37,14 @@ impl std::fmt::Display for Action<'_> {
match self { match self {
Action::Reassign { domain, to, ttl } => { Action::Reassign { domain, to, ttl } => {
let ttl = ttl.as_secs(); let ttl = ttl.as_secs();
let typ = match to { let kind = match to {
IpAddr::V4(_) => "A", IpAddr::V4(_) => "A",
IpAddr::V6(_) => "AAAA", IpAddr::V6(_) => "AAAA",
}; };
// Delete previous record of type `typ` // Delete previous record of type `kind`
writeln!(f, "update delete {domain} {ttl} IN {typ}")?; writeln!(f, "update delete {domain} {ttl} IN {kind}")?;
// Add record with new IP // Add record with new IP
writeln!(f, "update add {domain} {ttl} IN {typ} {to}") writeln!(f, "update add {domain} {ttl} IN {kind} {to}")
} }
} }
} }
@ -91,7 +91,7 @@ fn update_ns_records<'a>(
) -> std::io::Result<()> { ) -> std::io::Result<()> {
writeln!(buf, "server 127.0.0.1")?; writeln!(buf, "server 127.0.0.1")?;
for action in actions { for action in actions {
writeln!(buf, "{action}")?; write!(buf, "{action}")?;
} }
writeln!(buf, "send")?; writeln!(buf, "send")?;
writeln!(buf, "quit") writeln!(buf, "quit")

View file

@ -4,7 +4,7 @@
//! records //! records
use std::io::Write; use std::io::Write;
use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::OpenOptionsExt;
use std::path::Path; use std::path::PathBuf;
use base64::prelude::*; use base64::prelude::*;
use miette::{Context, IntoDiagnostic, Result}; use miette::{Context, IntoDiagnostic, Result};
@ -20,11 +20,18 @@ pub struct Mkpasswd {
/// The password /// The password
password: String, password: String,
/// An application specific value
#[arg(long, default_value = crate::DEFAULT_SALT)]
salt: String,
/// The file to write the password to
password_file: Option<PathBuf>,
} }
impl Mkpasswd { impl Mkpasswd {
pub fn process(self, args: &crate::Opts) -> Result<()> { pub fn process(self, _args: &crate::Opts) -> Result<()> {
mkpasswd(self, args.password_file.as_deref(), &args.salt) mkpasswd(self)
} }
} }
@ -45,13 +52,16 @@ pub fn hash_identity(username: &str, password: &str, salt: &str) -> Digest {
} }
pub fn mkpasswd( pub fn mkpasswd(
Mkpasswd { username, password }: Mkpasswd, Mkpasswd {
password_file: Option<&Path>, username,
salt: &str, password,
salt,
password_file,
}: Mkpasswd,
) -> miette::Result<()> { ) -> miette::Result<()> {
let hash = hash_identity(&username, &password, salt); let hash = hash_identity(&username, &password, &salt);
let encoded = BASE64_URL_SAFE_NO_PAD.encode(hash.as_ref()); let encoded = BASE64_URL_SAFE_NO_PAD.encode(hash.as_ref());
let Some(path) = password_file else { let Some(path) = password_file.as_deref() else {
println!("{encoded}"); println!("{encoded}");
return Ok(()); return Ok(());
}; };

View file

@ -1,52 +1,9 @@
//! Deal with the DNS records //! Deal with the DNS records
use std::path::Path; use miette::{ensure, miette, LabeledSpan, Result};
use miette::{ensure, miette, Context, IntoDiagnostic, LabeledSpan, NamedSource, Result}; pub fn validate_record_str(record: &str) -> Result<()> {
validate_line(0, record).map_err(|err| err.with_source_code(String::from(record)))
/// Loads and verifies the records from a file
pub fn load(path: &Path) -> Result<()> {
let records = std::fs::read_to_string(path)
.into_diagnostic()
.wrap_err_with(|| format!("failed to read records from {}", path.display()))?;
verify(&records, path)?;
Ok(())
}
/// Load records without verifying them
pub fn load_no_verify(path: &Path) -> Result<&'static [&'static str]> {
let records = std::fs::read_to_string(path)
.into_diagnostic()
.wrap_err_with(|| format!("failed to read records from {}", path.display()))?;
if let Err(err) = verify(&records, path) {
tracing::error!("Failed to verify records: {err}");
}
// leak memory: we only do this here and it prevents a bunch of allocations
let records: &str = records.leak();
let records: Box<[&str]> = records.lines().collect();
Ok(Box::leak(records))
}
/// Verifies that a list of records is valid
pub fn verify(data: &str, path: &Path) -> Result<()> {
let mut offset = 0usize;
for line in data.lines() {
validate_line(offset, line).map_err(|err| {
err.with_source_code(NamedSource::new(
path.display().to_string(),
data.to_string(),
))
})?;
offset += line.len() + 1;
}
Ok(())
} }
fn validate_line(offset: usize, line: &str) -> Result<()> { fn validate_line(offset: usize, line: &str) -> Result<()> {
@ -156,7 +113,7 @@ fn validate_octet(offset: usize, octet: u8) -> Result<()> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::records::verify; use crate::records::validate_record_str;
macro_rules! assert_miette_snapshot { macro_rules! assert_miette_snapshot {
($diag:expr) => {{ ($diag:expr) => {{
@ -180,104 +137,51 @@ mod test {
#[test] #[test]
fn valid_records() -> miette::Result<()> { fn valid_records() -> miette::Result<()> {
verify( for record in [
"\ "example.com.",
example.com.\n\ "example.org.",
example.org.\n\ "example.net.",
example.net.\n\ "subdomain.example.com.",
subdomain.example.com.\n\ ] {
", validate_record_str(record)?;
std::path::Path::new("test_records_valid"), }
) Ok(())
} }
#[test] #[test]
fn hostname_too_long() { fn hostname_too_long() {
let err = verify( let err = validate_record_str("example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.net.").unwrap_err();
"\
example.com.\n\
example.org.\n\
example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.net.\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
#[test] #[test]
fn not_fqd() { fn not_fqd() {
let err = verify( let err = validate_record_str("example.net").unwrap_err();
"\
example.com.\n\
example.org.\n\
example.net\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
#[test] #[test]
fn empty_label() { fn empty_label() {
let err = verify( let err = validate_record_str("name..example.org.").unwrap_err();
"\
example.com.\n\
name..example.org.\n\
example.net.\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
#[test] #[test]
fn label_too_long() { fn label_too_long() {
let err = verify( let err = validate_record_str("name.an-entremely-long-label-that-should-not-exist-because-it-goes-against-the-spec.example.org.").unwrap_err();
"\
example.com.\n\
name.an-entremely-long-label-that-should-not-exist-because-it-goes-against-the-spec.example.org.\n\
example.net.\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
#[test] #[test]
fn invalid_ascii() { fn invalid_ascii() {
let err = verify( let err = validate_record_str("name.this-is-not-ascii-ß.example.org.").unwrap_err();
"\
example.com.\n\
name.this-is-not-ascii-ß.example.org.\n\
example.net.\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
#[test] #[test]
fn invalid_octet() { fn invalid_octet() {
let err = verify( let err =
"\ validate_record_str("name.this-character:-is-not-allowed.example.org.").unwrap_err();
example.com.\n\
name.this-character:-is-not-allowed.example.org.\n\
example.net.\n\
subdomain.example.com.\n\
",
std::path::Path::new("test_records_invalid"),
)
.unwrap_err();
assert_miette_snapshot!(err); assert_miette_snapshot!(err);
} }
} }

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\
× empty label × empty label
╭─[test_records_invalid:2:6] ╭────
1 │ example.com. 1 │ name..example.org.
2 │ name..example.org.
· ▲ · ▲
· ╰── label · ╰── label
3 │ example.net.
╰──── ╰────
help: each label should have at least one character help: each label should have at least one character

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\
× hostname too long (260 octets) × hostname too long (260 octets)
╭─[test_records_invalid:3:1] ╭────
2 │ example.org. 1 │ example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.net.
3 │ example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.example.net.
· ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── · ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
· ╰── this line · ╰── this line
4 │ subdomain.example.com.
╰──── ╰────
help: fully qualified domain names can be at most 255 characters long help: fully qualified domain names can be at most 255 characters long

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Hostname#Syntax\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Hostname#Syntax\(link)]8;;\
× invalid octet: '\xc3' × invalid octet: '\xc3'
╭─[test_records_invalid:2:24] ╭────
1 │ example.com. 1 │ name.this-is-not-ascii-ß.example.org.
2 │ name.this-is-not-ascii-ß.example.org.
· ┬ · ┬
· ╰── octet · ╰── octet
3 │ example.net.
╰──── ╰────
help: we only accept ascii characters help: we only accept ascii characters

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Hostname#Syntax\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Hostname#Syntax\(link)]8;;\
× invalid octet: ':' × invalid octet: ':'
╭─[test_records_invalid:2:20] ╭────
1 │ example.com. 1 │ name.this-character:-is-not-allowed.example.org.
2 │ name.this-character:-is-not-allowed.example.org.
· ┬ · ┬
· ╰── octet · ╰── octet
3 │ example.net.
╰──── ╰────
help: hostnames are only allowed to contain characters in [a-zA-Z0-9_-] help: hostnames are only allowed to contain characters in [a-zA-Z0-9_-]

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\
× label too long (78 octets) × label too long (78 octets)
╭─[test_records_invalid:2:6] ╭────
1 │ example.com. 1 │ name.an-entremely-long-label-that-should-not-exist-because-it-goes-against-the-spec.example.org.
2 │ name.an-entremely-long-label-that-should-not-exist-because-it-goes-against-the-spec.example.org.
· ───────────────────────────────────────┬────────────────────────────────────── · ───────────────────────────────────────┬──────────────────────────────────────
· ╰── label · ╰── label
3 │ example.net.
╰──── ╰────
help: labels should be at most 63 octets help: labels should be at most 63 octets

View file

@ -6,11 +6,9 @@ expression: out
]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\ ]8;;https://en.wikipedia.org/wiki/Fully_qualified_domain_name\(link)]8;;\
× not a fully qualified domain name × not a fully qualified domain name
╭─[test_records_invalid:3:11] ╭────
2 │ example.org. 1 │ example.net
3 │ example.net
· ┬ · ┬
· ╰── last character · ╰── last character
4 │ subdomain.example.com.
╰──── ╰────
help: hostname should be a fully qualified domain name (end with a '.') help: hostname should be a fully qualified domain name (end with a '.')