Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repos:
- id: go-unit-tests

- repo: https://github.com/golangci/golangci-lint
rev: v1.62.2
rev: v2.11.4
hooks:
- id: golangci-lint
args: [--config=.golangci.yml]
5 changes: 5 additions & 0 deletions cmd/admin/handlers/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ func (h *HandlersAdmin) ConfPOSTHandler(w http.ResponseWriter, r *http.Request)
adminErrorResponse(w, "invalid CSRF token", http.StatusInternalServerError, nil)
return
}
// Check if configuration is read-only
if h.OsqueryValues.ReadOnly {
adminErrorResponse(w, "configuration is read-only", http.StatusForbidden, nil)
return
}
if c.ConfigurationB64 != "" {
// Base64 decode received configuration
// TODO verify configuration
Expand Down
59 changes: 47 additions & 12 deletions cmd/admin/templates/conf.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@
</div>
<div class="card-body">

<textarea id="options_conf" name="options_conf">{{ .Environment.Options }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
Comment thread
javuto marked this conversation as resolved.
Outdated
</div>
{{ end }}
<textarea id="options_conf" name="options_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.Options }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="options_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -136,7 +141,12 @@
</div>
<div class="card-body">

<textarea id="schedule_conf" name="schedule_conf">{{ .Environment.Schedule }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
Comment thread
javuto marked this conversation as resolved.
Outdated
</div>
{{ end }}
<textarea id="schedule_conf" name="schedule_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.Schedule }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="schedule_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -170,7 +180,12 @@
</div>
<div class="card-body">

<textarea id="packs_conf" name="packs_conf">{{ .Environment.Packs }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
</div>
{{ end }}
<textarea id="packs_conf" name="packs_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.Packs }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="packs_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -204,7 +219,12 @@
</div>
<div class="card-body">

<textarea id="atc_conf" name="atc_conf">{{ .Environment.ATC }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
</div>
{{ end }}
<textarea id="atc_conf" name="atc_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.ATC }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="atc_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -238,7 +258,12 @@
</div>
<div class="card-body">

<textarea id="decorators_conf" name="decorators_conf">{{ .Environment.Decorators }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
</div>
{{ end }}
<textarea id="decorators_conf" name="decorators_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.Decorators }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="decorators_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -272,7 +297,12 @@
</div>
<div class="card-body">

<textarea id="final_conf" name="final_conf">{{ .Environment.Configuration }}</textarea>
{{ if $leftmeta.OsqueryValues.ReadOnly }}
<div class="alert alert-warning py-2 mb-2" role="alert">
<i class="fas fa-lock mr-1"></i> Editing is disabled for this environment.
</div>
{{ end }}
<textarea id="final_conf" name="final_conf" {{ if $leftmeta.OsqueryValues.ReadOnly }}readonly{{ end }}>{{ .Environment.Configuration }}</textarea>
<div class="row">
<div class="col-md-12">
<button id="conf_json_status_color" class="text-left btn btn-sm btn-square btn-block btn-success disabled">
Expand Down Expand Up @@ -311,14 +341,19 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
<script src="/static/js/configuration.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var isReadOnly = {{ if $leftmeta.OsqueryValues.ReadOnly }}true{{ else }}false{{ end }};
if (isReadOnly) {
$('.main button').prop("disabled", true).addClass("disabled");
}

// Codemirror editor for configuration
// JSON validity check when content is changed
var editorConfiguration = CodeMirror.fromTextArea(document.getElementById("final_conf"), {
mode: 'application/json',
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#final_conf').data('CodeMirrorInstance', editorConfiguration);
editorConfiguration.on('change', function(_editor){
Expand Down Expand Up @@ -361,7 +396,7 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#options_conf').data('CodeMirrorInstance', editorOptions);
editorOptions.on('change', function(_editor){
Expand Down Expand Up @@ -407,7 +442,7 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#schedule_conf').data('CodeMirrorInstance', editorSchedule);
editorSchedule.on('change', function(_editor){
Expand Down Expand Up @@ -452,7 +487,7 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#packs_conf').data('CodeMirrorInstance', editorPacks);
editorPacks.on('change', function(_editor){
Expand Down Expand Up @@ -495,7 +530,7 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#atc_conf').data('CodeMirrorInstance', editorATC);
editorATC.on('change', function(_editor){
Expand Down Expand Up @@ -538,7 +573,7 @@ <h4 class="alert-heading"><i class="fas fa-exclamation-triangle"></i> osquery co
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: false
readOnly: isReadOnly
});
$('#decorators_conf').data('CodeMirrorInstance', editorDecorators);
editorDecorators.on('change', function(_editor){
Expand Down
9 changes: 9 additions & 0 deletions cmd/tls/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type HandlersTLS struct {
Logs *logging.LoggerTLS
WriteHandler *batchWriter
OsqueryValues *config.YAMLConfigurationOsquery
ConfigEndpoints *config.YAMLConfigurationEndpoints
DebugHTTP *zerolog.Logger
DebugHTTPConfig *config.YAMLConfigurationDebug
}
Expand Down Expand Up @@ -149,6 +150,14 @@ func WithOsqueryValues(values *config.YAMLConfigurationOsquery) Option {
}
}

// WithConfigEndpoints to pass configuration endpoints values
func WithConfigEndpoints(endpoints *config.YAMLConfigurationEndpoints) Option {
return func(h *HandlersTLS) {
h.ConfigEndpoints = endpoints
}
}

// WithDebugHTTP to pass debug HTTP configuration values
func WithDebugHTTP(cfg *config.YAMLConfigurationDebug) Option {
return func(h *HandlersTLS) {
h.DebugHTTPConfig = cfg
Expand Down
113 changes: 113 additions & 0 deletions cmd/tls/handlers/post.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package handlers

import (
"bytes"
"compress/gzip"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -1063,3 +1066,113 @@ func (h *HandlersTLS) EnrollPackageHandler(w http.ResponseWriter, r *http.Reques
return
}
}

// OsqueryConfigEndpointHandler - Function to handle the osquery configuration endpoint
func (h *HandlersTLS) OsqueryConfigEndpointHandler(w http.ResponseWriter, r *http.Request) {
// Retrieve environment variable
envVar := r.PathValue("env")
if envVar == "" {
utils.HTTPResponse(w, "", http.StatusBadRequest, []byte(""))
return
}
// To prevent abuse, check if the received UUID is valid
if !utils.CheckUUID(envVar) {
utils.HTTPResponse(w, "", http.StatusBadRequest, []byte(""))
return
}
// Extract secret
secretVar := r.PathValue("secret")
if secretVar == "" {
utils.HTTPResponse(w, "", http.StatusBadRequest, []byte(""))
return
}
confirmed := false
integrityCheck := false
for _, confEndpoint := range *h.ConfigEndpoints {
if confEndpoint.Environment == envVar && confEndpoint.Secret == secretVar {
confirmed = true
integrityCheck = confEndpoint.IntegrityCheck
break
}
}
if !confirmed {
utils.HTTPResponse(w, "", http.StatusForbidden, []byte(""))
return
}
// If we are here, the secret is confirmed, so we can proceed to get the environment
env, err := h.Envs.GetByUUID(envVar)
if err != nil {
log.Err(err).Msg("error getting environment")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
// Debug HTTP
if h.DebugHTTPConfig.EnableHTTP {
utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody)
}
// Decode read POST body
var o types.OsqueryConfigRequest
body, err := io.ReadAll(r.Body)
if err != nil {
log.Err(err).Msg("error reading POST body")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
if err := json.Unmarshal(body, &o); err != nil {
log.Err(err).Msg("error parsing POST body")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
// Decode base64 configuration
configDecoded, err := base64.StdEncoding.DecodeString(o.Configuration)
if err != nil {
log.Err(err).Msg("error decoding base64 configuration")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
// Unzip configuration
gzipReader, err := gzip.NewReader(bytes.NewReader(configDecoded))
if err != nil {
log.Err(err).Msg("error decoding gzip configuration")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
defer gzipReader.Close()
configuration, err := io.ReadAll(gzipReader)
if err != nil {
log.Err(err).Msg("error reading unzipped configuration")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
Comment thread
javuto marked this conversation as resolved.
// Verify integrity of the configuration using the provided hash
if integrityCheck {
hash := sha256.Sum256(configuration)
if o.Integrity != fmt.Sprintf("%x", hash) {
log.Err(err).Msg("configuration integrity check failed")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
Comment thread
javuto marked this conversation as resolved.
Outdated
return
}
}
// Parse configuration
cnf, err := h.Envs.GenStructConf(configuration)
if err != nil {
log.Err(err).Msg("error parsing configuration")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
// Update full configuration
if err := h.Envs.UpdateConfiguration(env.UUID, cnf); err != nil {
log.Err(err).Msg("error saving configuration")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
// Update all configuration parts
if err := h.Envs.UpdateConfigurationParts(env.UUID, cnf); err != nil {
log.Err(err).Msg("error saving configuration parts")
utils.HTTPResponse(w, "", http.StatusInternalServerError, []byte(""))
return
}
response := TLSResponse{Message: "configuration saved successfully"}
// Send response
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, response)
}
24 changes: 16 additions & 8 deletions cmd/tls/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,15 @@ func loadYAMLConfiguration(file string) (config.TLSConfiguration, error) {
func init() {
// Initialize default flagParams
flagParams = &config.ServiceParameters{
Service: &config.YAMLConfigurationService{},
DB: &config.YAMLConfigurationDB{},
BatchWriter: &config.YAMLConfigurationWriter{},
Redis: &config.YAMLConfigurationRedis{},
Osquery: &config.YAMLConfigurationOsquery{},
Osctrld: &config.YAMLConfigurationOsctrld{},
Metrics: &config.YAMLConfigurationMetrics{},
TLS: &config.YAMLConfigurationTLS{},
Service: &config.YAMLConfigurationService{},
DB: &config.YAMLConfigurationDB{},
BatchWriter: &config.YAMLConfigurationWriter{},
Redis: &config.YAMLConfigurationRedis{},
Osquery: &config.YAMLConfigurationOsquery{},
Osctrld: &config.YAMLConfigurationOsctrld{},
ConfigEndpoints: &config.YAMLConfigurationEndpoints{},
Metrics: &config.YAMLConfigurationMetrics{},
TLS: &config.YAMLConfigurationTLS{},
Logger: &config.YAMLConfigurationLogger{
DB: &config.YAMLConfigurationDB{},
S3: &config.S3Logger{},
Expand Down Expand Up @@ -283,6 +284,7 @@ func osctrlService() {
handlers.WithLogs(loggerTLS),
handlers.WithWriteHandler(tlsWriter),
handlers.WithOsqueryValues(flagParams.Osquery),
handlers.WithConfigEndpoints(flagParams.ConfigEndpoints),
handlers.WithDebugHTTP(flagParams.Debug),
)
// ///////////////////////// ALL CONTENT IS UNAUTHENTICATED FOR TLS
Expand Down Expand Up @@ -330,6 +332,12 @@ func osctrlService() {
muxTLS.HandleFunc("POST /{env}/{action}/{platform}/"+environments.DefaultScriptPath, handlersTLS.ScriptHandler)
}

// Enable configuration endpoints if passed via YAML configuration
if flagParams.ConfigEndpoints != nil && len(*flagParams.ConfigEndpoints) > 0 {
log.Info().Msgf("Enabling %d configuration endpoints", len(*flagParams.ConfigEndpoints))
muxTLS.HandleFunc("POST /{env}/{secret}/"+environments.DefaultConfigEndpointPath, handlersTLS.OsqueryConfigEndpointHandler)
}

// ////////////////////////////// Everything is ready at this point!
serviceListener := flagParams.Service.Listener + ":" + strconv.Itoa(flagParams.Service.Port)
if flagParams.TLS.Termination {
Expand Down
1 change: 1 addition & 0 deletions cmd/tls/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func loadedYAMLToServiceParams(yml config.TLSConfiguration, loadedFile string) *
BatchWriter: &yml.BatchWriter,
Redis: &yml.Redis,
Osquery: &yml.Osquery,
ConfigEndpoints: &yml.ConfigEndpoints,
Osctrld: &yml.Osctrld,
Metrics: &yml.Metrics,
TLS: &yml.TLS,
Expand Down
9 changes: 9 additions & 0 deletions pkg/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ type ServiceParameters struct {
Redis *YAMLConfigurationRedis
// osquery configuration values
Osquery *YAMLConfigurationOsquery
// Config endpoints configuration values
ConfigEndpoints *YAMLConfigurationEndpoints
// osctrld configuration values
Osctrld *YAMLConfigurationOsctrld
// Metrics configuration values
Expand Down Expand Up @@ -669,6 +671,13 @@ func initOsqueryFlags(params *ServiceParameters) []cli.Flag {
Sources: cli.EnvVars("OSQUERY_CARVE"),
Destination: &params.Osquery.Carve,
},
&cli.BoolFlag{
Name: "read-only-configuration",
Value: false,
Usage: "Disable configuration changes via admin or api services",
Sources: cli.EnvVars("OSQUERY_READ_ONLY"),
Destination: &params.Osquery.ReadOnly,
},
}
}

Expand Down
Loading
Loading