fleet/server/service/service.go
Victor Lyuboslavsky aaac4b1dfe
Changes needed before gokit/log to slog transition. (#39527)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38889

PLEASE READ BELOW before looking at file changes

Before converting individual files/packages to slog, we generally need
to make these 2 changes to make the conversion easier:
- Replace uses of `kitlog.With` since they are not fully compatible with
our kitlog adapter
- Directly use the kitlog adapter logger type instead of the kitlog
interface, which will let us have direct access to the underlying slog
logger: `*logging.Logger`

Note: that I did not replace absolutely all uses of `kitlog.Logger`, but
I did remove all uses of `kitlog.With` except for these due to
complexity:
- server/logging/filesystem.go and the other log writers (webhook,
firehose, kinesis, lambda, pubsub, nats)
- server/datastore/mysql/nanomdm_storage.go (adapter pattern)
- server/vulnerabilities/nvd/* (cascades to CLI tools)
- server/service/osquery_utils/queries.go (callback type signatures
cascade broadly)
- cmd/maintained-apps/ (standalone, so can be transitioned later all at
once)

Most of the changes in this PR follow these patterns:
- `kitlog.Logger` type → `*logging.Logger`
- `kitlog.With(logger, ...)` → `logger.With(...)`
- `kitlog.NewNopLogger() → logging.NewNopLogger()`, including similar
variations such as `logging.NewLogfmtLogger(w)` and
`logging.NewJSONLogger(w)`
- removed many now-unused kitlog imports

Unique changes that the PR review should focus on:
- server/platform/logging/kitlog_adapter.go: Core adapter changes
- server/platform/logging/logging.go: New convenience functions
- server/service/integration_logger_test.go: Test changes for slog

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
  - Was added in previous PR

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Migrated the codebase to a unified internal structured logging system
for more consistent, reliable logs and observability.
* No user-facing functionality changed; runtime behavior and APIs remain
compatible.
* **Tests**
* Updated tests to use the new logging helpers to ensure consistent test
logging and validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-11 10:08:33 -06:00

204 lines
7 KiB
Go

// Package service holds the implementation of the fleet interface and HTTP
// endpoints for the API
package service
import (
"context"
"fmt"
"html/template"
"sync"
"time"
"github.com/WatchBeam/clock"
"github.com/fleetdm/fleet/v4/server/authz"
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/android"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
microsoft_mdm "github.com/fleetdm/fleet/v4/server/mdm/microsoft"
nanodep_storage "github.com/fleetdm/fleet/v4/server/mdm/nanodep/storage"
nanomdm_push "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/push"
nanomdm_storage "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/storage"
"github.com/fleetdm/fleet/v4/server/platform/logging"
"github.com/fleetdm/fleet/v4/server/service/async"
"github.com/fleetdm/fleet/v4/server/service/conditional_access_microsoft_proxy"
"github.com/fleetdm/fleet/v4/server/sso"
)
var _ fleet.Service = (*Service)(nil)
// Service is the struct implementing fleet.Service. Create a new one with NewService.
type Service struct {
ds fleet.Datastore
task *async.Task
carveStore fleet.CarveStore
resultStore fleet.QueryResultStore
liveQueryStore fleet.LiveQueryStore
logger *logging.Logger
config config.FleetConfig
clock clock.Clock
osqueryLogWriter *OsqueryLogger
mailService fleet.MailService
ssoSessionStore sso.SessionStore
failingPolicySet fleet.FailingPolicySet
enrollHostLimiter fleet.EnrollHostLimiter
authz *authz.Authorizer
jitterMu *sync.Mutex
jitterH map[time.Duration]*jitterHashTable
geoIP fleet.GeoIP
*fleet.EnterpriseOverrides
depStorage nanodep_storage.AllDEPStorage
mdmStorage nanomdm_storage.AllStorage
mdmPushService nanomdm_push.Pusher
mdmAppleCommander *apple_mdm.MDMAppleCommander
cronSchedulesService fleet.CronSchedulesService
wstepCertManager microsoft_mdm.CertManager
scepConfigService fleet.SCEPConfigService
digiCertService fleet.DigiCertService
conditionalAccessMicrosoftProxy ConditionalAccessMicrosoftProxy
keyValueStore fleet.KeyValueStore
androidSvc android.Service
}
// ConditionalAccessMicrosoftProxy is the interface of the Microsoft compliance proxy.
type ConditionalAccessMicrosoftProxy interface {
// Create creates the integration on the MS proxy and returns the consent URL.
Create(ctx context.Context, tenantID string) (*conditional_access_microsoft_proxy.CreateResponse, error)
// Get returns the integration settings.
Get(ctx context.Context, tenantID string, secret string) (*conditional_access_microsoft_proxy.GetResponse, error)
// Delete deprovisions the tenant on Microsoft and deletes the integration in the proxy service.
// Returns a fleet.IsNotFound error if the integration doesn't exist.
Delete(ctx context.Context, tenantID string, secret string) (*conditional_access_microsoft_proxy.DeleteResponse, error)
// SetComplianceStatus sets the inventory and compliance status of a host.
// Returns the message ID to query the status of the operation (MS has an asynchronous API).
SetComplianceStatus(
ctx context.Context,
tenantID string, secret string,
deviceID string,
userPrincipalName string,
mdmEnrolled bool,
deviceName, osName, osVersion string,
compliant bool,
lastCheckInTime time.Time,
) (*conditional_access_microsoft_proxy.SetComplianceStatusResponse, error)
// GetMessageStatusResponse returns the status of a "compliance set" operation.
GetMessageStatus(ctx context.Context, tenantID string, secret string, messageID string) (*conditional_access_microsoft_proxy.GetMessageStatusResponse, error)
}
func (svc *Service) LookupGeoIP(ctx context.Context, ip string) *fleet.GeoLocation {
return svc.geoIP.Lookup(ctx, ip)
}
func (svc *Service) SetEnterpriseOverrides(overrides fleet.EnterpriseOverrides) {
svc.EnterpriseOverrides = &overrides
}
// OsqueryLogger holds osqueryd's status and result loggers.
type OsqueryLogger struct {
// Status holds the osqueryd's status logger.
//
// See https://osquery.readthedocs.io/en/stable/deployment/logging/#status-logs
Status fleet.JSONLogger
// Result holds the osqueryd's result logger.
//
// See https://osquery.readthedocs.io/en/stable/deployment/logging/#results-logs
Result fleet.JSONLogger
}
// NewService creates a new service from the config struct
func NewService(
ctx context.Context,
ds fleet.Datastore,
task *async.Task,
resultStore fleet.QueryResultStore,
logger *logging.Logger,
osqueryLogger *OsqueryLogger,
config config.FleetConfig,
mailService fleet.MailService,
c clock.Clock,
sso sso.SessionStore,
lq fleet.LiveQueryStore,
carveStore fleet.CarveStore,
failingPolicySet fleet.FailingPolicySet,
geoIP fleet.GeoIP,
enrollHostLimiter fleet.EnrollHostLimiter,
depStorage nanodep_storage.AllDEPStorage,
mdmStorage fleet.MDMAppleStore,
mdmPushService nanomdm_push.Pusher,
cronSchedulesService fleet.CronSchedulesService,
wstepCertManager microsoft_mdm.CertManager,
scepConfigService fleet.SCEPConfigService,
digiCertService fleet.DigiCertService,
conditionalAccessProxy ConditionalAccessMicrosoftProxy,
keyValueStore fleet.KeyValueStore,
androidSvc android.Service,
) (fleet.Service, error) {
authorizer, err := authz.NewAuthorizer()
if err != nil {
return nil, fmt.Errorf("new authorizer: %w", err)
}
svc := &Service{
ds: ds,
task: task,
carveStore: carveStore,
resultStore: resultStore,
liveQueryStore: lq,
logger: logger,
config: config,
clock: c,
osqueryLogWriter: osqueryLogger,
mailService: mailService,
ssoSessionStore: sso,
failingPolicySet: failingPolicySet,
authz: authorizer,
jitterH: make(map[time.Duration]*jitterHashTable),
jitterMu: new(sync.Mutex),
geoIP: geoIP,
enrollHostLimiter: enrollHostLimiter,
depStorage: depStorage,
// TODO: remove mdmStorage and mdmPushService when
// we remove deprecated top-level service methods
// from the prototype.
mdmStorage: mdmStorage,
mdmPushService: mdmPushService,
mdmAppleCommander: apple_mdm.NewMDMAppleCommander(mdmStorage, mdmPushService),
cronSchedulesService: cronSchedulesService,
wstepCertManager: wstepCertManager,
scepConfigService: scepConfigService,
digiCertService: digiCertService,
conditionalAccessMicrosoftProxy: conditionalAccessProxy,
keyValueStore: keyValueStore,
androidSvc: androidSvc,
}
return validationMiddleware{svc, ds, sso}, nil
}
func (svc *Service) SendEmail(ctx context.Context, mail fleet.Email) error {
return svc.mailService.SendEmail(ctx, mail)
}
type validationMiddleware struct {
fleet.Service
ds fleet.Datastore
ssoSessionStore sso.SessionStore
}
// getAssetURL simply returns the base url used for retrieving image assets from fleetdm.com.
func getAssetURL() template.URL {
return template.URL("https://fleetdm.com/images/permanent")
}