package handlers import ( "log" "net/http" "os" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/utils" "github.com/gin-gonic/gin" ) type AgentRegistrationGroup struct { *Handlers certBundle *utils.CertBundle } func NewAgentRegistrationGroup(h *Handlers) *AgentRegistrationGroup { certDir := getCertDir() bundle, err := utils.LoadCertBundle(certDir) if err != nil { log.Printf("[agent-reg] WARNING: cert bundle load failed: %v", err) } return &AgentRegistrationGroup{ Handlers: h, certBundle: bundle, } } // CreateRegistrationToken — админ создаёт токен для агента // @Summary Create registration token // @Tags agents // @Accept json // @Produce json // @Param request body repository.RegistrationRequest true "Label" // @Success 200 {object} map[string]string // @Security Bearer // @Router /agents/register-token [post] func (arg *AgentRegistrationGroup) CreateRegistrationToken(c *gin.Context) { var req repository.RegistrationRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } token, err := arg.Repo.CreateRegistrationToken(req.Label) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create token"}) return } c.JSON(http.StatusOK, gin.H{"token": token}) } // Register — агент шлёт CSR + token, получает сертификаты // @Summary Register agent // @Tags agents // @Accept json // @Produce json // @Param request body RegisterRequest true "CSR + token" // @Success 200 {object} RegisterResponse // @Router /agents/register [post] func (arg *AgentRegistrationGroup) Register(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if arg.certBundle == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "certificate bundle not available"}) return } regToken, err := arg.Repo.GetRegistrationToken(req.Token) if err != nil { if err == repository.ErrNotFound { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid registration token"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to verify token"}) return } if regToken.Used { c.JSON(http.StatusGone, gin.H{"error": "registration token already used"}) return } clientCertPEM, err := arg.certBundle.SignCSR([]byte(req.CSR), regToken.Label) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "failed to sign CSR: " + err.Error()}) return } if err := arg.Repo.MarkRegistrationTokenUsed(req.Token); err != nil { log.Printf("[agent-reg] WARNING: failed to mark token used: %v", err) } c.JSON(http.StatusOK, RegisterResponse{ CACert: string(arg.certBundle.GetCACertPEM()), ClientCert: string(clientCertPEM), }) } type RegisterRequest struct { CSR string `json:"csr" binding:"required"` Token string `json:"token" binding:"required"` } type RegisterResponse struct { CACert string `json:"ca_cert"` ClientCert string `json:"client_cert"` } func getCertDir() string { if d := os.Getenv("SSL_CERT_DIR"); d != "" { return d } return "/var/lib/hellreign/ssl" }