feat: Implement CSR signing and node registration handler for agent join

This commit is contained in:
2025-05-17 13:05:21 -04:00
parent f1f2b8f9ef
commit bf80b65873
4 changed files with 217 additions and 135 deletions

View File

@ -248,124 +248,9 @@ func runInit(cmd *cobra.Command, args []string) {
log.Printf("Failed to create API server: %v", err)
} else {
// Register the join handler
apiServer.RegisterJoinHandler(func(w http.ResponseWriter, r *http.Request) {
log.Printf("Received join request from %s", r.RemoteAddr)
// In Phase 2, we're not requiring client certificates yet
log.Printf("Processing join request without client certificate verification (Phase 2)")
// Read request body
var joinReq cli.JoinRequest
if err := json.NewDecoder(r.Body).Decode(&joinReq); err != nil {
log.Printf("Error decoding join request: %v", err)
http.Error(w, "Invalid request format", http.StatusBadRequest)
return
}
// Validate request
if joinReq.NodeName == "" || joinReq.AdvertiseAddr == "" || joinReq.CSRData == "" {
log.Printf("Invalid join request: missing required fields")
http.Error(w, "Missing required fields", http.StatusBadRequest)
return
}
log.Printf("Processing join request for node: %s, advertise address: %s",
joinReq.NodeName, joinReq.AdvertiseAddr)
// Decode CSR data
csrData, err := base64.StdEncoding.DecodeString(joinReq.CSRData)
if err != nil {
log.Printf("Error decoding CSR data: %v", err)
http.Error(w, "Invalid CSR data", http.StatusBadRequest)
return
}
// Create a temporary file for the CSR
tempCSRFile, err := os.CreateTemp("", "node-csr-*.pem")
if err != nil {
log.Printf("Error creating temp CSR file: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
defer os.Remove(tempCSRFile.Name())
// Write CSR data to temp file
if _, err := tempCSRFile.Write(csrData); err != nil {
log.Printf("Error writing CSR data to temp file: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
tempCSRFile.Close()
// Create a temp file for the signed certificate
tempCertFile, err := os.CreateTemp("", "node-cert-*.pem")
if err != nil {
log.Printf("Error creating temp cert file: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
defer os.Remove(tempCertFile.Name())
tempCertFile.Close()
// Sign the CSR
if err := pki.SignCertificateRequest(
filepath.Join(pkiDir, "ca.key"),
filepath.Join(pkiDir, "ca.crt"),
tempCSRFile.Name(),
tempCertFile.Name(),
365*24*time.Hour, // 1 year validity
); err != nil {
log.Printf("Error signing CSR: %v", err)
http.Error(w, "Failed to sign certificate", http.StatusInternalServerError)
return
}
// Read the signed certificate
signedCert, err := os.ReadFile(tempCertFile.Name())
if err != nil {
log.Printf("Error reading signed certificate: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Read the CA certificate
caCert, err := os.ReadFile(filepath.Join(pkiDir, "ca.crt"))
if err != nil {
log.Printf("Error reading CA certificate: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Generate a unique node UID
nodeUID := uuid.New().String()
// Store node registration in etcd (placeholder for now)
// In a future phase, we'll implement proper node registration with subnet assignment
// Create response
joinResp := cli.JoinResponse{
NodeName: joinReq.NodeName,
NodeUID: nodeUID,
SignedCertificate: base64.StdEncoding.EncodeToString(signedCert),
CACertificate: base64.StdEncoding.EncodeToString(caCert),
AssignedSubnet: "10.100.0.0/24", // Placeholder, will be properly implemented in network phase
}
// If etcd peer was requested, add join instructions (placeholder)
if etcdPeer {
joinResp.EtcdJoinInstructions = "Etcd peer join not implemented in this phase"
}
// Send response
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(joinResp); err != nil {
log.Printf("Error encoding join response: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
log.Printf("Successfully processed join request for node: %s", joinReq.NodeName)
})
joinHandler := api.NewJoinHandler(etcdStore, caKeyPath, caCertPath)
apiServer.RegisterJoinHandler(joinHandler)
log.Printf("Registered join handler with CA key: %s, CA cert: %s", caKeyPath, caCertPath)
// Start the server in a goroutine
go func() {