feat(contrib): add TXT record helper with key generation
- Add contrib/make_welist_txt.sh to print the _welist-key TXT name, value and BIND zone line for a domain and Ed25519 key - Accept a private/public PEM file or an existing base64 SPKI DER - Generate private_key.pem and public_key.pem when the key argument is omitted, reusing an existing private key - Document the helper in docs/10_dnssec_key_publishing.md
This commit is contained in:
120
contrib/make_welist_txt.sh
Executable file
120
contrib/make_welist_txt.sh
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Print the WeTime v2 DNSSEC TXT record for an executor hostname.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: make_welist_txt.sh <domain> [key]
|
||||||
|
|
||||||
|
<domain> Executor hostname or URL, for example we.example.com
|
||||||
|
[key] Ed25519 private/public PEM file, or the base64
|
||||||
|
SubjectPublicKeyInfo DER value to publish.
|
||||||
|
|
||||||
|
If [key] is omitted, the script uses ./private_key.pem when it exists,
|
||||||
|
otherwise it generates ./private_key.pem and ./public_key.pem.
|
||||||
|
|
||||||
|
The record is published at _welist-key.<domain> with value:
|
||||||
|
v=we1; alg=ed25519; key=<base64 SPKI DER>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
domain=$1
|
||||||
|
key_input=${2:-}
|
||||||
|
|
||||||
|
# Normalize the hostname: strip scheme, path, trailing dot, and lowercase.
|
||||||
|
domain=${domain#http://}
|
||||||
|
domain=${domain#https://}
|
||||||
|
domain=${domain%%/*}
|
||||||
|
domain=${domain%.}
|
||||||
|
domain=$(printf '%s' "$domain" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Avoid doubling the prefix if the caller already supplied it.
|
||||||
|
domain=${domain#_welist-key.}
|
||||||
|
|
||||||
|
if [ -z "$domain" ] || ! printf '%s' "$domain" | grep -Eq '^[a-z0-9.-]+$'; then
|
||||||
|
echo "error: invalid hostname: $domain" >&2
|
||||||
|
echo " use the host only, without scheme, port, or path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf '%s' "$domain" | grep -Eq '^[0-9.]+$'; then
|
||||||
|
echo "error: DNSSEC key discovery requires a hostname, not an IP address" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$key_input" ]; then
|
||||||
|
private_key="./private_key.pem"
|
||||||
|
public_key="./public_key.pem"
|
||||||
|
|
||||||
|
if [ -f "$private_key" ]; then
|
||||||
|
key_input=$private_key
|
||||||
|
echo "Using existing $private_key" >&2
|
||||||
|
else
|
||||||
|
if [ -e "$private_key" ] || [ -e "$public_key" ]; then
|
||||||
|
echo "error: $private_key or $public_key exists and will not be overwritten" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! (umask 077; openssl genpkey -algorithm ED25519 -out "$private_key"); then
|
||||||
|
echo "error: cannot generate $private_key" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! openssl pkey -in "$private_key" -pubout -out "$public_key"; then
|
||||||
|
echo "error: cannot derive $public_key" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod 600 "$private_key"
|
||||||
|
chmod 644 "$public_key"
|
||||||
|
|
||||||
|
key_input=$private_key
|
||||||
|
cat >&2 <<EOF
|
||||||
|
Generated Ed25519 key pair:
|
||||||
|
private key: $private_key (keep secret)
|
||||||
|
public key: $public_key
|
||||||
|
|
||||||
|
Set SSL_KEY_PATH and BAL_SERVER_PUB_KEY_PATH to these files.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_der=$(mktemp)
|
||||||
|
trap 'rm -f "$tmp_der"' EXIT
|
||||||
|
|
||||||
|
if [ -f "$key_input" ]; then
|
||||||
|
if ! openssl pkey -in "$key_input" -pubout -outform DER -out "$tmp_der" 2>/dev/null; then
|
||||||
|
if ! openssl pkey -pubin -in "$key_input" -outform DER -out "$tmp_der" 2>/dev/null; then
|
||||||
|
echo "error: cannot read an Ed25519 key from $key_input" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf '%s' "$key_input" | tr -d '[:space:]' \
|
||||||
|
| openssl base64 -d -A -out "$tmp_der" 2>/dev/null || {
|
||||||
|
echo "error: key is neither a PEM file nor a base64 SPKI DER value" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
der_len=$(wc -c < "$tmp_der" | tr -d ' ')
|
||||||
|
prefix_hex=$(od -An -tx1 -N12 "$tmp_der" | tr -d ' \n')
|
||||||
|
if [ "$der_len" -ne 44 ] || [ "$prefix_hex" != "302a300506032b6570032100" ]; then
|
||||||
|
echo "error: key is not a 44-byte Ed25519 SubjectPublicKeyInfo DER" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
key_b64=$(openssl base64 -A -in "$tmp_der")
|
||||||
|
value="v=we1; alg=ed25519; key=$key_b64"
|
||||||
|
name="_welist-key.$domain"
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
TXT name: $name
|
||||||
|
TXT value: $value
|
||||||
|
|
||||||
|
BIND zone line:
|
||||||
|
$name. 3600 IN TXT "$value"
|
||||||
|
EOF
|
||||||
@@ -71,6 +71,13 @@ openssl pkey -in private_key.pem -pubout -out public_key.pem
|
|||||||
chmod 600 private_key.pem
|
chmod 600 private_key.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively, `contrib/make_welist_txt.sh <host>` generates
|
||||||
|
`private_key.pem` and `public_key.pem` in the current directory when the key
|
||||||
|
argument is omitted, then prints the TXT record. The private key is created at
|
||||||
|
mode `0600`. If `./private_key.pem` already exists, the script reuses it instead
|
||||||
|
of overwriting it. Move both files to a protected location before starting the
|
||||||
|
services.
|
||||||
|
|
||||||
Then point both settings at the new pair and restart `bal-server` and
|
Then point both settings at the new pair and restart `bal-server` and
|
||||||
`bal-pusher`:
|
`bal-pusher`:
|
||||||
|
|
||||||
@@ -100,6 +107,10 @@ KEY=$(openssl pkey -in private_key.pem -pubout -outform DER | base64 | tr -d '\n
|
|||||||
echo "v=we1; alg=ed25519; key=$KEY"
|
echo "v=we1; alg=ed25519; key=$KEY"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`contrib/make_welist_txt.sh <host> [key]` prints the record name, value, and a
|
||||||
|
BIND zone line directly. `key` may be a private/public PEM file or an existing
|
||||||
|
base64 SPKI DER value; omit it to generate a new key pair first.
|
||||||
|
|
||||||
The value has this form:
|
The value has this form:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
Reference in New Issue
Block a user