Netpulse_SasS/server/internal/grpcapi/enroll.go
byrsapty c28852df66
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Syslog: транспорт до сервера і тригер позачергового бекапу
Приймач на зонді лежав без діла — тепер під'єднаний. Окремий стрім
StreamLogs, а не контрольний канал: сплеск логів під час аварії не має
заважати heartbeat і командам.

Хост зіставляється за адресою джерела на зонді: у сервера немає
контексту мережі клієнта, а один приватний діапазон трапляється в
десятках кабінетів. Невідома адреса не привід викинути подію.

Подія, що збіглася зі зразком у ncm.device_policies.syslog_match,
ставить позачерговий збір конфігу. Типовий зразок покриває Cisco,
HP/Huawei, Juniper і MikroTik — навмисно широкий: зайвий бекап коштує
секунд, пропущений — цілої зміни.

Заразом увесь репозиторій прогнано через gofmt: CI, написаний два
кроки тому, перевіряє це і впав би на 29 файлах із порушеннями,
накопиченими за весь проєкт.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 15:20:32 +03:00

67 lines
3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package grpcapi
import (
"context"
"errors"
"strings"
npv1 "github.com/netpulse/netpulse/gen/go/netpulse/v1"
"github.com/netpulse/netpulse/server/internal/store"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// EnrollService реалізує npv1.EnrollmentServiceServer.
//
// Окремий тип, а не метод на Service: Enroll — єдиний виклик, який іде
// БЕЗ токена зонда, тож він не має проходити через інтерсептор
// автентифікації AgentService. Різні сервіси в одному gRPC-сервері
// дозволяють це розвести без винятків усередині інтерсептора.
type EnrollService struct {
npv1.UnimplementedEnrollmentServiceServer
store *store.Store
// Куди підключатись після реєстрації. Може відрізнятись від адреси,
// на яку прийшов Enroll: балансувальник, регіональний шлюз.
controlEndpoint string
}
func NewEnrollService(st *store.Store, controlEndpoint string) *EnrollService {
return &EnrollService{store: st, controlEndpoint: controlEndpoint}
}
// Enroll обмінює одноразове запрошення на постійний зонд.
//
// Сертифікат у відповіді поки порожній. mTLS у продукті є (сервер
// приймає -client-ca), але він налаштовується окремо від реєстрації:
// власний CA з ротацією — це друга система, і вдавати, що вона вже
// працює, означало б віддати агенту порожній сертифікат і зламати
// з'єднання там, де воно щойно працювало.
func (s *EnrollService) Enroll(ctx context.Context, req *npv1.EnrollRequest) (*npv1.EnrollResponse, error) {
token := strings.TrimSpace(req.GetEnrollmentToken())
if token == "" {
return nil, status.Error(codes.InvalidArgument, "потрібен токен запрошення")
}
b := req.GetBuild()
agent, err := s.store.RedeemEnrollment(ctx, token,
req.GetHostname(), req.GetRequestedName(),
b.GetVersion(), b.GetOs(), b.GetArch())
if errors.Is(err, store.ErrEnrollmentInvalid) {
// Одна відповідь на «немає», «згоріло» і «вже використано»:
// розрізняти їх означало б підказувати, наскільки близько той,
// хто підбирає токени.
return nil, status.Error(codes.PermissionDenied, "запрошення недійсне")
}
if err != nil {
return nil, status.Errorf(codes.Internal, "реєстрація: %v", err)
}
return &npv1.EnrollResponse{
AgentId: agent.ID,
AgentName: agent.Name,
ControlEndpoint: s.controlEndpoint,
AgentToken: agent.Token,
}, nil
}