mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 14:13:04 +00:00
134 lines
4.0 KiB
Go
134 lines
4.0 KiB
Go
package gateway
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/client"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// ACMERequest represents the request body for ACME DNS-01 challenges
|
|
// from the lego httpreq provider
|
|
type ACMERequest struct {
|
|
FQDN string `json:"fqdn"` // e.g., "_acme-challenge.example.com."
|
|
Value string `json:"value"` // The challenge token
|
|
}
|
|
|
|
// acmePresentHandler handles DNS-01 challenge presentation
|
|
// POST /v1/internal/acme/present
|
|
// Creates a TXT record in the dns_records table for ACME validation
|
|
func (g *Gateway) acmePresentHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req ACMERequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
g.logger.Error("Failed to decode ACME present request", zap.Error(err))
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if req.FQDN == "" || req.Value == "" {
|
|
http.Error(w, "fqdn and value are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Normalize FQDN (ensure trailing dot for DNS format)
|
|
fqdn := strings.TrimSuffix(req.FQDN, ".")
|
|
fqdn = strings.ToLower(fqdn) + "." // Add trailing dot for DNS format
|
|
|
|
g.logger.Info("ACME DNS-01 challenge: presenting TXT record",
|
|
zap.String("fqdn", fqdn),
|
|
zap.String("value_prefix", req.Value[:min(10, len(req.Value))]+"..."),
|
|
)
|
|
|
|
// Insert TXT record into dns_records
|
|
db := g.client.Database()
|
|
ctx := client.WithInternalAuth(r.Context())
|
|
|
|
// First, delete any existing ACME challenge for this FQDN (in case of retry)
|
|
deleteQuery := `DELETE FROM dns_records WHERE fqdn = ? AND record_type = 'TXT' AND namespace = 'acme'`
|
|
_, _ = db.Query(ctx, deleteQuery, fqdn)
|
|
|
|
// Insert new TXT record
|
|
insertQuery := `INSERT INTO dns_records (fqdn, record_type, value, ttl, namespace, is_active, created_at, updated_at, created_by)
|
|
VALUES (?, 'TXT', ?, 60, 'acme', TRUE, datetime('now'), datetime('now'), 'system')`
|
|
|
|
_, err := db.Query(ctx, insertQuery, fqdn, req.Value)
|
|
if err != nil {
|
|
g.logger.Error("Failed to insert ACME TXT record", zap.Error(err))
|
|
http.Error(w, "Failed to create DNS record", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
g.logger.Info("ACME TXT record created",
|
|
zap.String("fqdn", fqdn),
|
|
)
|
|
|
|
// Give DNS a moment to propagate (CoreDNS reads from RQLite)
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
// acmeCleanupHandler handles DNS-01 challenge cleanup
|
|
// POST /v1/internal/acme/cleanup
|
|
// Removes the TXT record after ACME validation completes
|
|
func (g *Gateway) acmeCleanupHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req ACMERequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
g.logger.Error("Failed to decode ACME cleanup request", zap.Error(err))
|
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if req.FQDN == "" {
|
|
http.Error(w, "fqdn is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Normalize FQDN (ensure trailing dot for DNS format)
|
|
fqdn := strings.TrimSuffix(req.FQDN, ".")
|
|
fqdn = strings.ToLower(fqdn) + "." // Add trailing dot for DNS format
|
|
|
|
g.logger.Info("ACME DNS-01 challenge: cleaning up TXT record",
|
|
zap.String("fqdn", fqdn),
|
|
)
|
|
|
|
// Delete TXT record from dns_records
|
|
db := g.client.Database()
|
|
ctx := client.WithInternalAuth(r.Context())
|
|
|
|
deleteQuery := `DELETE FROM dns_records WHERE fqdn = ? AND record_type = 'TXT' AND namespace = 'acme'`
|
|
_, err := db.Query(ctx, deleteQuery, fqdn)
|
|
if err != nil {
|
|
g.logger.Error("Failed to delete ACME TXT record", zap.Error(err))
|
|
http.Error(w, "Failed to delete DNS record", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
g.logger.Info("ACME TXT record deleted",
|
|
zap.String("fqdn", fqdn),
|
|
)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
// min returns the smaller of two integers
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|