Files
HellreigN/backend/internal/handlers/agent_register.go
T
d3m0k1d 0f8b148279
ci-agent / build (push) Failing after 2m50s
fix: linter and docs
2026-04-04 19:44:16 +03:00

122 lines
3.2 KiB
Go

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"
}