New agent RPCs for v2 multi-node orchestration: - SetupEdgeRoute: provisions TLS cert from Metacrypt, resolves backend hostname to Tailnet IP, validates it's in 100.64.0.0/10, registers L7 route in mc-proxy. Rejects backend_tls=false. - RemoveEdgeRoute: removes mc-proxy route, cleans up TLS cert, removes registry entry. - ListEdgeRoutes: returns all edge routes with cert serial/expiry. - HealthCheck: returns agent health and container count. New database table (migration 4): edge_routes stores hostname, backend info, and cert paths for persistence across agent restarts. ProxyRouter gains CertPath/KeyPath helpers for consistent cert path construction. Security: - Backend hostname must resolve to a Tailnet IP (100.64.0.0/10) - backend_tls=false is rejected (no cleartext to backends) - Cert provisioning failure fails the setup (no route to missing cert) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
3.0 KiB
Go
94 lines
3.0 KiB
Go
package registry
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// EdgeRoute represents a public edge route managed by the master.
|
|
type EdgeRoute struct {
|
|
Hostname string
|
|
BackendHostname string
|
|
BackendPort int
|
|
TLSCert string
|
|
TLSKey string
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// CreateEdgeRoute inserts or replaces an edge route.
|
|
func CreateEdgeRoute(db *sql.DB, hostname, backendHostname string, backendPort int, tlsCert, tlsKey string) error {
|
|
_, err := db.Exec(`
|
|
INSERT INTO edge_routes (hostname, backend_hostname, backend_port, tls_cert, tls_key, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
|
ON CONFLICT(hostname) DO UPDATE SET
|
|
backend_hostname = excluded.backend_hostname,
|
|
backend_port = excluded.backend_port,
|
|
tls_cert = excluded.tls_cert,
|
|
tls_key = excluded.tls_key,
|
|
updated_at = datetime('now')
|
|
`, hostname, backendHostname, backendPort, tlsCert, tlsKey)
|
|
if err != nil {
|
|
return fmt.Errorf("create edge route %s: %w", hostname, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetEdgeRoute returns a single edge route by hostname.
|
|
func GetEdgeRoute(db *sql.DB, hostname string) (*EdgeRoute, error) {
|
|
var r EdgeRoute
|
|
var createdAt, updatedAt string
|
|
err := db.QueryRow(`
|
|
SELECT hostname, backend_hostname, backend_port, tls_cert, tls_key, created_at, updated_at
|
|
FROM edge_routes WHERE hostname = ?
|
|
`, hostname).Scan(&r.Hostname, &r.BackendHostname, &r.BackendPort, &r.TLSCert, &r.TLSKey, &createdAt, &updatedAt)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get edge route %s: %w", hostname, err)
|
|
}
|
|
r.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
|
|
r.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)
|
|
return &r, nil
|
|
}
|
|
|
|
// ListEdgeRoutes returns all edge routes.
|
|
func ListEdgeRoutes(db *sql.DB) ([]*EdgeRoute, error) {
|
|
rows, err := db.Query(`
|
|
SELECT hostname, backend_hostname, backend_port, tls_cert, tls_key, created_at, updated_at
|
|
FROM edge_routes ORDER BY hostname
|
|
`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list edge routes: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var routes []*EdgeRoute
|
|
for rows.Next() {
|
|
var r EdgeRoute
|
|
var createdAt, updatedAt string
|
|
if err := rows.Scan(&r.Hostname, &r.BackendHostname, &r.BackendPort, &r.TLSCert, &r.TLSKey, &createdAt, &updatedAt); err != nil {
|
|
return nil, fmt.Errorf("scan edge route: %w", err)
|
|
}
|
|
r.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
|
|
r.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)
|
|
routes = append(routes, &r)
|
|
}
|
|
return routes, rows.Err()
|
|
}
|
|
|
|
// DeleteEdgeRoute removes an edge route by hostname.
|
|
func DeleteEdgeRoute(db *sql.DB, hostname string) error {
|
|
result, err := db.Exec(`DELETE FROM edge_routes WHERE hostname = ?`, hostname)
|
|
if err != nil {
|
|
return fmt.Errorf("delete edge route %s: %w", hostname, err)
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
if n == 0 {
|
|
return fmt.Errorf("edge route %s not found", hostname)
|
|
}
|
|
return nil
|
|
}
|