Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 107 additions & 3 deletions ADMIN_API_ENDPOINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2072,13 +2072,19 @@ Body:
"region_id": number,
// Required - Region ID
"reverse_zone_id": "string | null",
// Optional - Reverse DNS zone ID
// Optional - Reverse DNS zone ID (provider specific, e.g. Cloudflare reverse zone id)
"access_policy_id": "number | null",
// Optional - Access policy ID
"allocation_mode": "sequential",
// IpRangeAllocationMode enum: "random", "sequential", or "slaac_eui64", default: "sequential"
"use_full_range": boolean
"use_full_range": boolean,
// Optional - Use first and last IPs in range, default: false
"forward_dns_server_id": "number | null",
// Optional - dns_server id used for forward (A/AAAA) records
"reverse_dns_server_id": "number | null",
// Optional - dns_server id used for reverse (PTR) records
"forward_zone_id": "string | null"
// Optional - Forward DNS zone id (provider specific, e.g. Cloudflare forward zone id)
}
```

Expand Down Expand Up @@ -2108,8 +2114,14 @@ Body (all optional):
// Access policy ID (null to clear)
"allocation_mode": "sequential",
// IpRangeAllocationMode enum: "random", "sequential", or "slaac_eui64"
"use_full_range": boolean
"use_full_range": boolean,
// Use first and last IPs in range
"forward_dns_server_id": "number | null",
// dns_server id for forward (A/AAAA) records (null to clear)
"reverse_dns_server_id": "number | null",
// dns_server id for reverse (PTR) records (null to clear)
"forward_zone_id": "string | null"
// Forward DNS zone id (null to clear)
}
```

Expand Down Expand Up @@ -2151,6 +2163,20 @@ Response:
}
```

#### Patch DNS for Range

```
POST /api/admin/v1/ip_ranges/{id}/patch_dns
```

Required Permission: `ip_range::update`

Queues a background job (`PatchIpRangeDns`) that re-applies forward + reverse DNS records for every
(non-deleted) IP assignment in the range, reconciling them to the range's current DNS server
configuration. Useful after changing a range's `forward_dns_server_id` / `reverse_dns_server_id`
(e.g. switching reverse DNS to OVH). Per-assignment DNS failures are logged and skipped so one bad
record can't abort the batch. Returns a `JobResponse` (`{ "job_id": string }`).

### Access Policy Management

#### List Access Policies
Expand Down Expand Up @@ -2495,6 +2521,83 @@ Required Permission: `router::update`
Remove the router's static default route(s). Idempotent — succeeds even when no default route is configured. Returns a
`JobResponse`.

### DNS Server Management

DNS providers are configured in the database (`dns_server` table) and referenced per IP range via
`forward_dns_server_id` / `reverse_dns_server_id` (see IP Range Management). This replaces the old static
`dns` block in `config.yaml`.

#### List DNS Servers

```
GET /api/admin/v1/dns_servers
```

Query Parameters:

- `limit`: number (optional) - Items per page (max 100, default 50)
- `offset`: number (optional) - Pagination offset

Required Permission: `dns_server::view`

Returns a paginated list of DNS servers. Each entry includes `id`, `name`, `enabled`, `kind`, `url`, and
`ip_range_count` (number of IP ranges referencing it for forward or reverse DNS). The `token` is never returned.

#### Get DNS Server Details

```
GET /api/admin/v1/dns_servers/{dns_server_id}
```

Required Permission: `dns_server::view`

#### Create DNS Server

```
POST /api/admin/v1/dns_servers
```

Required Permission: `dns_server::create`

Body:

```json
{
"name": "string",
// Required - Display name
"enabled": boolean,
// Optional - Default: true
"kind": "cloudflare",
// DnsServerKind enum: "cloudflare" (forward + reverse) or "ovh" (reverse only)
"url": "string",
// Optional - API base url (required for OVH, e.g. https://eu.api.ovh.com)
"token": "string"
// Required - Credential token. Cloudflare: bearer token.
// OVH: "application_key:application_secret:consumer_key"
}
```

#### Update DNS Server

```
PATCH /api/admin/v1/dns_servers/{dns_server_id}
```

Required Permission: `dns_server::update`

Body (all optional): `name`, `enabled`, `kind`, `url`, `token`.

#### Delete DNS Server

```
DELETE /api/admin/v1/dns_servers/{dns_server_id}
```

Required Permission: `dns_server::delete`

Note: DNS servers referenced by any IP range (forward or reverse) cannot be deleted. Remove the reference from all IP
ranges first.

### VM IP Assignment Management

VM IP assignments bind specific IP addresses from IP ranges to virtual machines. These endpoints provide comprehensive
Expand Down Expand Up @@ -3018,6 +3121,7 @@ The RBAC system uses the following permission format: `resource::action`
- `company` - Company/organization management
- `ip_range` - IP address range management
- `router` - Network router configuration
- `dns_server` - DNS provider configuration
- `vm_custom_pricing` - Custom VM pricing models
- `host_region` - Host region configuration
- `vm_os_image` - VM operating system images
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ These docs apply to all projects using this agent structure:
| [docs/agents/bug-fixes.md](docs/agents/bug-fixes.md) | Resolving bugs — LNVPS-specific additions |
| [docs/agents/coverage.md](docs/agents/coverage.md) | Function coverage — LNVPS-specific additions |
| [docs/agents/e2e-tests.md](docs/agents/e2e-tests.md) | Writing or running E2E integration tests (`lnvps_e2e` crate) |
| [docs/agents/testing-db-backups.md](docs/agents/testing-db-backups.md) | Testing branch changes (migrations, startup) against a restored production DB backup safely (mock hosts, no user notifications) |
| [docs/agents/fw-testing.md](docs/agents/fw-testing.md) | Running or extending the firewall XDP netns test harness (`lnvps_fw`) |

## Pull Request Procedure
Expand Down
17 changes: 17 additions & 0 deletions API_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ All notable changes to the LNVPS APIs are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added

- **2026-07-10** - Database-configured DNS providers + OVH reverse DNS (admin)
- DNS providers are now configured in the database (`dns_server` table) instead of the static `dns` block in `config.yaml`. Each provider has `kind` (`"cloudflare"` or `"ovh"`), `url`, and an encrypted `token`.
- `GET /api/admin/v1/dns_servers` — paginated list (`id`, `name`, `enabled`, `kind`, `url`, `ip_range_count`; token never returned). Requires `dns_server::view`.
- `GET /api/admin/v1/dns_servers/{id}` — get one. Requires `dns_server::view`.
- `POST /api/admin/v1/dns_servers` — create. Body: `{ name, enabled?, kind, url?, token }`. Cloudflare token is the bearer token; OVH token is `"application_key:application_secret:consumer_key"`. Requires `dns_server::create`.
- `PATCH /api/admin/v1/dns_servers/{id}` — update (all fields optional). Requires `dns_server::update`.
- `DELETE /api/admin/v1/dns_servers/{id}` — delete (blocked while referenced by any IP range). Requires `dns_server::delete`.
- IP ranges gained `forward_dns_server_id`, `reverse_dns_server_id`, and `forward_zone_id` fields (in addition to the existing `reverse_zone_id`) on the create/update/list admin endpoints, selecting which DNS provider + zone manages forward (A/AAAA) and reverse (PTR) records for the range.
- New `ovh` DNS provider implements reverse DNS (PTR) via OVH's `POST/DELETE /ip/{ip}/reverse` (reverse only; forward records must use another provider). Closes #78.
- New `dns_server` admin permission resource.
- `POST /api/admin/v1/ip_ranges/{id}/patch_dns` — queue a `PatchIpRangeDns` job that re-applies forward + reverse DNS for every IP assignment in a range, reconciling them to the range's current DNS server config (e.g. after switching reverse DNS to OVH). Returns a `JobResponse`. Requires `ip_range::update`.
- Migration: the legacy `config.yaml` `dns` block is migrated into a `dns_server` row on startup, and existing IP ranges are pointed at it automatically. OVH additional-IP routers are also imported as `Ovh` DNS servers (reusing their `url` + token), with reverse DNS auto-mapped onto the ranges they route. Existing OVH-routed IPs that still carry a stale Cloudflare reverse record are force-refreshed to a real OVH PTR (idempotent — keyed on the IP; working Cloudflare reverse records are left untouched). (The OVH consumer key may need `/ip/*/reverse` permissions granted for reverse DNS calls to succeed.) Record backfill/refresh is best-effort and never blocks startup.

## [0.3.0] - 2026-07-09

### Added
Expand Down
Loading
Loading