From f5df8df32bf65b3200bbd057b51a7405141a5fa0 Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Tue, 29 Mar 2022 14:46:20 +0000 Subject: [PATCH 1/3] Allow enabling CWS and CSPM --- attributes/default.rb | 4 ++ recipes/dd-agent.rb | 3 + recipes/security-agent.rb | 77 +++++++++++++++++++++++ recipes/system-probe.rb | 16 ++++- spec/security-agent_spec.rb | 58 +++++++++++++++++ templates/default/security-agent.yaml.erb | 13 ++++ templates/default/system_probe.yaml.erb | 8 ++- 7 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 recipes/security-agent.rb create mode 100644 spec/security-agent_spec.rb create mode 100644 templates/default/security-agent.yaml.erb diff --git a/attributes/default.rb b/attributes/default.rb index 5fdf62af..f691b5f7 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -360,6 +360,10 @@ default['datadog']['process_agent']['container_interval'] = nil default['datadog']['process_agent']['rtcontainer_interval'] = nil +# Cloud Workload Security functionality settings +default['datadog']['security_agent']['cws']['enabled'] = false +default['datadog']['security_agent']['cspm']['enabled'] = false + # System probe functionality settings # Whether this cookbook should write system-probe.yaml or not. diff --git a/recipes/dd-agent.rb b/recipes/dd-agent.rb index 5f190188..f9f860ad 100644 --- a/recipes/dd-agent.rb +++ b/recipes/dd-agent.rb @@ -166,6 +166,9 @@ def template_vars # system-probe is a dependency of the agent on Linux or Windows include_recipe '::system-probe' if system_probe_managed && system_probe_supported +# security-agent is a dependency of the agent on Linux or Windows +include_recipe '::security-agent' unless is_windows + # Installation metadata to let know the agent about installation method and its version include_recipe '::install_info' diff --git a/recipes/security-agent.rb b/recipes/security-agent.rb new file mode 100644 index 00000000..fb6557ce --- /dev/null +++ b/recipes/security-agent.rb @@ -0,0 +1,77 @@ +# +# Cookbook:: datadog +# Recipe:: security-agent +# +# Copyright:: 2011-2022, Datadog +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +is_windows = platform_family?('windows') + +# Set the correct agent startup action +security_agent_enabled = !is_windows && node['datadog']['security_agent']['cws']['enabled'] || node['datadog']['security_agent']['cspm']['enabled'] +security_agent_start = security_agent_enabled ? :start : :stop + +# +# Configures security-agent agent +security_agent_config_file = '/etc/datadog-agent/security-agent.yaml' +security_agent_config_file_exists = ::File.exist?(security_agent_config_file) + +template security_agent_config_file do + runtime_security_extra_config = {} + if node['datadog']['extra_config'] && node['datadog']['extra_config']['security_agent'] && node['datadog']['extra_config']['security_agent']['runtime_security_config'] + node['datadog']['extra_config']['security_agent']['runtime_security_config'].each do |k, v| + next if v.nil? + runtime_security_extra_config[k] = v + end + end + + compliance_extra_config = {} + if node['datadog']['extra_config'] && node['datadog']['extra_config']['security_agent'] && node['datadog']['extra_config']['security_agent']['compliance_config'] + node['datadog']['extra_config']['security_agent']['compliance_config'].each do |k, v| + next if v.nil? + compliance_extra_config[k] = v + end + end + + source 'security-agent.yaml.erb' + variables( + runtime_security_enabled: node['datadog']['security_agent']['cws']['enabled'], + runtime_security_extra_config: runtime_security_extra_config, + compliance_enabled: node['datadog']['security_agent']['cspm']['enabled'], + compliance_extra_config: compliance_extra_config + ) + + owner 'root' + group 'dd-agent' + mode '640' + + notifies :restart, 'service[datadog-agent-security]', :delayed if security_agent_enabled + + # Security agent is not enabled and the file doesn't exists, don't create it + not_if { !security_agent_enabled && !security_agent_config_file_exists } +end + +# Common configuration +service_provider = Chef::Datadog.service_provider(node) + +service_name = 'datadog-agent-security' + +service 'datadog-agent-security' do + service_name service_name + action [security_agent_start] + provider service_provider unless service_provider.nil? + supports :restart => true, :status => true, :start => true, :stop => true + subscribes :restart, "template[#{security_agent_config_file}]", :delayed if security_agent_enabled +end diff --git a/recipes/system-probe.rb b/recipes/system-probe.rb index 3477480d..f55ee997 100644 --- a/recipes/system-probe.rb +++ b/recipes/system-probe.rb @@ -20,10 +20,11 @@ is_windows = platform_family?('windows') # Set the correct agent startup action +cws_enabled = node['datadog']['security_agent']['cws']['enabled'] sysprobe_enabled = if is_windows node['datadog']['system_probe']['network_enabled'] else - node['datadog']['system_probe']['enabled'] || node['datadog']['system_probe']['network_enabled'] + node['datadog']['system_probe']['enabled'] || node['datadog']['system_probe']['network_enabled'] || cws_enabled end sysprobe_agent_start = sysprobe_enabled ? :start : :stop @@ -47,6 +48,14 @@ end end + runtime_security_extra_config = {} + if node['datadog']['extra_config'] && node['datadog']['extra_config']['security_agent'] && node['datadog']['extra_config']['security_agent']['runtime_security_config'] + node['datadog']['extra_config']['security_agent']['runtime_security_config'].each do |k, v| + next if v.nil? + runtime_security_extra_config[k] = v + end + end + source 'system_probe.yaml.erb' variables( enabled: node['datadog']['system_probe']['enabled'], @@ -54,7 +63,9 @@ debug_port: node['datadog']['system_probe']['debug_port'], bpf_debug: node['datadog']['system_probe']['bpf_debug'], enable_conntrack: node['datadog']['system_probe']['enable_conntrack'], - extra_config: extra_config + system_probe_extra_config: extra_config, + runtime_security_enabled: cws_enabled, + runtime_security_extra_config: runtime_security_extra_config ) if is_windows @@ -70,6 +81,7 @@ notifies :restart, 'service[datadog-agent-sysprobe]', :delayed if sysprobe_enabled # since process-agent collects network info through system-probe, enabling system-probe should also restart process-agent notifies :restart, 'service[datadog-agent]', :delayed if sysprobe_enabled + notifies :restart, 'service[datadog-agent-security]', :delayed if cws_enabled # System probe is not enabled and the file doesn't exists, don't create it not_if { !sysprobe_enabled && !system_probe_config_file_exists } diff --git a/spec/security-agent_spec.rb b/spec/security-agent_spec.rb new file mode 100644 index 00000000..af6b3349 --- /dev/null +++ b/spec/security-agent_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'datadog::security-agent' do + context 'with CWS enabled' do + cached(:solo) do + ChefSpec::SoloRunner.new( + platform: 'ubuntu', + version: '16.04' + ) do |node| + node.name 'chef-nodename' # expected to be used as the hostname in `datadog.yaml` + node.normal['datadog'] = { + 'api_key' => 'somethingnotnil', + 'agent_major_version' => 6, + 'security_agent' => { + 'cws' => { + 'enabled' => true, + } + }, + 'extra_config' => { + 'security_agent' => { + 'runtime_security_config' => { + 'activity_dump' => { + 'enabled' => true, + } + } + } + } + } + end + end + + cached(:chef_run) do + solo.converge(described_recipe) do + solo.resource_collection.insert( + Chef::Resource::Service.new('datadog-agent', solo.run_context)) + end + end + + it 'security-agent.yaml is created' do + expect(chef_run).to create_template('/etc/datadog-agent/security-agent.yaml') + end + + it 'security-agent.yaml contains expected YAML configuration' do + expected_yaml = <<-EOF + compliance_config: + enabled: false + runtime_security_config: + enabled: true + activity_dump: + enabled: true + EOF + + expect(chef_run).to(render_file('/etc/datadog-agent/security-agent.yaml').with_content { |content| + expect(YAML.safe_load(content).to_json).to be_json_eql(YAML.safe_load(expected_yaml).to_json) + }) + end + end +end diff --git a/templates/default/security-agent.yaml.erb b/templates/default/security-agent.yaml.erb new file mode 100644 index 00000000..dafb4379 --- /dev/null +++ b/templates/default/security-agent.yaml.erb @@ -0,0 +1,13 @@ +<% +## Populate system_probe_config ## +security_agent_config = { + runtime_security_config: { + enabled: @runtime_security_enabled, + }.merge(@runtime_security_extra_config), + compliance_config: { + enabled: @compliance_enabled, + }.merge(@compliance_extra_config), +} +-%> + +<%= JSON.parse(security_agent_config.to_json).to_yaml %> \ No newline at end of file diff --git a/templates/default/system_probe.yaml.erb b/templates/default/system_probe.yaml.erb index 801c8363..ce1b72eb 100644 --- a/templates/default/system_probe.yaml.erb +++ b/templates/default/system_probe.yaml.erb @@ -7,9 +7,15 @@ system_probe_config = { debug_port: @debug_port, bpf_debug: @bpf_debug, enable_conntrack: @enable_conntrack, - }.merge(@extra_config), + }.merge(@system_probe_extra_config), } +if @runtime_security_enabled + system_probe_config['runtime_security_config'] = { + enabled: @runtime_security_enabled + }.merge(@runtime_security_extra_config) +end + # We let the Agent default value take effect if user didn't explicitly # specify something else than nil for 'network_enabled' if !node['datadog']['system_probe']['network_enabled'].nil? From 28f19556a353d4b2654b1b1078346d22517a615e Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Wed, 30 Mar 2022 09:31:57 +0000 Subject: [PATCH 2/3] Only create service when supported --- recipes/security-agent.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/recipes/security-agent.rb b/recipes/security-agent.rb index fb6557ce..65448ee6 100644 --- a/recipes/security-agent.rb +++ b/recipes/security-agent.rb @@ -68,10 +68,12 @@ service_name = 'datadog-agent-security' -service 'datadog-agent-security' do - service_name service_name - action [security_agent_start] - provider service_provider unless service_provider.nil? - supports :restart => true, :status => true, :start => true, :stop => true - subscribes :restart, "template[#{security_agent_config_file}]", :delayed if security_agent_enabled -end +if security_agent_enabled + service 'datadog-agent-security' do + service_name service_name + action [security_agent_start] + provider service_provider unless service_provider.nil? + supports :restart => true, :status => true, :start => true, :stop => true + subscribes :restart, "template[#{security_agent_config_file}]", :delayed if security_agent_enabled + end +end \ No newline at end of file From 0f9b8fffb672158e2489002b4837e65cd440f688 Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Wed, 30 Mar 2022 15:40:39 +0000 Subject: [PATCH 3/3] Simplify security-agent recipe --- recipes/security-agent.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/recipes/security-agent.rb b/recipes/security-agent.rb index 65448ee6..3bfcc52b 100644 --- a/recipes/security-agent.rb +++ b/recipes/security-agent.rb @@ -21,7 +21,6 @@ # Set the correct agent startup action security_agent_enabled = !is_windows && node['datadog']['security_agent']['cws']['enabled'] || node['datadog']['security_agent']['cspm']['enabled'] -security_agent_start = security_agent_enabled ? :start : :stop # # Configures security-agent agent @@ -71,9 +70,9 @@ if security_agent_enabled service 'datadog-agent-security' do service_name service_name - action [security_agent_start] + action :start provider service_provider unless service_provider.nil? supports :restart => true, :status => true, :start => true, :stop => true - subscribes :restart, "template[#{security_agent_config_file}]", :delayed if security_agent_enabled + subscribes :restart, "template[#{security_agent_config_file}]", :delayed end end \ No newline at end of file