chore: cargo update #9
12 changed files with 532 additions and 314 deletions
110
Cargo.lock
generated
110
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
|
@ -28,9 +28,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
|
@ -43,36 +43,36 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.4"
|
version = "3.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -208,15 +208,15 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.30"
|
version = "1.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
@ -237,6 +237,16 @@ dependencies = [
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap-verbosity-flag"
|
||||||
|
version = "2.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.20"
|
version = "4.5.20"
|
||||||
|
@ -269,9 +279,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console"
|
name = "console"
|
||||||
|
@ -436,9 +446,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.4.1"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -507,9 +517,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.159"
|
version = "0.2.161"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
|
@ -659,9 +669,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
@ -671,9 +681,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.87"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -689,9 +699,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.0"
|
version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -767,9 +777,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.17"
|
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 = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
|
@ -779,18 +789,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -799,9 +809,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.128"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -918,9 +928,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -962,18 +972,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -992,9 +1002,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -1159,6 +1169,7 @@ dependencies = [
|
||||||
"axum-client-ip",
|
"axum-client-ip",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"clap",
|
"clap",
|
||||||
|
"clap-verbosity-flag",
|
||||||
"http",
|
"http",
|
||||||
"insta",
|
"insta",
|
||||||
"miette",
|
"miette",
|
||||||
|
@ -1208,6 +1219,15 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
|
25
Cargo.toml
25
Cargo.toml
|
@ -7,27 +7,28 @@ version = "0.3.0-dev"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.7.7"
|
axum = "0.7"
|
||||||
axum-auth = { version = "0.7.0", default-features = false, features = [
|
axum-auth = { version = "0.7", default-features = false, features = [
|
||||||
"auth-basic",
|
"auth-basic",
|
||||||
] }
|
] }
|
||||||
axum-client-ip = "0.6.1"
|
axum-client-ip = "0.6"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22"
|
||||||
clap = { version = "4.5.20", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
http = "1.1.0"
|
clap-verbosity-flag = "2"
|
||||||
miette = { version = "7.2.0", features = ["fancy"] }
|
http = "1"
|
||||||
ring = { version = "0.17.8", features = ["std"] }
|
miette = { version = "7", features = ["fancy"] }
|
||||||
tokio = { version = "1.40.0", features = [
|
ring = { version = "0.17", features = ["std"] }
|
||||||
|
tokio = { version = "1", features = [
|
||||||
"macros",
|
"macros",
|
||||||
"rt",
|
"rt",
|
||||||
"process",
|
"process",
|
||||||
"io-util",
|
"io-util",
|
||||||
] }
|
] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "1.40.0"
|
insta = "1"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
debug = 0
|
debug = 0
|
||||||
|
|
33
flake-modules/default.nix
Normal file
33
flake-modules/default.nix
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.treefmt-nix.flakeModule
|
||||||
|
./package.nix
|
||||||
|
./overlay.nix
|
||||||
|
./module.nix
|
||||||
|
./tests.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
perSystem =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
# Setup formatters
|
||||||
|
treefmt = {
|
||||||
|
projectRootFile = "flake.nix";
|
||||||
|
programs = {
|
||||||
|
nixfmt.enable = true;
|
||||||
|
rustfmt.enable = true;
|
||||||
|
statix.enable = true;
|
||||||
|
typos.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.default = pkgs.mkShellNoCC {
|
||||||
|
packages = [
|
||||||
|
pkgs.cargo-insta
|
||||||
|
pkgs.cargo-udeps
|
||||||
|
pkgs.mold
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
196
flake-modules/module.nix
Normal file
196
flake-modules/module.nix
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
let
|
||||||
|
module =
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.services.webnsupdate;
|
||||||
|
inherit (lib)
|
||||||
|
mkOption
|
||||||
|
mkEnableOption
|
||||||
|
mkPackageOption
|
||||||
|
types
|
||||||
|
;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.webnsupdate = mkOption {
|
||||||
|
description = "An HTTP server for nsupdate.";
|
||||||
|
default = { };
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "webnsupdate";
|
||||||
|
extraArgs = mkOption {
|
||||||
|
description = ''
|
||||||
|
Extra arguments to be passed to the webnsupdate server command.
|
||||||
|
'';
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
example = [ "--ip-source" ];
|
||||||
|
};
|
||||||
|
package = mkPackageOption pkgs "webnsupdate" { };
|
||||||
|
bindIp = mkOption {
|
||||||
|
description = ''
|
||||||
|
IP address to bind to.
|
||||||
|
|
||||||
|
Setting it to anything other than localhost is very insecure as
|
||||||
|
`webnsupdate` only supports plain HTTP and should always be behind a
|
||||||
|
reverse proxy.
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
example = "0.0.0.0";
|
||||||
|
};
|
||||||
|
bindPort = mkOption {
|
||||||
|
description = "Port to bind to.";
|
||||||
|
type = types.port;
|
||||||
|
default = 5353;
|
||||||
|
};
|
||||||
|
passwordFile = mkOption {
|
||||||
|
description = ''
|
||||||
|
The file where the password is stored.
|
||||||
|
|
||||||
|
This file can be created by running `webnsupdate mkpasswd $USERNAME $PASSWORD`.
|
||||||
|
'';
|
||||||
|
type = types.path;
|
||||||
|
example = "/secrets/webnsupdate.pass";
|
||||||
|
};
|
||||||
|
keyFile = mkOption {
|
||||||
|
description = ''
|
||||||
|
The TSIG key that `nsupdate` should use.
|
||||||
|
|
||||||
|
This file will be passed to `nsupdate` through the `-k` option, so look
|
||||||
|
at `man 8 nsupdate` for information on the key's format.
|
||||||
|
'';
|
||||||
|
type = types.path;
|
||||||
|
example = "/secrets/webnsupdate.key";
|
||||||
|
};
|
||||||
|
ttl = mkOption {
|
||||||
|
description = "The TTL that should be set on the zone records created by `nsupdate`.";
|
||||||
|
type = types.ints.positive;
|
||||||
|
default = 60;
|
||||||
|
example = 3600;
|
||||||
|
};
|
||||||
|
records = mkOption {
|
||||||
|
description = ''
|
||||||
|
The fqdn of records that should be updated.
|
||||||
|
|
||||||
|
Empty lines will be ignored, but whitespace will not be.
|
||||||
|
'';
|
||||||
|
type = types.nullOr types.lines;
|
||||||
|
default = null;
|
||||||
|
example = ''
|
||||||
|
example.com.
|
||||||
|
|
||||||
|
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 {
|
||||||
|
description = "The user to run as.";
|
||||||
|
type = types.str;
|
||||||
|
default = "named";
|
||||||
|
};
|
||||||
|
group = mkOption {
|
||||||
|
description = "The group to run as.";
|
||||||
|
type = types.str;
|
||||||
|
default = "named";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
recordsFile =
|
||||||
|
if cfg.recordsFile != null then cfg.recordsFile else pkgs.writeText "webnsrecords" cfg.records;
|
||||||
|
args = lib.strings.escapeShellArgs (
|
||||||
|
[
|
||||||
|
"--records"
|
||||||
|
recordsFile
|
||||||
|
"--key-file"
|
||||||
|
cfg.keyFile
|
||||||
|
"--password-file"
|
||||||
|
cfg.passwordFile
|
||||||
|
"--address"
|
||||||
|
cfg.bindIp
|
||||||
|
"--port"
|
||||||
|
(builtins.toString cfg.bindPort)
|
||||||
|
"--ttl"
|
||||||
|
(builtins.toString cfg.ttl)
|
||||||
|
"--data-dir=%S/webnsupdate"
|
||||||
|
]
|
||||||
|
++ cfg.extraArgs
|
||||||
|
);
|
||||||
|
cmd = "${lib.getExe cfg.package} ${args}";
|
||||||
|
in
|
||||||
|
lib.mkIf cfg.enable {
|
||||||
|
# warnings =
|
||||||
|
# lib.optional (!config.services.bind.enable) "`webnsupdate` is expected to be used alongside `bind`. This is an unsopported 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 = {
|
||||||
|
description = "Web interface for nsupdate.";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [
|
||||||
|
"network.target"
|
||||||
|
"bind.service"
|
||||||
|
];
|
||||||
|
preStart = "${cmd} verify";
|
||||||
|
path = [ pkgs.dig ];
|
||||||
|
startLimitIntervalSec = 60;
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = [ cmd ];
|
||||||
|
Type = "exec";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
# User and group
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
# Runtime directory and mode
|
||||||
|
RuntimeDirectory = "webnsupdate";
|
||||||
|
RuntimeDirectoryMode = "0750";
|
||||||
|
# Cache directory and mode
|
||||||
|
CacheDirectory = "webnsupdate";
|
||||||
|
CacheDirectoryMode = "0750";
|
||||||
|
# Logs directory and mode
|
||||||
|
LogsDirectory = "webnsupdate";
|
||||||
|
LogsDirectoryMode = "0750";
|
||||||
|
# State directory and mode
|
||||||
|
StateDirectory = "webnsupdate";
|
||||||
|
StateDirectoryMode = "0750";
|
||||||
|
# New file permissions
|
||||||
|
UMask = "0027";
|
||||||
|
# Security
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.nixosModules = {
|
||||||
|
default = module;
|
||||||
|
webnsupdate = module;
|
||||||
|
};
|
||||||
|
}
|
5
flake-modules/overlay.nix
Normal file
5
flake-modules/overlay.nix
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
flake = {
|
||||||
|
overlays.default = _final: prev: { webnsupdate = prev.callPackage ../default.nix { }; };
|
||||||
|
};
|
||||||
|
}
|
24
flake-modules/package.nix
Normal file
24
flake-modules/package.nix
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
packages =
|
||||||
|
let
|
||||||
|
webnsupdate = pkgs.callPackage ../default.nix { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit webnsupdate;
|
||||||
|
default = webnsupdate;
|
||||||
|
cargo-update = pkgs.writeShellApplication {
|
||||||
|
name = "cargo-update-lockfile";
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
cargo
|
||||||
|
gnused
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
CARGO_TERM_COLOR=never cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
144
flake-modules/tests.nix
Normal file
144
flake-modules/tests.nix
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
{ self, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{ pkgs, self', ... }:
|
||||||
|
{
|
||||||
|
checks =
|
||||||
|
let
|
||||||
|
testDomain = "webnstest.example";
|
||||||
|
dynamicZonesDir = "/var/lib/named/zones";
|
||||||
|
zoneFile = pkgs.writeText "${testDomain}.zoneinfo" ''
|
||||||
|
$ORIGIN .
|
||||||
|
$TTL 60 ; 1 minute
|
||||||
|
${testDomain} IN SOA ns1.${testDomain}. admin.${testDomain}. (
|
||||||
|
1 ; serial
|
||||||
|
21600 ; refresh (6 hours)
|
||||||
|
3600 ; retry (1 hour)
|
||||||
|
604800 ; expire (1 week)
|
||||||
|
86400) ; negative caching TTL (1 day)
|
||||||
|
|
||||||
|
IN NS ns1.${testDomain}.
|
||||||
|
$ORIGIN ${testDomain}.
|
||||||
|
${testDomain}. IN A 127.0.0.1
|
||||||
|
${testDomain}. IN AAAA ::1
|
||||||
|
ns1 IN A 127.0.0.1
|
||||||
|
ns1 IN AAAA ::1
|
||||||
|
nsupdate IN A 127.0.0.1
|
||||||
|
nsupdate IN AAAA ::1
|
||||||
|
'';
|
||||||
|
|
||||||
|
webnsupdate-machine = {
|
||||||
|
imports = [ self.nixosModules.webnsupdate ];
|
||||||
|
|
||||||
|
config = {
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.dig
|
||||||
|
pkgs.curl
|
||||||
|
];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
webnsupdate = {
|
||||||
|
enable = true;
|
||||||
|
bindIp = "127.0.0.1";
|
||||||
|
keyFile = "/etc/bind/rndc.key";
|
||||||
|
# test:test (user:password)
|
||||||
|
passwordFile = pkgs.writeText "webnsupdate.pass" "FQoNmuU1BKfg8qsU96F6bK5ykp2b0SLe3ZpB3nbtfZA";
|
||||||
|
package = self'.packages.webnsupdate;
|
||||||
|
extraArgs = [
|
||||||
|
"-vvv" # debug messages
|
||||||
|
"--ip-source=ConnectInfo"
|
||||||
|
];
|
||||||
|
records = ''
|
||||||
|
test1.${testDomain}.
|
||||||
|
test2.${testDomain}.
|
||||||
|
test3.${testDomain}.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
bind = {
|
||||||
|
enable = true;
|
||||||
|
zones.${testDomain} = {
|
||||||
|
master = true;
|
||||||
|
file = "${dynamicZonesDir}/${testDomain}";
|
||||||
|
extraConfig = ''
|
||||||
|
allow-update { key rndc-key; };
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.bind.preStart = ''
|
||||||
|
# shellcheck disable=SC2211,SC1127
|
||||||
|
rm -f ${dynamicZonesDir}/* # reset dynamic zones
|
||||||
|
|
||||||
|
${pkgs.coreutils}/bin/mkdir -m 0755 -p ${dynamicZonesDir}
|
||||||
|
chown "named" ${dynamicZonesDir}
|
||||||
|
chown "named" /var/lib/named
|
||||||
|
|
||||||
|
# copy dynamic zone's file to the dynamic zones dir
|
||||||
|
cp ${zoneFile} ${dynamicZonesDir}/${testDomain}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
module-test = pkgs.testers.runNixOSTest {
|
||||||
|
name = "webnsupdate-module";
|
||||||
|
nodes.machine = webnsupdate-machine;
|
||||||
|
testScript = ''
|
||||||
|
machine.start(allow_reboot=True)
|
||||||
|
machine.wait_for_unit("webnsupdate.service")
|
||||||
|
|
||||||
|
# ensure base DNS records area available
|
||||||
|
with subtest("query base DNS records"):
|
||||||
|
machine.succeed("dig @127.0.0.1 ${testDomain} | grep ^${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 ns1.${testDomain} | grep ^ns1.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 nsupdate.${testDomain} | grep ^nsupdate.${testDomain}")
|
||||||
|
|
||||||
|
# ensure webnsupdate managed records are missing
|
||||||
|
with subtest("query webnsupdate DNS records (fail)"):
|
||||||
|
machine.fail("dig @127.0.0.1 test1.${testDomain} | grep ^test1.${testDomain}")
|
||||||
|
machine.fail("dig @127.0.0.1 test2.${testDomain} | grep ^test2.${testDomain}")
|
||||||
|
machine.fail("dig @127.0.0.1 test3.${testDomain} | grep ^test3.${testDomain}")
|
||||||
|
|
||||||
|
with subtest("update webnsupdate DNS records (invalid auth)"):
|
||||||
|
machine.fail("curl --fail --silent -u test1:test1 -X GET http://localhost:5353/update")
|
||||||
|
machine.fail("cat /var/lib/webnsupdate/last-ip") # no last-ip set yet
|
||||||
|
|
||||||
|
# ensure webnsupdate managed records are missing
|
||||||
|
with subtest("query webnsupdate DNS records (fail)"):
|
||||||
|
machine.fail("dig @127.0.0.1 test1.${testDomain} | grep ^test1.${testDomain}")
|
||||||
|
machine.fail("dig @127.0.0.1 test2.${testDomain} | grep ^test2.${testDomain}")
|
||||||
|
machine.fail("dig @127.0.0.1 test3.${testDomain} | grep ^test3.${testDomain}")
|
||||||
|
|
||||||
|
with subtest("update webnsupdate DNS records (valid auth)"):
|
||||||
|
machine.succeed("curl --fail --silent -u test:test -X GET http://localhost:5353/update")
|
||||||
|
machine.succeed("cat /var/lib/webnsupdate/last-ip")
|
||||||
|
|
||||||
|
# ensure webnsupdate managed records are available
|
||||||
|
with subtest("query webnsupdate DNS records (succeed)"):
|
||||||
|
machine.succeed("dig @127.0.0.1 test1.${testDomain} | grep ^test1.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 test2.${testDomain} | grep ^test2.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 test3.${testDomain} | grep ^test3.${testDomain}")
|
||||||
|
|
||||||
|
machine.reboot()
|
||||||
|
machine.succeed("cat /var/lib/webnsupdate/last-ip")
|
||||||
|
machine.wait_for_unit("webnsupdate.service")
|
||||||
|
machine.succeed("cat /var/lib/webnsupdate/last-ip")
|
||||||
|
|
||||||
|
# ensure base DNS records area available after a reboot
|
||||||
|
with subtest("query base DNS records"):
|
||||||
|
machine.succeed("dig @127.0.0.1 ${testDomain} | grep ^${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 ns1.${testDomain} | grep ^ns1.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 nsupdate.${testDomain} | grep ^nsupdate.${testDomain}")
|
||||||
|
|
||||||
|
# ensure webnsupdate managed records are available after a reboot
|
||||||
|
with subtest("query webnsupdate DNS records (succeed)"):
|
||||||
|
machine.succeed("dig @127.0.0.1 test1.${testDomain} | grep ^test1.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 test2.${testDomain} | grep ^test2.${testDomain}")
|
||||||
|
machine.succeed("dig @127.0.0.1 test3.${testDomain} | grep ^test3.${testDomain}")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
23
flake.lock
23
flake.lock
|
@ -39,7 +39,8 @@
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"systems": "systems"
|
"systems": "systems",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
|
@ -56,6 +57,26 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729613947,
|
||||||
|
"narHash": "sha256-XGOvuIPW1XRfPgHtGYXd5MAmJzZtOuwlfKDgxX5KT3s=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "aac86347fb5063960eccb19493e0cadcdb4205ca",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
67
flake.nix
67
flake.nix
|
@ -7,73 +7,16 @@
|
||||||
url = "github:hercules-ci/flake-parts";
|
url = "github:hercules-ci/flake-parts";
|
||||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
treefmt-nix = {
|
||||||
|
url = "github:numtide/treefmt-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
inputs:
|
inputs:
|
||||||
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
imports = [ ./flake-modules ];
|
||||||
systems = import inputs.systems;
|
systems = import inputs.systems;
|
||||||
perSystem =
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
self',
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
packages =
|
|
||||||
let
|
|
||||||
webnsupdate = pkgs.callPackage ./default.nix { };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
inherit webnsupdate;
|
|
||||||
default = webnsupdate;
|
|
||||||
cargo-update = pkgs.writeShellApplication {
|
|
||||||
name = "cargo-update-lockfile";
|
|
||||||
runtimeInputs = with pkgs; [
|
|
||||||
cargo
|
|
||||||
gnused
|
|
||||||
];
|
|
||||||
text = ''
|
|
||||||
CARGO_TERM_COLOR=never cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
formatter = pkgs.nixfmt-rfc-style;
|
|
||||||
|
|
||||||
checks = {
|
|
||||||
fmtRust = pkgs.callPackage ./run-cmd.nix {
|
|
||||||
src = inputs.self;
|
|
||||||
name = "fmt-rust";
|
|
||||||
extraNativeBuildInputs = [ pkgs.rustfmt ];
|
|
||||||
cmd = "${lib.getExe pkgs.cargo} fmt --all --check --verbose";
|
|
||||||
};
|
|
||||||
fmtNix = pkgs.callPackage ./run-cmd.nix {
|
|
||||||
src = inputs.self;
|
|
||||||
name = "fmt-nix";
|
|
||||||
cmd = "${lib.getExe self'.formatter} --check .";
|
|
||||||
};
|
|
||||||
lintNix = pkgs.callPackage ./run-cmd.nix {
|
|
||||||
src = inputs.self;
|
|
||||||
name = "lint-nix";
|
|
||||||
cmd = "${lib.getExe pkgs.statix} check .";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
packages = [
|
|
||||||
pkgs.cargo-insta
|
|
||||||
pkgs.cargo-udeps
|
|
||||||
pkgs.mold
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake = {
|
|
||||||
overlays.default = final: prev: { webnsupdate = final.callPackage ./default.nix { }; };
|
|
||||||
|
|
||||||
nixosModules.default = ./module.nix;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
177
module.nix
177
module.nix
|
@ -1,177 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.services.webnsupdate;
|
|
||||||
inherit (lib) mkOption mkEnableOption types;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.webnsupdate = mkOption {
|
|
||||||
description = "An HTTP server for nsupdate.";
|
|
||||||
default = { };
|
|
||||||
type = types.submodule {
|
|
||||||
options = {
|
|
||||||
enable = mkEnableOption "webnsupdate";
|
|
||||||
extraArgs = mkOption {
|
|
||||||
description = ''
|
|
||||||
Extra arguments to be passed to the webnsupdate server command.
|
|
||||||
'';
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
example = [ "--ip-source" ];
|
|
||||||
};
|
|
||||||
bindIp = mkOption {
|
|
||||||
description = ''
|
|
||||||
IP address to bind to.
|
|
||||||
|
|
||||||
Setting it to anything other than localhost is very insecure as
|
|
||||||
`webnsupdate` only supports plain HTTP and should always be behind a
|
|
||||||
reverse proxy.
|
|
||||||
'';
|
|
||||||
type = types.str;
|
|
||||||
default = "localhost";
|
|
||||||
example = "0.0.0.0";
|
|
||||||
};
|
|
||||||
bindPort = mkOption {
|
|
||||||
description = "Port to bind to.";
|
|
||||||
type = types.port;
|
|
||||||
default = 5353;
|
|
||||||
};
|
|
||||||
passwordFile = mkOption {
|
|
||||||
description = ''
|
|
||||||
The file where the password is stored.
|
|
||||||
|
|
||||||
This file can be created by running `webnsupdate mkpasswd $USERNAME $PASSWORD`.
|
|
||||||
'';
|
|
||||||
type = types.path;
|
|
||||||
example = "/secrets/webnsupdate.pass";
|
|
||||||
};
|
|
||||||
keyFile = mkOption {
|
|
||||||
description = ''
|
|
||||||
The TSIG key that `nsupdate` should use.
|
|
||||||
|
|
||||||
This file will be passed to `nsupdate` through the `-k` option, so look
|
|
||||||
at `man 8 nsupdate` for information on the key's format.
|
|
||||||
'';
|
|
||||||
type = types.path;
|
|
||||||
example = "/secrets/webnsupdate.key";
|
|
||||||
};
|
|
||||||
ttl = mkOption {
|
|
||||||
description = "The TTL that should be set on the zone records created by `nsupdate`.";
|
|
||||||
type = types.ints.positive;
|
|
||||||
default = 60;
|
|
||||||
example = 3600;
|
|
||||||
};
|
|
||||||
records = mkOption {
|
|
||||||
description = ''
|
|
||||||
The fqdn of records that should be updated.
|
|
||||||
|
|
||||||
Empty lines will be ignored, but whitespace will not be.
|
|
||||||
'';
|
|
||||||
type = types.nullOr types.lines;
|
|
||||||
default = null;
|
|
||||||
example = ''
|
|
||||||
example.com.
|
|
||||||
|
|
||||||
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 {
|
|
||||||
description = "The user to run as.";
|
|
||||||
type = types.str;
|
|
||||||
default = "named";
|
|
||||||
};
|
|
||||||
group = mkOption {
|
|
||||||
description = "The group to run as.";
|
|
||||||
type = types.str;
|
|
||||||
default = "named";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config =
|
|
||||||
let
|
|
||||||
recordsFile =
|
|
||||||
if cfg.recordsFile != null then cfg.recordsFile else pkgs.writeText "webnsrecords" cfg.records;
|
|
||||||
args = lib.strings.escapeShellArgs (
|
|
||||||
[
|
|
||||||
"--records"
|
|
||||||
recordsFile
|
|
||||||
"--key-file"
|
|
||||||
cfg.keyFile
|
|
||||||
"--password-file"
|
|
||||||
cfg.passwordFile
|
|
||||||
"--address"
|
|
||||||
cfg.bindIp
|
|
||||||
"--port"
|
|
||||||
(builtins.toString cfg.bindPort)
|
|
||||||
"--ttl"
|
|
||||||
(builtins.toString cfg.ttl)
|
|
||||||
]
|
|
||||||
++ cfg.extraArgs
|
|
||||||
);
|
|
||||||
cmd = "${lib.getExe pkgs.webnsupdate} ${args}";
|
|
||||||
in
|
|
||||||
lib.mkIf cfg.enable {
|
|
||||||
# warnings =
|
|
||||||
# lib.optional (!config.services.bind.enable) "`webnsupdate` is expected to be used alongside `bind`. This is an unsopported 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 = {
|
|
||||||
description = "Web interface for nsupdate.";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [
|
|
||||||
"network.target"
|
|
||||||
"bind.service"
|
|
||||||
];
|
|
||||||
preStart = "${cmd} verify";
|
|
||||||
path = [ pkgs.dig ];
|
|
||||||
startLimitIntervalSec = 60;
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = [ cmd ];
|
|
||||||
Type = "exec";
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = "10s";
|
|
||||||
# User and group
|
|
||||||
User = cfg.user;
|
|
||||||
Group = cfg.group;
|
|
||||||
# Runtime directory and mode
|
|
||||||
RuntimeDirectory = "webnsupdate";
|
|
||||||
RuntimeDirectoryMode = "0750";
|
|
||||||
# Cache directory and mode
|
|
||||||
CacheDirectory = "webnsupdate";
|
|
||||||
CacheDirectoryMode = "0750";
|
|
||||||
# Logs directory and mode
|
|
||||||
LogsDirectory = "webnsupdate";
|
|
||||||
LogsDirectoryMode = "0750";
|
|
||||||
# New file permissions
|
|
||||||
UMask = "0027";
|
|
||||||
# Security
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
ProtectHome = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
16
run-cmd.nix
16
run-cmd.nix
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
stdenvNoCC,
|
|
||||||
src,
|
|
||||||
name,
|
|
||||||
cmd,
|
|
||||||
extraBuildInputs ? [ ],
|
|
||||||
extraNativeBuildInputs ? [ ],
|
|
||||||
}:
|
|
||||||
stdenvNoCC.mkDerivation {
|
|
||||||
name = "${name}-src";
|
|
||||||
inherit src;
|
|
||||||
buildInputs = extraBuildInputs;
|
|
||||||
nativeBuildInputs = extraNativeBuildInputs;
|
|
||||||
buildPhase = cmd;
|
|
||||||
installPhase = "mkdir $out";
|
|
||||||
}
|
|
26
src/main.rs
26
src/main.rs
|
@ -12,6 +12,7 @@ use axum_auth::AuthBasic;
|
||||||
use axum_client_ip::{SecureClientIp, SecureClientIpSource};
|
use axum_client_ip::{SecureClientIp, SecureClientIpSource};
|
||||||
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 http::StatusCode;
|
use http::StatusCode;
|
||||||
use miette::{bail, ensure, Context, IntoDiagnostic, Result};
|
use miette::{bail, ensure, Context, IntoDiagnostic, Result};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
@ -26,6 +27,9 @@ const DEFAULT_SALT: &str = "UpdateMyDNS";
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
|
#[command(flatten)]
|
||||||
|
verbosity: Verbosity,
|
||||||
|
|
||||||
/// Ip address of the server
|
/// Ip address of the server
|
||||||
#[arg(long, default_value = "127.0.0.1")]
|
#[arg(long, default_value = "127.0.0.1")]
|
||||||
address: IpAddr,
|
address: IpAddr,
|
||||||
|
@ -121,6 +125,7 @@ struct AppState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_ip(path: &Path) -> Result<Option<IpAddr>> {
|
fn load_ip(path: &Path) -> Result<Option<IpAddr>> {
|
||||||
|
debug!("loading last IP from {}", path.display());
|
||||||
let data = match std::fs::read_to_string(path) {
|
let data = match std::fs::read_to_string(path) {
|
||||||
Ok(ip) => ip,
|
Ok(ip) => ip,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -146,13 +151,28 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
// parse cli arguments
|
// parse cli arguments
|
||||||
let mut args = Opts::parse();
|
let mut args = Opts::parse();
|
||||||
|
debug!("{args:?}");
|
||||||
|
|
||||||
// configure logger
|
// configure logger
|
||||||
let subscriber = tracing_subscriber::FmtSubscriber::builder()
|
let subscriber = tracing_subscriber::FmtSubscriber::builder()
|
||||||
.without_time()
|
.without_time()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
EnvFilter::builder()
|
EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::WARN.into())
|
.with_default_directive(
|
||||||
|
if args.verbosity.is_present() {
|
||||||
|
match args.verbosity.log_level_filter() {
|
||||||
|
clap_verbosity_flag::LevelFilter::Off => LevelFilter::OFF,
|
||||||
|
clap_verbosity_flag::LevelFilter::Error => LevelFilter::ERROR,
|
||||||
|
clap_verbosity_flag::LevelFilter::Warn => LevelFilter::WARN,
|
||||||
|
clap_verbosity_flag::LevelFilter::Info => LevelFilter::INFO,
|
||||||
|
clap_verbosity_flag::LevelFilter::Debug => LevelFilter::DEBUG,
|
||||||
|
clap_verbosity_flag::LevelFilter::Trace => LevelFilter::TRACE,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LevelFilter::WARN
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
.from_env_lossy(),
|
.from_env_lossy(),
|
||||||
)
|
)
|
||||||
.finish();
|
.finish();
|
||||||
|
@ -166,6 +186,7 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let Opts {
|
let Opts {
|
||||||
|
verbosity: _,
|
||||||
address: ip,
|
address: ip,
|
||||||
port,
|
port,
|
||||||
password_file,
|
password_file,
|
||||||
|
@ -287,6 +308,7 @@ async fn update_records(
|
||||||
AuthBasic((username, pass)): AuthBasic,
|
AuthBasic((username, pass)): AuthBasic,
|
||||||
SecureClientIp(ip): SecureClientIp,
|
SecureClientIp(ip): SecureClientIp,
|
||||||
) -> axum::response::Result<&'static str> {
|
) -> axum::response::Result<&'static str> {
|
||||||
|
debug!("received update request from {ip}");
|
||||||
let Some(pass) = pass else {
|
let Some(pass) = pass else {
|
||||||
return Err((StatusCode::UNAUTHORIZED, Json::from("no password provided")).into());
|
return Err((StatusCode::UNAUTHORIZED, Json::from("no password provided")).into());
|
||||||
};
|
};
|
||||||
|
@ -309,9 +331,11 @@ async fn update_records(
|
||||||
match nsupdate(ip, state.ttl, state.key_file, state.records).await {
|
match nsupdate(ip, state.ttl, state.key_file, state.records).await {
|
||||||
Ok(status) if status.success() => {
|
Ok(status) if status.success() => {
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
|
info!("updating last ip to {ip}");
|
||||||
if let Err(err) = std::fs::write(state.ip_file, format!("{ip}")) {
|
if let Err(err) = std::fs::write(state.ip_file, format!("{ip}")) {
|
||||||
error!("Failed to update last IP: {err}");
|
error!("Failed to update last IP: {err}");
|
||||||
}
|
}
|
||||||
|
info!("updated last ip to {ip}");
|
||||||
});
|
});
|
||||||
Ok("successful update")
|
Ok("successful update")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue