Skip to content

fix(dns): a forward record is named by its assignment - #412

Merged
v0l merged 1 commit into
masterfrom
fix/dns-forward-applied
Aug 29, 2026
Merged

fix(dns): a forward record is named by its assignment#412
v0l merged 1 commit into
masterfrom
fix/dns-forward-applied

Conversation

@v0l

@v0l v0l commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

PATCH /api/admin/v1/vm_ip_assignments/{id} accepted dns_forward, documented it as the forward FQDN, wrote it to the database and never sent it to DNS. BasicRecord::forward derived the record name from the VM id and never read the stored value, so the row and the nameserver disagreed. Reverse worked, because reverse_to_fwd does read its stored value.

forward now uses dns_forward when set, falling back to the derived vm-{id} label when it is not, so clearing the field restores the old name.

The FQDN question

The issue flagged this as the thing not to guess at, and it is the only real decision here. The derived name is a bare label that Cloudflare expands against the zone. Accepting a label as a stored name would turn host into host.lnvps.cloud.lnvps.cloud on live records for every VM in a shared zone, not only the one being renamed.

So a stored name must be a full FQDN, as reverse names already must. That is also what a create writes back, since providers return the record's resolved name. The fallback stays a label because there is no stored name for the zone to disagree with.

Checked against production rather than assumed

Of 1787 assignments: 1348 have no forward name, 439 hold an FQDN, none hold a bare label, so nothing starts failing validation.

Of the 439, the only rows whose name is not already vm-{vm_id}.<zone> are the six belonging to the three route servers, which are the rows the bug was found on. For every other row the record produced is unchanged.

Test

a_forward_record_is_named_by_the_assignment covers the derived name, the stored name (keeping its provider reference, so the record is renamed rather than duplicated), the vm-42.lnvps.cloud form every un-renamed row already holds, and the rejected bare label. Verified to fail with left: "vm-42", right: "ie-01.vpn.example.com" when the fix is reverted.

2052 pass, clippy clean.

Fixes #410

`PATCH /api/admin/v1/vm_ip_assignments/{id}` accepted `dns_forward`,
documented it as the forward FQDN, wrote it to the database and never
sent it to DNS. `BasicRecord::forward` derived the record name from the
VM id and never read the stored value, so the row and the nameserver
disagreed -- worse than refusing the change, because anything reading
`vm_ip_assignment.dns_forward` was then being told something false.
Reverse worked, because `reverse_to_fwd` does read its stored value.

Editing the record at the provider did not help either: the next forward
sync renamed it back, including `POST /ip_ranges/{id}/patch_dns`, which
re-applies a whole range.

`forward` now uses `dns_forward` when set and falls back to the derived
`vm-{id}` label when it is not, so clearing the field restores the old
name.

A stored name must be a full FQDN, as reverse names already must. This is
the wrinkle worth not guessing at: the derived name is a bare *label*
that Cloudflare expands against the zone, so accepting a label here would
turn `host` into `host.lnvps.cloud.lnvps.cloud` on live records for every
VM in a shared zone, not only the one being renamed. The fallback stays a
label because there is no stored name for the zone to disagree with.

Checked against production before changing it, rather than assuming: of
1787 assignments, 1348 have no forward name and 439 hold an FQDN. None
hold a bare label, so nothing starts failing validation. Of the 439, the
only rows whose name is not already `vm-{vm_id}.<zone>` are the six
belonging to the three route servers, which are exactly the rows this bug
was found on. For every other row the record produced is unchanged.

The test fails with `left: "vm-42", right: "ie-01.vpn.example.com"` when
the fix is reverted.

Fixes #410
@v0l
v0l merged commit 56a5979 into master Aug 29, 2026
10 checks passed
@v0l
v0l deleted the fix/dns-forward-applied branch August 29, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin API stores dns_forward but never applies it, so the row disagrees with DNS

1 participant