serviceconfig

package
v0.5.81 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package serviceconfig persists the structured YAML configuration sections (logger, carver, SAML, OIDC, metrics, TLS, etc.) per service into the database. Each row holds one section as a JSON-encoded blob, mirroring the YAML structure so it can be round-tripped back to YAML or rendered in the frontend.

Phase 1 is read-only: sections are seeded from the YAML file on first boot (create-if-missing) and served via GET endpoints. The YAML file remains the live source of truth. Phase 2 will allow editing editable sections from the API, and phase 3 will let services consume the DB values at startup so the YAML can shrink to connection-only settings.

Index

Constants

View Source
const NoEnvironmentID = 0

NoEnvironmentID is the sentinel environment ID for global (non-env-scoped) config rows. Mirrors settings.NoEnvironmentID.

View Source
const SourceDB string = "db"

SourceDB marks a row that has been edited through the API.

View Source
const SourceYAML string = "yaml"

SourceYAML marks a row seeded from the YAML file.

Variables

View Source
var ErrConfigFileNotWritable = errors.New("configuration file is not writable")

ErrConfigFileNotWritable is returned when a persist is attempted against a configuration file the service process cannot write.

View Source
var ErrSectionNotEditable = fmt.Errorf("section is not editable")

ErrSectionNotEditable is returned when UpdateSection is called on a section that is not marked editable in the registry.

View Source
var SectionRegistry = map[string][]SectionSpec{
	config.ServiceTLS: {
		{"service", true, "Core service listener, port, log level, auth mode"},
		{"db", false, "Backend connection — not DB-editable"},
		{"batchWriter", true, "DB batch writer tuning"},
		{"redis", false, "Redis connection — not DB-editable"},
		{"osquery", true, "osquery feature toggles"},
		{"configEndpoints", false, "Config endpoint fan-out targets — contains secrets"},
		{"osctrld", true, "osctrld integration"},
		{"metrics", true, "Prometheus metrics endpoint"},
		{"tls", false, "TLS termination certificate/key paths — not DB-editable"},

		{"carver", false, "File carver configuration — may contain credentials"},
		{"debug", true, "HTTP debug dump settings"},
		{"rateLimits", true, "HTTP request rate limits"},
	},
	config.ServiceAPI: {
		{"service", true, "Core service listener, port, log level, auth mode"},
		{"db", false, "Backend connection — not DB-editable"},
		{"redis", false, "Redis connection — not DB-editable"},
		{"osquery", true, "osquery tables and feature toggles"},

		{"jwt", false, "JWT signing configuration — not DB-editable"},
		{"tls", false, "TLS termination certificate/key paths — not DB-editable"},

		{"carver", false, "File carver configuration — may contain credentials"},
		{"debug", true, "HTTP debug dump settings"},
		{"rateLimits", true, "HTTP request rate limits"},
	},
}

SectionRegistry maps each service to the sections it owns. The order of the slice defines the display order in the frontend. Sections marked Editable=false (db, redis, tls, saml, oidc, jwt, and other connection / secret / auth-bearing sections) can never be written through the API.

Functions

func CheckWritable

func CheckWritable(path string) (bool, string)

CheckWritable reports whether the current process can write the given configuration file, and a human-readable reason when it cannot.

The check opens the file for writing without O_CREATE or O_TRUNC: that tests the permission the persist actually needs without touching the contents, and it is accurate where reasoning about mode bits, uid/gid and ACLs separately would not be.

Types

type ConfigFileStatus

type ConfigFileStatus struct {
	gorm.Model
	Service   string `gorm:"uniqueIndex"`
	Path      string
	Writable  bool
	Reason    string
	CheckedAt time.Time
}

ConfigFileStatus records where a service's YAML configuration file lives and whether the process running that service can write to it.

Each service reports its own row at boot. It cannot be reported centrally: osctrl-tls and osctrl-api run as separate processes, usually in separate containers with separate config volumes, so the API service can neither stat nor write the TLS service's file. The database is the only channel through which that fact travels — the same reason restarts are requested through pkg/servicecommands rather than performed directly.

type SectionSpec

type SectionSpec struct {
	Name     string
	Editable bool
	Info     string
}

SectionSpec describes one section in the registry.

type ServiceConfig

type ServiceConfig struct {
	gorm.Model
	Name          string `gorm:"uniqueIndex:idx_service_config_unique"`
	Service       string `gorm:"uniqueIndex:idx_service_config_unique"`
	EnvironmentID uint   `gorm:"uniqueIndex:idx_service_config_unique"`
	Type          string // always "json" for now
	Value         string `gorm:"type:text"`
	Source        string // "yaml" or "db"
	Editable      bool
	Info          string
}

ServiceConfig stores one YAML configuration section for a service.

type ServiceConfigManager

type ServiceConfigManager struct {
	DB *gorm.DB
}

ServiceConfigManager manages the service_config table.

func NewServiceConfigManager

func NewServiceConfigManager(backend *gorm.DB) *ServiceConfigManager

NewServiceConfigManager initializes the manager and auto-migrates the service_config table.

func (*ServiceConfigManager) GetAll

func (m *ServiceConfigManager) GetAll(envID uint) ([]ServiceConfig, error)

GetAll retrieves all sections across all services.

func (*ServiceConfigManager) GetAllByService

func (m *ServiceConfigManager) GetAllByService(service string, envID uint) ([]ServiceConfig, error)

GetAllByService retrieves all sections for a service.

func (*ServiceConfigManager) GetFileStatus

func (m *ServiceConfigManager) GetFileStatus(service string) (ConfigFileStatus, error)

GetFileStatus retrieves the reported configuration file status of a service.

func (*ServiceConfigManager) GetSection

func (m *ServiceConfigManager) GetSection(service, name string, envID uint) (ServiceConfig, error)

GetSection retrieves one section by service and name.

func (*ServiceConfigManager) HasPendingChanges

func (m *ServiceConfigManager) HasPendingChanges(service string, envID uint) (bool, error)

HasPendingChanges reports whether any section of the service has been edited through the API and therefore no longer matches the YAML file on disk. A row with source=db is by definition a change the file does not have.

func (*ServiceConfigManager) IsEditable

func (m *ServiceConfigManager) IsEditable(service, section string) bool

IsEditable checks that the section is registered and marked editable.

func (*ServiceConfigManager) PersistToFile

func (m *ServiceConfigManager) PersistToFile(service, path string, cfg *config.ServiceParameters, envID uint) error

PersistToFile writes the effective configuration — the YAML file with the DB-edited sections overlaid — back to disk, then marks every section as being in sync with the file again.

cfg must be a FRESH load of the YAML file, never the running service's live ServiceParameters: Resolve mutates what it is given, so passing the live struct would apply the operator's pending edits to the running service without the restart they are supposed to go through.

func (*ServiceConfigManager) ReportFile

func (m *ServiceConfigManager) ReportFile(service, path string) error

ReportFile records this service's configuration file path and whether the running process can write it. Services call this at boot, alongside Seed.

func (*ServiceConfigManager) Resolve

func (m *ServiceConfigManager) Resolve(service string, cfg *config.ServiceParameters, envID uint) error

Resolve applies DB-edited sections back into ServiceParameters so the service uses the operator's DB values at runtime instead of the YAML defaults. For each section with source=db, the JSON value is unmarshaled into the matching ServiceParameters field, overriding the YAML value. Sections with source=yaml are skipped — the YAML value is already in ServiceParameters from the initial load.

This is the core of phase 3: the YAML file bootstraps the config, the DB overrides it for sections the operator has edited through the API.

func (*ServiceConfigManager) Seed

func (m *ServiceConfigManager) Seed(service string, cfg *config.ServiceParameters, envID uint) error

Seed persists all sections from the YAML-loaded ServiceParameters into the database using create-if-missing semantics. If a row already exists for a (service, section, envID) tuple it is left untouched — the YAML never overwrites a DB-edited value. This makes seeding idempotent and safe to run on every boot.

func (*ServiceConfigManager) UpdateSection

func (m *ServiceConfigManager) UpdateSection(service, name, value string, envID uint) (ServiceConfig, error)

UpdateSection replaces the JSON value of an editable section. It validates that the new value is valid JSON, that the section exists and is marked editable, and flips the source to SourceDB so subsequent boots won't clobber the change. Returns the updated row.

func (*ServiceConfigManager) VerifySection

func (m *ServiceConfigManager) VerifySection(service, section string) bool

VerifySection checks that the section is registered for the given service.

func (*ServiceConfigManager) VerifyService

func (m *ServiceConfigManager) VerifyService(service string) bool

VerifyService checks that the service is one of the known services.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL