Initial project commit

This commit is contained in:
2026-06-20 13:03:42 +02:00
parent 90326f5cb4
commit 3b82cf973f
6 changed files with 2087 additions and 15 deletions
+269 -2
View File
@@ -1,3 +1,270 @@
# dyndns
# dynDNS
dynDNS is a POSIX-compliant shell script for managing database-backed dynamic DNS hostnames and synchronizing them with a BIND zone. It handles hostname lifecycle operations (add, check, remove), maintains related database records and generates secure nsupdate transactions with optional DNSSEC and rndc integration.
Lightweight dynDNS backend for BIND-based dynamic DNS updates.
This repository contains only the backend components of the dynDNS service: the main `dyndns.sh` script and a `vars.example` configuration template. It is designed as a backend component behind a web frontend or API that manages member accounts and IP assignments.
## Outline
- [Features](#features)
- [Installation](#installation)
- [MariaDB Initialization](#mariadb-initialization)
- [Database Schema](#database-schema)
- [Configuration](#configuration)
- [Directory Layout](#directory-layout)
- [CRON Job Example](#cron-job-example)
- [License](#license)
- [Authors](#authors)
- [Project Home](#project-home)
## Features
- Dynamic DNS backend script for BIND-based environments
- Database-driven record management
- A and AAAA record handling
- Wildcard CNAME generation for managed subdomains
- `nsupdate` integration with TSIG authentication
- DNSSEC-aware update workflow
- Simple configuration through a separate vars file
- Designed for self-hosted Linux environments
## Installation
1. Clone the repository.
2. Copy `vars.example` to a site-specific vars file.
3. Adjust paths, database settings, BIND zone settings, and TSIG key references.
4. Make `dyndns.sh` executable.
5. Run the script manually once before automation.
Example:
```sh
sudo groupadd --system dyndns
sudo install -d -m 0750 -o root -g dyndns /opt/dyndns
sudo git clone https://dev.town-square.de/cb601/dyndns.git /opt/dyndns
cd /opt/dyndns
sudo cp vars.example vars
sudo chown root:dyndns /opt/dyndns/dyndns.sh /opt/dyndns/vars
sudo chmod 0750 /opt/dyndns/dyndns.sh
sudo chmod 0640 /opt/dyndns/vars
sudo /opt/dyndns/dyndns.sh help
```
The `dyndns.sh` script is owned by root and readable by the dyndns group only. This keeps the contents of the `vars` file (including database credentials) restricted to the backend.
## MariaDB Initialization
Before running the dynDNS backend for the first time, create the application database and a dedicated database user. Using a dedicated user with privileges limited to the dynDNS database is the recommended setup.
Connect to MariaDB as an administrative user:
```sh
mariadb -u root -p
```
Alternatively, when using the system root account and a configured `/root/.my.cnf`:
```sh
sudo mariadb
```
Create the database and grant access to a dedicated backend user:
```sql
CREATE DATABASE dyndns;
CREATE USER 'dyndnsuser'@'localhost' IDENTIFIED BY '<REPLACE_WITH_STRONG_PASSWORD>';
CREATE USER 'dyndnsuser'@'::1' IDENTIFIED BY '<REPLACE_WITH_STRONG_PASSWORD>';
CREATE USER 'dyndnsuser'@'127.0.0.1' IDENTIFIED BY '<REPLACE_WITH_STRONG_PASSWORD>';
GRANT ALL PRIVILEGES ON dyndns.* TO 'dyndnsuser'@'localhost';
GRANT ALL PRIVILEGES ON dyndns.* TO 'dyndnsuser'@'::1';
GRANT ALL PRIVILEGES ON dyndns.* TO 'dyndnsuser'@'127.0.0.1';
FLUSH PRIVILEGES;
```
This setup allows local access for the dedicated dynDNS user from `localhost`, `127.0.0.1`, and `::1`, and keeps the dynDNS backend user restricted to the `dyndns` database and local connections only.
After database initialization, adjust the matching settings in your vars file:
```sh
DYNDNS_SQL_HOST="localhost"
DYNDNS_SQL_DATABASE="dyndns"
DYNDNS_SQL_USER="dyndnsuser"
DYNDNS_SQL_PASS="<REPLACE_WITH_STRONG_PASSWORD>"
```
Replace `<REPLACE_WITH_STRONG_PASSWORD>` with a sitespecific, strong password.
A first manual connection test is recommended before enabling cron-based automation, so database connectivity and privileges can be verified early.
## Database Schema
The dynDNS backend expects a MariaDB schema with three core tables:
- `members` for account and hostname ownership data
- `connections` for the currently assigned IP address per member
- `login_attempts` for tracking login attempts
The following definitions reflect the currently used database layout.
### Table: `members`
Stores dynDNS account identity and hostname ownership data.
```sql
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(63) NOT NULL,
`domain` varchar(253) NOT NULL,
`email` varchar(254) NOT NULL,
`password` varchar(255) NOT NULL,
`salt` varchar(255) NOT NULL,
`timestamp` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`,`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
Field overview:
- `id`: Internal primary key
- `username`: Host-specific account name, usually the subdomain part
- `domain`: Managed DNS zone name
- `email`: Contact address of the member
- `password`: Stored password hash
- `salt`: Per-user salt used for password hashing
- `timestamp`: Record timestamp stored as unsigned Unix time (seconds since epoch)
- `UNIQUE (username, domain)`: Ensures that a hostname can only exist once per zone
### Table: `connections`
Stores the currently active IP address assignment for a dynDNS member.
```sql
CREATE TABLE `connections` (
`id` int(11) NOT NULL,
`IP` varchar(45) NOT NULL,
`IPv6_flag` tinyint(1) unsigned NOT NULL DEFAULT 0,
`IP_locked` tinyint(1) unsigned NOT NULL DEFAULT 0,
`timestamp` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_connections_members`
FOREIGN KEY (`id`) REFERENCES `members` (`id`)
ON DELETE CASCADE
ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
Field overview:
- `id`: Shared primary key and foreign key referencing `members.id`
- `IP`: Current IP address in text form; `varchar(45)` supports both IPv4 and IPv6
- `IPv6_flag`: `0` for A records, `1` for AAAA records
- `IP_locked`: Prevents automated DNS updates when set
- `timestamp`: Record timestamp stored as unsigned Unix time (seconds since epoch)
### Table: `login_attempts`
Stores the login attempt counter for a member.
```sql
CREATE TABLE `login_attempts` (
`id` int(11) NOT NULL,
`count` smallint(5) unsigned NOT NULL DEFAULT 0,
`timestamp` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_login_attempts_members`
FOREIGN KEY (`id`) REFERENCES `members` (`id`)
ON DELETE CASCADE
ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
Field overview:
- `id`: Shared primary key and foreign key referencing `members.id`
- `count`: Number of tracked login attempts
- `timestamp`: Record timestamp stored as unsigned Unix time (seconds since epoch)
### Referential Integrity
The schema uses explicit foreign key constraints from `connections.id` and `login_attempts.id` to `members.id`. Both child tables use `ON DELETE CASCADE`, so dependent records are removed automatically when a member is deleted. This ensures database-level referential integrity and matches the intended one-to-one relationship design.
When deleting a member, the application only needs to remove the parent row from `members`; dependent rows in `connections` and `login_attempts` are removed automatically by the database.
### Notes
- The schema uses `utf8mb4` with `utf8mb4_unicode_ci`.
- `username` uses `varchar(63)`, which matches the maximum length of a single DNS label.
- `domain` uses `varchar(253)`, which matches the typical maximum length of a FQDN without trailing root dot.
- IP addresses are stored as text in `varchar(45)`, which is sufficient for IPv4 and IPv6 storage.
## Configuration
The backend is configured through a separate configuration file derived from `vars.example`.
Typical settings include:
- Base paths and log paths
- Database connection settings
- DNS zone name and zone file paths
- BIND server endpoint
- TSIG key file used for `nsupdate` and `rndc`
- Default TTL values
Recommended workflow:
1. Start from `vars.example`.
2. Create an environment-specific copy, for example `vars`.
3. Keep secrets and local overrides out of version control.
4. Validate database access and BIND update permissions before enabling automation.
## Directory Layout
```text
.
├── dyndns.sh
├── vars.example
├── README.md
├── Changelog.md
├── LICENSE
└── .gitignore
```
## CRON Job Example
The following example assumes the job is installed in the crontab of a dedicated service user or root, with sufficient read access to the vars file and execute permissions on `/opt/dyndns/dyndns.sh`.
```sh
*/5 * * * * /opt/dyndns/dyndns.sh update-zone >/dev/null 2>&1
```
Running the job every 5 minutes is a reasonable default for typical dynDNS use cases.
If a non-default vars file is used:
```sh
*/5 * * * * /opt/dyndns/dyndns.sh --vars /opt/dyndns/vars update-zone >/dev/null 2>&1
```
## License
[MIT](https://dev.town-square.de/cb601/dyndns/src/branch/main/LICENSE)
See `LICENSE` for details.
## Authors
CB-601 - the open tec Elevator
- Stephan Düsterhaupt ([XMPP](xmpp:me@jabber.stephanduesterhaupt.de))
- Ivo Noack aka Insonic ([XMPP](xmpp:me@jabber.ivonoack.de))
## Project Home
Project Home: [https://dev.town-square.de/cb601/dyndns](https://dev.town-square.de/cb601/dyndns)