mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-27 22:04:12 +00:00
- add monorepo Makefile delegating to sub-projects - update CI workflows, GoReleaser, gitignore for new structure - revise README, CONTRIBUTING.md for monorepo overview - bump Go to 1.24
139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
// Package enroll implements the one-time enrollment server for OramaOS nodes.
|
|
//
|
|
// On first boot, the agent starts an HTTP server on port 9999 that serves
|
|
// a registration code. The operator retrieves this code and provides it to
|
|
// the Gateway (via `orama node enroll`). The Gateway then pushes cluster
|
|
// configuration back to the agent via WebSocket.
|
|
package enroll
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/orama-os/agent/internal/types"
|
|
)
|
|
|
|
// Result contains the enrollment data received from the Gateway.
|
|
type Result struct {
|
|
NodeID string `json:"node_id"`
|
|
WireGuardConfig string `json:"wireguard_config"`
|
|
ClusterSecret string `json:"cluster_secret"`
|
|
Peers []types.Peer `json:"peers"`
|
|
}
|
|
|
|
// Server is the enrollment HTTP server.
|
|
type Server struct {
|
|
gatewayURL string
|
|
result *Result
|
|
mu sync.Mutex
|
|
done chan struct{}
|
|
}
|
|
|
|
// NewServer creates a new enrollment server.
|
|
func NewServer(gatewayURL string) *Server {
|
|
return &Server{
|
|
gatewayURL: gatewayURL,
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Run starts the enrollment server and blocks until enrollment is complete.
|
|
// Returns the enrollment result containing cluster configuration.
|
|
func (s *Server) Run() (*Result, error) {
|
|
// Generate registration code (8 alphanumeric chars)
|
|
code, err := generateCode()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate registration code: %w", err)
|
|
}
|
|
|
|
log.Printf("ENROLLMENT CODE: %s", code)
|
|
log.Printf("Waiting for enrollment on port 9999...")
|
|
|
|
// Channel for enrollment completion
|
|
enrollCh := make(chan *Result, 1)
|
|
errCh := make(chan error, 1)
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
// Serve registration code — one-shot endpoint
|
|
var served bool
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
s.mu.Lock()
|
|
if served {
|
|
s.mu.Unlock()
|
|
http.Error(w, "already served", http.StatusGone)
|
|
return
|
|
}
|
|
served = true
|
|
s.mu.Unlock()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"code": code,
|
|
"expires": time.Now().Add(10 * time.Minute).Format(time.RFC3339),
|
|
})
|
|
})
|
|
|
|
// Receive enrollment config from Gateway (pushed after code verification)
|
|
mux.HandleFunc("/v1/agent/enroll/complete", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var result Result
|
|
if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
|
|
http.Error(w, "invalid JSON", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
|
|
|
enrollCh <- &result
|
|
})
|
|
|
|
server := &http.Server{
|
|
Addr: ":9999",
|
|
Handler: mux,
|
|
ReadTimeout: 10 * time.Second,
|
|
WriteTimeout: 10 * time.Second,
|
|
}
|
|
|
|
// Start server in background
|
|
go func() {
|
|
if err := server.ListenAndServe(); err != http.ErrServerClosed {
|
|
errCh <- fmt.Errorf("enrollment server error: %w", err)
|
|
}
|
|
}()
|
|
|
|
// Wait for enrollment or error
|
|
select {
|
|
case result := <-enrollCh:
|
|
// Gracefully shut down the enrollment server
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
server.Shutdown(ctx)
|
|
log.Println("enrollment server closed")
|
|
return result, nil
|
|
case err := <-errCh:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// generateCode generates an 8-character alphanumeric registration code.
|
|
func generateCode() (string, error) {
|
|
b := make([]byte, 4)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
return hex.EncodeToString(b), nil
|
|
}
|