feat: add VPN DNS service with dynamic service catalog resolution and CoreDNS integration

Add ServiceDNSRecord type and gateway API endpoint to expose active service domain-to-IP mappings. Implement ListServiceDNSRecords repository method querying services table with proxy_ip resolution using effectiveAccessProxyIP helper.

Add vpn-dns microservice built on CoreDNS with periodic sync from backend API. Generate Corefile with configurable upstream DNS servers and hosts plugin for service overrides.
This commit is contained in:
2026-03-18 13:30:34 +01:00
parent 6cf49ff3e0
commit 3e2169f217
11 changed files with 238 additions and 1 deletions

View File

@@ -17,6 +17,7 @@ type Repository interface {
List(ctx context.Context) ([]Gateway, error)
FirstActive(ctx context.Context) (Gateway, error)
BuildSyncBundle(ctx context.Context, gatewayID uuid.UUID) (wireguard.GatewayBundle, error)
ListServiceDNSRecords(ctx context.Context) ([]ServiceDNSRecord, error)
StoreTelemetry(ctx context.Context, gatewayID uuid.UUID, snapshot TelemetrySnapshot) error
Update(ctx context.Context, gatewayID uuid.UUID, input UpdateRequest) (Gateway, error)
UpsertByName(ctx context.Context, input BootstrapRequest) (Gateway, error)
@@ -192,6 +193,33 @@ func effectiveAccessProxyIP(proxyIP string) string {
return proxyIP
}
func (r *PGRepository) ListServiceDNSRecords(ctx context.Context) ([]ServiceDNSRecord, error) {
rows, err := r.db.Query(ctx, `
select distinct
s.domain,
host(s.proxy_ip)
from services s
where s.deleted_at is null and s.is_active = true
order by s.domain asc
`)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ServiceDNSRecord
for rows.Next() {
var item ServiceDNSRecord
var proxyIP string
if err := rows.Scan(&item.Domain, &proxyIP); err != nil {
return nil, err
}
item.TargetIP = effectiveAccessProxyIP(proxyIP)
items = append(items, item)
}
return items, rows.Err()
}
func (r *PGRepository) Update(ctx context.Context, gatewayID uuid.UUID, input UpdateRequest) (Gateway, error) {
row := r.db.QueryRow(ctx, `
update gateways