Skip to content

Commit

Permalink
[CWS-3833] Refreshing security agent secrets from the CLI (DataDog#33092
Browse files Browse the repository at this point in the history
)
  • Loading branch information
paulcacheux authored Jan 20, 2025
1 parent d12fb30 commit c2c17b7
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 13 deletions.
29 changes: 25 additions & 4 deletions cmd/agent/subcommands/secret/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,31 @@ func secretRefresh(config config.Component, _ log.Component) error {
}
fmt.Println(string(res))
}

{
fmt.Println("Security Agent refresh:")
res, err := securityAgentSecretRefresh(config)
if err != nil {
// the security agent might not be running
// so we handle the error in a non-fatal way
fmt.Println(err.Error())
} else {
fmt.Println(string(res))
}
}

return nil
}

func traceAgentSecretRefresh(conf config.Component) ([]byte, error) {
func commonSubAgentSecretRefresh(conf config.Component, agentName, portConfigName string) ([]byte, error) {
err := apiutil.SetAuthToken(conf)
if err != nil {
return nil, err
}

port := conf.GetInt("apm_config.debug.port")
port := conf.GetInt(portConfigName)
if port <= 0 {
return nil, fmt.Errorf("invalid apm_config.debug.port -- %d", port)
return nil, fmt.Errorf("invalid %s -- %d", portConfigName, port)
}

c := apiutil.GetClient(false)
Expand All @@ -104,12 +117,20 @@ func traceAgentSecretRefresh(conf config.Component) ([]byte, error) {
url := fmt.Sprintf("https://127.0.0.1:%d/secret/refresh", port)
res, err := apiutil.DoGet(c, url, apiutil.CloseConnection)
if err != nil {
return nil, fmt.Errorf("could not contact trace-agent: %s", err)
return nil, fmt.Errorf("could not contact %s: %s", agentName, err)
}

return res, nil
}

func traceAgentSecretRefresh(conf config.Component) ([]byte, error) {
return commonSubAgentSecretRefresh(conf, "trace-agent", "apm_config.debug.port")
}

func securityAgentSecretRefresh(conf config.Component) ([]byte, error) {
return commonSubAgentSecretRefresh(conf, "security-agent", "security_agent.cmd_port")
}

func callIPCEndpoint(config config.Component, endpointURL string) ([]byte, error) {
endpoint, err := apiutil.NewIPCEndpoint(config, endpointURL)
if err != nil {
Expand Down
23 changes: 22 additions & 1 deletion cmd/security-agent/api/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import (

"github.com/DataDog/datadog-agent/cmd/agent/common"
"github.com/DataDog/datadog-agent/cmd/agent/common/signals"
"github.com/DataDog/datadog-agent/comp/core/secrets"
"github.com/DataDog/datadog-agent/comp/core/settings"
"github.com/DataDog/datadog-agent/comp/core/status"
workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def"
apiutil "github.com/DataDog/datadog-agent/pkg/api/util"
pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup"
"github.com/DataDog/datadog-agent/pkg/flare"
"github.com/DataDog/datadog-agent/pkg/status/health"
Expand All @@ -31,14 +33,16 @@ type Agent struct {
statusComponent status.Component
settings settings.Component
wmeta workloadmeta.Component
secrets secrets.Component
}

// NewAgent returns a new Agent
func NewAgent(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) *Agent {
func NewAgent(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, secrets secrets.Component) *Agent {
return &Agent{
statusComponent: statusComponent,
settings: settings,
wmeta: wmeta,
secrets: secrets,
}
}

Expand All @@ -60,6 +64,7 @@ func (a *Agent) SetupHandlers(r *mux.Router) {
verbose := r.URL.Query().Get("verbose") == "true"
workloadList(w, verbose, a.wmeta)
}).Methods("GET")
r.HandleFunc("/secret/refresh", a.refreshSecrets).Methods("GET")
}

func workloadList(w http.ResponseWriter, verbose bool, wmeta workloadmeta.Component) {
Expand Down Expand Up @@ -153,3 +158,19 @@ func (a *Agent) makeFlare(w http.ResponseWriter, _ *http.Request) {
}
w.Write([]byte(filePath))
}

func (a *Agent) refreshSecrets(w http.ResponseWriter, req *http.Request) {
if apiutil.Validate(w, req) != nil {
return
}

res, err := a.secrets.Refresh()
if err != nil {
log.Errorf("error while refresing secrets: %s", err)
w.Header().Set("Content-Type", "application/json")
body, _ := json.Marshal(map[string]string{"error": err.Error()})
http.Error(w, string(body), http.StatusInternalServerError)
return
}
w.Write([]byte(res))
}
5 changes: 3 additions & 2 deletions cmd/security-agent/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/DataDog/datadog-agent/cmd/security-agent/api/agent"
"github.com/DataDog/datadog-agent/comp/api/authtoken"
"github.com/DataDog/datadog-agent/comp/core/secrets"
"github.com/DataDog/datadog-agent/comp/core/settings"
"github.com/DataDog/datadog-agent/comp/core/status"
workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def"
Expand All @@ -38,14 +39,14 @@ type Server struct {
}

// NewServer creates a new Server instance
func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (*Server, error) {
func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component, secrets secrets.Component) (*Server, error) {
listener, err := newListener()
if err != nil {
return nil, err
}
return &Server{
listener: listener,
agent: agent.NewAgent(statusComponent, settings, wmeta),
agent: agent.NewAgent(statusComponent, settings, wmeta, secrets),
tlsConfig: at.GetTLSServerConfig(),
}, nil
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/security-agent/main_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ func (s *service) Run(svcctx context.Context) error {

params := &cliParams{}
err := fxutil.OneShot(
func(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component,
func(log log.Component, config config.Component, secrets secrets.Component, _ statsd.Component, _ sysprobeconfig.Component,
telemetry telemetry.Component, _ workloadmeta.Component, _ *cliParams, statusComponent status.Component, _ autoexit.Component,
settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error {
defer start.StopAgent(log)

err := start.RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at)
err := start.RunAgent(log, config, secrets, telemetry, statusComponent, settings, wmeta, at)
if err != nil {
if errors.Is(err, start.ErrAllComponentsDisabled) {
// If all components are disabled, we should exit cleanly
Expand Down
8 changes: 4 additions & 4 deletions cmd/security-agent/subcommands/start/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command {
// TODO(components): note how workloadmeta is passed anonymously, it is still required as it is used
// as a global. This should eventually be fixed and all workloadmeta interactions should be via the
// injected instance.
func start(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error {
func start(log log.Component, config config.Component, secrets secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error {
defer StopAgent(log)

err := RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at)
err := RunAgent(log, config, secrets, telemetry, statusComponent, settings, wmeta, at)
if errors.Is(err, ErrAllComponentsDisabled) || errors.Is(err, errNoAPIKeyConfigured) {
return nil
}
Expand Down Expand Up @@ -256,7 +256,7 @@ var ErrAllComponentsDisabled = errors.New("all security-agent component are disa
var errNoAPIKeyConfigured = errors.New("no API key configured")

// RunAgent initialized resources and starts API server
func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (err error) {
func RunAgent(log log.Component, config config.Component, secrets secrets.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (err error) {
if err := coredump.Setup(config); err != nil {
log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err)
}
Expand Down Expand Up @@ -299,7 +299,7 @@ func RunAgent(log log.Component, config config.Component, telemetry telemetry.Co
}
}()

srv, err = api.NewServer(statusComponent, settings, wmeta, at)
srv, err = api.NewServer(statusComponent, settings, wmeta, at, secrets)
if err != nil {
return log.Errorf("Error while creating api server, exiting: %v", err)
}
Expand Down

0 comments on commit c2c17b7

Please sign in to comment.