Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ Example YAML configuration:

```yaml
osctrld:
# osctrl enrollment secret value used to authenticate requests
secret: "thisisthesecret"
# Local path where the osquery enrollment secret file is written or verified
secretFile: "/path/to/osquery.secret"
flags: "/path/to/osquery.flags"
cert: "/path/to/osquery.crt"
Expand All @@ -128,8 +130,8 @@ JSON configuration files are also supported. Use a `.json` extension and osctrld

| Field | Description | Default |
| --- | --- | --- |
| `secret` | Enrollment secret for osctrl authentication | Required for enrollment workflows |
| `secretFile` | Path to the osquery enrollment secret file | OS-dependent |
| `secret` | osctrl enrollment secret value used to authenticate requests | Required for enrollment workflows |
| `secretFile` | Local path where the osquery enrollment secret file is written or verified | OS-dependent |
| `flags` | Path to the osquery flags file | OS-dependent |
| `cert` | Path to the osquery TLS certificate file | OS-dependent |
| `enrollScript` | Path to the enroll script | OS-dependent |
Expand Down Expand Up @@ -246,11 +248,11 @@ go tool cover -func=coverage.out

```text
--configuration FILE, -c FILE Configuration file for osctrld
--secret value, -s value Enrollment secret
--secret value, -s value osctrl enrollment secret
--environment value, -e value osctrl environment name or UUID
--secret-file FILE, -S FILE Secret file for osquery
--flagfile FILE, -F FILE Flag file for osquery
--certificate FILE, -C FILE Certificate file for osquery TLS
--secret-file FILE, -S FILE Local osquery enrollment secret file
--flagfile FILE, -F FILE Local osquery flags file
--certificate FILE, -C FILE Local osquery TLS certificate file
--osctrl-url value, -U value Base URL for the osctrl server
--osquery-path FILE, -o FILE Path to osquery installation
--insecure, -i Ignore TLS warnings
Expand Down
40 changes: 20 additions & 20 deletions cmd/osctrld/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ var (

// FlagsRequest to retrieve flags
type FlagsRequest struct {
Secret string `json:"secret"`
SecretFile string `json:"secretFile"`
CertFile string `json:"certFile"`
OsctrlSecret string `json:"secret"`
OsquerySecretFile string `json:"secretFile"`
OsqueryCertFile string `json:"certFile"`
}

// CertRequest to retrieve certificate
type CertRequest struct {
Secret string `json:"secret"`
OsctrlSecret string `json:"secret"`
}

// ScriptRequest to retrieve script
Expand All @@ -65,13 +65,13 @@ type ExtensionEntry struct {

// ExtensionsRequest to retrieve extension manifest
type ExtensionsRequest struct {
Secret string `json:"secret"`
OsctrlSecret string `json:"secret"`
}

// Function to action on enroll command
func enrollNode(c *cli.Context) error {
log.Debug().Str("url", osctrlURLs.Enroll).Msg("enrolling node")
script, err := retrieveScript(appConfig.Secret, osctrlURLs.Enroll, appConfig.Insecure)
script, err := retrieveScript(appConfig.OsctrlSecret, osctrlURLs.Enroll, appConfig.Insecure)
if err != nil {
return fmt.Errorf("error retrieving enroll - %v", err)
}
Expand All @@ -82,39 +82,39 @@ func enrollNode(c *cli.Context) error {
// Function to action on flags command
func getFlags(c *cli.Context) (bool, error) {
log.Debug().Str("url", osctrlURLs.Flags).Msg("getting flags")
flags, err := retrieveFlags(appConfig.Secret, appConfig.SecretFile, appConfig.CertFile)
flags, err := retrieveFlags(appConfig.OsctrlSecret, appConfig.OsquerySecretFile, appConfig.OsqueryCertFile)
if err != nil {
return false, fmt.Errorf("error retrieving flags - %v", err)
}
log.Debug().Str("flags", flags).Msg("flags content")
changed, err := writeContentExists(appConfig.FlagFile, flags, "flags", appConfig.Force)
changed, err := writeContentExists(appConfig.OsqueryFlagFile, flags, "flags", appConfig.Force)
if err != nil {
return false, err
}
log.Info().Str("path", appConfig.FlagFile).Msg("flags ready")
log.Info().Str("path", appConfig.OsqueryFlagFile).Msg("flags ready")
return changed, nil
}

// Function to action on cert command
func getCert(c *cli.Context) (bool, error) {
log.Debug().Str("url", osctrlURLs.Cert).Msg("getting cert")
cert, err := retrieveCert(appConfig.Secret, osctrlURLs.Cert, appConfig.Insecure)
cert, err := retrieveCert(appConfig.OsctrlSecret, osctrlURLs.Cert, appConfig.Insecure)
if err != nil {
return false, fmt.Errorf("error retrieving cert - %v", err)
}
log.Debug().Str("cert", cert).Msg("cert content")
changed, err := writeContentExists(appConfig.CertFile, cert, "cert", appConfig.Force)
changed, err := writeContentExists(appConfig.OsqueryCertFile, cert, "cert", appConfig.Force)
if err != nil {
return false, err
}
log.Info().Str("path", appConfig.CertFile).Msg("cert ready")
log.Info().Str("path", appConfig.OsqueryCertFile).Msg("cert ready")
return changed, nil
}

// Function to action on remove command. It retrieves the script to run the removal from osctrl
func removeNode(c *cli.Context) error {
log.Debug().Str("url", osctrlURLs.Remove).Msg("removing node")
script, err := retrieveScript(appConfig.Secret, osctrlURLs.Remove, appConfig.Insecure)
script, err := retrieveScript(appConfig.OsctrlSecret, osctrlURLs.Remove, appConfig.Insecure)
if err != nil {
return fmt.Errorf("error retrieving remove - %v", err)
}
Expand All @@ -126,30 +126,30 @@ func removeNode(c *cli.Context) error {
// Function to action on verify command. It verifies flags, cert and secret for and enrolled node in osctrl
func verifyNode(c *cli.Context) error {
// Compare secret with local
log.Debug().Str("path", appConfig.SecretFile).Msg("comparing secret")
if checkFileContent(appConfig.SecretFile, appConfig.Secret) {
log.Debug().Str("path", appConfig.OsquerySecretFile).Msg("comparing secret")
if checkFileContent(appConfig.OsquerySecretFile, appConfig.OsctrlSecret) {
log.Info().Msg("osquery secret is valid")
} else {
log.Warn().Msg("osquery secret mismatch")
}
// Retrieve verification
log.Debug().Str("url", osctrlURLs.Verify).Msg("retrieving verification")
verification, err := retrieveVerify(appConfig.Secret, appConfig.SecretFile, appConfig.CertFile, osctrlURLs.Verify, appConfig.Insecure)
verification, err := retrieveVerify(appConfig.OsctrlSecret, appConfig.OsquerySecretFile, appConfig.OsqueryCertFile, osctrlURLs.Verify, appConfig.Insecure)
if err != nil {
return fmt.Errorf("error retrieving verification - %v", err)
}
// Compare flags with local
log.Debug().Str("path", appConfig.FlagFile).Msg("comparing flags")
if checkFileContent(appConfig.FlagFile, strings.TrimSpace(verification.Flags)) {
log.Debug().Str("path", appConfig.OsqueryFlagFile).Msg("comparing flags")
if checkFileContent(appConfig.OsqueryFlagFile, strings.TrimSpace(verification.Flags)) {
log.Info().Msg("flags are valid")
} else {
log.Warn().Msg("flags mismatch")
}
// Retrieve certificate if flag is present
if strings.Contains(verification.Flags, FlagTLSServerCerts) {
// Compare certificate with local
log.Debug().Str("path", appConfig.CertFile).Msg("comparing certificate")
if checkFileContent(appConfig.CertFile, strings.TrimSpace(verification.Certificate)) {
log.Debug().Str("path", appConfig.OsqueryCertFile).Msg("comparing certificate")
if checkFileContent(appConfig.OsqueryCertFile, strings.TrimSpace(verification.Certificate)) {
log.Info().Msg("osquery certificate is valid")
} else {
log.Warn().Msg("osquery certificate mismatch")
Expand Down
25 changes: 12 additions & 13 deletions cmd/osctrld/actions_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
)

// Helper function to retrieve flags
func retrieveFlags(secret, secretFile, certFile string) (string, error) {
func retrieveFlags(osctrlSecret, osquerySecretFile, osqueryCertFile string) (string, error) {
flagsData := FlagsRequest{
Secret: secret,
SecretFile: secretFile,
CertFile: certFile,
OsctrlSecret: osctrlSecret,
OsquerySecretFile: osquerySecretFile,
OsqueryCertFile: osqueryCertFile,
}
jsonReq, err := json.Marshal(flagsData)
if err != nil {
Expand Down Expand Up @@ -53,29 +53,29 @@ func genericRetrieve(url string, insecure bool, data any) ([]byte, error) {
}

// Helper function to retrieve script
func retrieveScript(secret, url string, insecure bool) (string, error) {
func retrieveScript(osctrlSecret, url string, insecure bool) (string, error) {
scriptData := ScriptRequest{
Secret: secret,
OsctrlSecret: osctrlSecret,
}
resp, err := genericRetrieve(url, insecure, scriptData)
return strings.TrimSpace(string(resp)), err
}

// Helper function to retrieve cert
func retrieveCert(secret, url string, insecure bool) (string, error) {
func retrieveCert(osctrlSecret, url string, insecure bool) (string, error) {
certData := CertRequest{
Secret: secret,
OsctrlSecret: osctrlSecret,
}
resp, err := genericRetrieve(url, insecure, certData)
return strings.TrimSpace(string(resp)), err
}

// Helper function to retrieve verify
func retrieveVerify(secret, secretFile, certFile, url string, insecure bool) (VerifyResponse, error) {
func retrieveVerify(osctrlSecret, osquerySecretFile, osqueryCertFile, url string, insecure bool) (VerifyResponse, error) {
verifyData := VerifyRequest{
Secret: secret,
SecretFile: secretFile,
CertFile: certFile,
OsctrlSecret: osctrlSecret,
OsquerySecretFile: osquerySecretFile,
OsqueryCertFile: osqueryCertFile,
}
var vData VerifyResponse
resp, err := genericRetrieve(url, insecure, verifyData)
Expand Down Expand Up @@ -149,4 +149,3 @@ func getOsqueryVersion() string {
}
return splitted[2]
}

6 changes: 3 additions & 3 deletions cmd/osctrld/actions_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func TestGenericRetrieve_Success(t *testing.T) {
server := mockOsctrlServer()
defer server.Close()

data := ScriptRequest{Secret: "test-secret"}
data := ScriptRequest{OsctrlSecret: "test-secret"}
body, err := genericRetrieve(server.URL+"/env/enroll/darwin/osctrld-script", false, data)
assert.NoError(t, err)
assert.Contains(t, string(body), "echo enroll")
Expand All @@ -109,14 +109,14 @@ func TestGenericRetrieve_ServerError(t *testing.T) {
server := mockOsctrlServer()
defer server.Close()

data := ScriptRequest{Secret: "test-secret"}
data := ScriptRequest{OsctrlSecret: "test-secret"}
_, err := genericRetrieve(server.URL+"/error", false, data)
assert.Error(t, err)
assert.Contains(t, err.Error(), "HTTP 500")
}

func TestGenericRetrieve_ConnectionRefused(t *testing.T) {
data := ScriptRequest{Secret: "test-secret"}
data := ScriptRequest{OsctrlSecret: "test-secret"}
_, err := genericRetrieve("http://127.0.0.1:1/unreachable", false, data)
assert.Error(t, err)
}
Expand Down
44 changes: 22 additions & 22 deletions cmd/osctrld/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ func setupTestConfig(t *testing.T, server *httptest.Server) (cleanup func()) {
dir := t.TempDir()

appConfig = Configuration{
Secret: "test-secret",
SecretFile: filepath.Join(dir, "osquery.secret"),
FlagFile: filepath.Join(dir, "osquery.flags"),
CertFile: filepath.Join(dir, "osctrl.crt"),
OsqueryPath: dir,
Environment: "env",
BaseURL: server.URL,
Insecure: false,
Verbose: false,
Force: true,
EnrollScript: filepath.Join(dir, "osctrld-enroll.sh"),
RemoveScript: filepath.Join(dir, "osctrld-remove.sh"),
OsctrlSecret: "test-secret",
OsquerySecretFile: filepath.Join(dir, "osquery.secret"),
OsqueryFlagFile: filepath.Join(dir, "osquery.flags"),
OsqueryCertFile: filepath.Join(dir, "osctrl.crt"),
OsqueryPath: dir,
Environment: "env",
BaseURL: server.URL,
Insecure: false,
Verbose: false,
Force: true,
EnrollScript: filepath.Join(dir, "osctrld-enroll.sh"),
RemoveScript: filepath.Join(dir, "osctrld-remove.sh"),
}
osctrlURLs = genURLs(server.URL, "env", false)

Expand Down Expand Up @@ -59,7 +59,7 @@ func TestGetFlags_Success(t *testing.T) {
assert.NoError(t, err)
assert.True(t, changed, "new flags file should report changed")

content, err := os.ReadFile(appConfig.FlagFile)
content, err := os.ReadFile(appConfig.OsqueryFlagFile)
require.NoError(t, err)
assert.Equal(t, "--tls_hostname=osctrl.example.com", string(content))
}
Expand Down Expand Up @@ -98,7 +98,7 @@ func TestGetCert_Success(t *testing.T) {
assert.NoError(t, err)
assert.True(t, changed, "new cert file should report changed")

content, err := os.ReadFile(appConfig.CertFile)
content, err := os.ReadFile(appConfig.OsqueryCertFile)
require.NoError(t, err)
assert.Contains(t, string(content), "BEGIN CERTIFICATE")
}
Expand Down Expand Up @@ -210,14 +210,14 @@ func TestVerifyNode_Success(t *testing.T) {
defer server.Close()

appConfig = Configuration{
Secret: "test-secret",
SecretFile: secretPath,
FlagFile: flagPath,
CertFile: certPath,
OsqueryPath: dir,
Environment: "env",
BaseURL: server.URL,
Verbose: false,
OsctrlSecret: "test-secret",
OsquerySecretFile: secretPath,
OsqueryFlagFile: flagPath,
OsqueryCertFile: certPath,
OsqueryPath: dir,
Environment: "env",
BaseURL: server.URL,
Verbose: false,
}
osctrlURLs.Verify = server.URL + "/verify"

Expand Down
34 changes: 18 additions & 16 deletions cmd/osctrld/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,28 @@ const (
// Configuration holds all configuration values for osctrld.
// Supports both YAML (default) and JSON config files.
type Configuration struct {
Secret string `json:"secret" yaml:"secret" mapstructure:"secret"`
SecretFile string `json:"secretFile" yaml:"secretFile" mapstructure:"secretFile"`
FlagFile string `json:"flags" yaml:"flags" mapstructure:"flags"`
CertFile string `json:"cert" yaml:"cert" mapstructure:"cert"`
EnrollScript string `json:"enrollScript" yaml:"enrollScript" mapstructure:"enrollScript"`
RemoveScript string `json:"removeScript" yaml:"removeScript" mapstructure:"removeScript"`
OsqueryPath string `json:"osquery" yaml:"osquery" mapstructure:"osquery"`
Environment string `json:"environment" yaml:"environment" mapstructure:"environment"`
BaseURL string `json:"baseurl" yaml:"baseurl" mapstructure:"baseurl"`
Insecure bool `json:"insecure" yaml:"insecure" mapstructure:"insecure"`
Verbose bool `json:"verbose" yaml:"verbose" mapstructure:"verbose"`
Force bool `json:"force" yaml:"force" mapstructure:"force"`
LogFormat string `json:"logFormat" yaml:"logFormat" mapstructure:"logFormat"`
Interval int `json:"interval" yaml:"interval" mapstructure:"interval"`
ExtensionsDir string `json:"extensionsDir" yaml:"extensionsDir" mapstructure:"extensionsDir"`
OsctrlSecret string `json:"secret" yaml:"secret" mapstructure:"secret"`
OsquerySecretFile string `json:"secretFile" yaml:"secretFile" mapstructure:"secretFile"`
OsqueryFlagFile string `json:"flags" yaml:"flags" mapstructure:"flags"`
OsqueryCertFile string `json:"cert" yaml:"cert" mapstructure:"cert"`
EnrollScript string `json:"enrollScript" yaml:"enrollScript" mapstructure:"enrollScript"`
RemoveScript string `json:"removeScript" yaml:"removeScript" mapstructure:"removeScript"`
OsqueryPath string `json:"osquery" yaml:"osquery" mapstructure:"osquery"`
Environment string `json:"environment" yaml:"environment" mapstructure:"environment"`
BaseURL string `json:"baseurl" yaml:"baseurl" mapstructure:"baseurl"`
Insecure bool `json:"insecure" yaml:"insecure" mapstructure:"insecure"`
Verbose bool `json:"verbose" yaml:"verbose" mapstructure:"verbose"`
Force bool `json:"force" yaml:"force" mapstructure:"force"`
LogFormat string `json:"logFormat" yaml:"logFormat" mapstructure:"logFormat"`
Interval int `json:"interval" yaml:"interval" mapstructure:"interval"`
ExtensionsDir string `json:"extensionsDir" yaml:"extensionsDir" mapstructure:"extensionsDir"`
}

func defaultConfigurationYAML() string {
return `osctrld:
secret: "replace-with-enrollment-secret"
# osctrl enrollment secret value used to authenticate requests
secret: "replace-with-osctrl-enrollment-secret"
# Local path where the osquery enrollment secret file is written or verified
secretFile: "/path/to/osquery.secret"
flags: "/path/to/osquery.flags"
cert: "/path/to/osctrl.crt"
Expand Down
10 changes: 8 additions & 2 deletions cmd/osctrld/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ func TestLoadConfigurationJSON(t *testing.T) {

cfg, err := loadConfiguration(configPath, false)
assert.NoError(t, err)
assert.Equal(t, "test-secret", cfg.Secret)
assert.Equal(t, "test-secret", cfg.OsctrlSecret)
assert.Equal(t, "/tmp/osquery.secret", cfg.OsquerySecretFile)
assert.Equal(t, "/tmp/osquery.flags", cfg.OsqueryFlagFile)
assert.Equal(t, "/tmp/osctrl.crt", cfg.OsqueryCertFile)
assert.Equal(t, "dev", cfg.Environment)
assert.Equal(t, "https://localhost:9000", cfg.BaseURL)
assert.True(t, cfg.Insecure)
Expand Down Expand Up @@ -64,7 +67,10 @@ func TestLoadConfigurationYAML(t *testing.T) {

cfg, err := loadConfiguration(configPath, false)
assert.NoError(t, err)
assert.Equal(t, "test-secret", cfg.Secret)
assert.Equal(t, "test-secret", cfg.OsctrlSecret)
assert.Equal(t, "/tmp/osquery.secret", cfg.OsquerySecretFile)
assert.Equal(t, "/tmp/osquery.flags", cfg.OsqueryFlagFile)
assert.Equal(t, "/tmp/osctrl.crt", cfg.OsqueryCertFile)
assert.Equal(t, "dev", cfg.Environment)
assert.Equal(t, "https://localhost:9000", cfg.BaseURL)
assert.True(t, cfg.Insecure)
Expand Down
Loading
Loading