Accountant: Inventar, where, Checkmk, Abrechnung

This commit is contained in:
br committed 2026-10-18 17:08:58 +02:00
1 parent 9f16148cde
commit dd93277750
59 files changed
+7522 -113

No files matched your search

+48 -2
View File
@@ -32,6 +32,7 @@ import (
"sultan/pkg/lander"
"sultan/pkg/notify"
"sultan/pkg/policy"
"sultan/pkg/ratelimit"
"sultan/pkg/registry"
"sultan/pkg/store"
"sultan/pkg/stream"
@@ -67,6 +68,16 @@ type Config struct {
// -1 = unbegrenzt) – gegen das Durchprobieren von Einladungen.
EnrollLimit int
// Accountant (Spec 5.3): Einladung (nur beim ersten Start) bzw.
// Adress-Override; ohne beides und ohne accountant.json meldet der
// Server nichts. Interval: Prüfen auf Änderungen (Default 10s),
// Heartbeat: Lebenszeichen ohne Änderung (Default 1h).
AccountantToken string
AccountantURL string
AccountantInterval time.Duration
AccountantHeartbeat time.Duration
Version string // für den Bericht (sultan version)
interceptorDerived bool // InterceptorURL folgt der gebundenen Lander-Adresse
}
@@ -99,6 +110,12 @@ func (c Config) withDefaults() Config {
if c.EnrollLimit == 0 {
c.EnrollLimit = 10
}
if c.AccountantInterval <= 0 {
c.AccountantInterval = 10 * time.Second
}
if c.AccountantHeartbeat <= 0 {
c.AccountantHeartbeat = time.Hour
}
if c.Logger == nil {
c.Logger = slog.New(slog.NewTextHandler(os.Stderr, nil))
}
@@ -141,7 +158,8 @@ type Server struct {
registry *registry.Registry
lander *lander.Lander
notifier notify.Target // nil = keine Benachrichtigungen
enrollRL *rateLimiter
enrollRL *ratelimit.Limiter
payee *payee // nil = meldet nicht an einen Accountant
vmu sync.Mutex
violations []time.Time // Zeitpunkte der Policy-Verstöße (für Checkmk)
@@ -151,6 +169,7 @@ type Server struct {
providerAddr string
landerAddr string
agentSrv *http.Server
payeeRunning bool
providerSrv *http.Server
landerSrv *http.Server
serveErr chan error
@@ -199,6 +218,11 @@ func New(ctx context.Context, cfg Config) (*Server, error) {
st.Close()
return nil, err
}
pay, err := newPayee(cfg, cfg.Logger)
if err != nil {
st.Close()
return nil, err
}
s := &Server{
cfg: cfg,
@@ -209,9 +233,13 @@ func New(ctx context.Context, cfg Config) (*Server, error) {
verifier: &auth.Verifier{Directory: st},
serveErr: make(chan error, 3),
stopRefresh: make(chan struct{}),
payee: pay,
}
if pay != nil {
pay.s = s
}
s.hub = &stream.Hub{OnConnect: s.nodeOnline, OnDisconnect: s.nodeOffline, SigningKey: master}
s.enrollRL = &rateLimiter{limit: cfg.EnrollLimit, window: time.Minute}
s.enrollRL = &ratelimit.Limiter{Limit: cfg.EnrollLimit, Window: time.Minute}
s.notifier = cfg.Notify
if s.notifier == nil {
var targets notify.Multi
@@ -260,6 +288,12 @@ func loadOrCreateMasterKey(path string) (ed25519.PrivateKey, error) {
// Fingerprint ist der Fingerprint des TLS-Zertifikats (für Enrollment-Tokens).
func (s *Server) Fingerprint() crypto.Fingerprint { return s.cert.Fingerprint }
// NodeState liefert den zuletzt gemeldeten Zustand eines verbundenen
// Knotens (Kopie, inkl. Zusatzdaten für das Inventar).
func (s *Server) NodeState(nodeID string) (registry.NodeState, bool) {
return s.registry.State(nodeID)
}
// MasterPublicKey ist der Schlüssel, mit dem Lizenzen geprüft werden.
func (s *Server) MasterPublicKey() ed25519.PublicKey {
return s.master.Public().(ed25519.PublicKey)
@@ -338,6 +372,12 @@ func (s *Server) Start() error {
go s.serve(func() error { return landerSrv.Serve(landerLn) })
}
go s.refreshLoop()
if s.payee != nil {
s.mu.Lock()
s.payeeRunning = true
s.mu.Unlock()
go s.payee.run()
}
s.log.Info("Sultan-Server gestartet",
"agent", s.AgentAddr(), "provider", s.ProviderAddr(), "lander", s.LanderAddr(),
@@ -405,6 +445,12 @@ func (s *Server) Shutdown(ctx context.Context) error {
errs = append(errs, srv.Shutdown(ctx))
}
}
s.mu.Lock()
payeeRunning := s.payeeRunning
s.mu.Unlock()
if payeeRunning {
s.payee.shutdown() // vor dem Schließen der Datenbank
}
errs = append(errs, s.store.Close())
s.shutdownErr = errors.Join(errs...)
s.log.Info("Sultan-Server beendet")