From 9c2c583244d2bd2c5d8d946fb044f8756b23fb7f Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 12 Feb 2024 11:08:08 +0100 Subject: [PATCH] add support for CWS on Windows (#907) * run security agent specs on windows * add security agent test * add support for security agent service on windows * small code cleanup * add rspec test for windows on CWS --- kitchen.yml | 13 +++++ recipes/dd-agent.rb | 2 +- recipes/security-agent.rb | 36 ++++++++---- recipes/system-probe.rb | 10 ++-- spec/security-agent_spec.rb | 55 ++++++++++++++++++ spec/system-probe_spec.rb | 111 ++++++++++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 19 deletions(-) diff --git a/kitchen.yml b/kitchen.yml index 4e0ef1d1..2f8d7ddb 100644 --- a/kitchen.yml +++ b/kitchen.yml @@ -107,6 +107,19 @@ suites: bpf_debug: true enable_conntrack: true +- name: security-agent + run_list: + - recipe[datadog::dd-agent] + attributes: + datadog: &DATADOG + aptrepo: 'http://apt.datad0g.com' + aptrepo_dist: 'beta' + api_key: somenonnullapikeythats32charlong + application_key: alsonotnil + security_agent: + cws: + enabled: true + - name: dd-handler run_list: - recipe[datadog::dd-handler] diff --git a/recipes/dd-agent.rb b/recipes/dd-agent.rb index 188d227b..3079d33e 100644 --- a/recipes/dd-agent.rb +++ b/recipes/dd-agent.rb @@ -167,7 +167,7 @@ def template_vars 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 +include_recipe '::security-agent' # 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 index 0e58ff05..983e731c 100644 --- a/recipes/security-agent.rb +++ b/recipes/security-agent.rb @@ -20,11 +20,17 @@ 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_enabled = node['datadog']['security_agent']['cws']['enabled'] || (!is_windows && node['datadog']['security_agent']['cspm']['enabled']) +security_agent_start = security_agent_enabled && node['datadog']['agent_start'] && node['datadog']['agent_enable'] ? :start : :stop # # Configures security-agent agent -security_agent_config_file = '/etc/datadog-agent/security-agent.yaml' +security_agent_config_file = + if is_windows + 'C:/ProgramData/Datadog/security-agent.yaml' + else + '/etc/datadog-agent/security-agent.yaml' + end security_agent_config_file_exists = ::File.exist?(security_agent_config_file) template security_agent_config_file do @@ -52,9 +58,11 @@ compliance_extra_config: compliance_extra_config ) - owner 'root' - group 'dd-agent' - mode '640' + unless is_windows + owner 'root' + group 'dd-agent' + mode '640' + end notifies :restart, 'service[datadog-agent-security]', :delayed if security_agent_enabled @@ -65,14 +73,18 @@ # Common configuration service_provider = Chef::Datadog.service_provider(node) -service_name = 'datadog-agent-security' +service_name = is_windows ? 'datadog-security-agent' : 'datadog-agent-security' -if security_agent_enabled - service 'datadog-agent-security' do - service_name service_name - action :start - provider service_provider unless service_provider.nil? +service 'datadog-agent-security' do + service_name service_name + action [security_agent_start] + provider service_provider unless service_provider.nil? + if is_windows + supports :restart => true, :start => true, :stop => true + restart_command "powershell restart-service #{service_name} -Force" + stop_command "powershell stop-service #{service_name} -Force" + else supports :restart => true, :status => true, :start => true, :stop => true - subscribes :restart, "template[#{security_agent_config_file}]", :delayed end + subscribes :restart, "template[#{security_agent_config_file}]", :delayed if security_agent_enabled end \ No newline at end of file diff --git a/recipes/system-probe.rb b/recipes/system-probe.rb index 91aa2522..fafe4792 100644 --- a/recipes/system-probe.rb +++ b/recipes/system-probe.rb @@ -20,12 +20,11 @@ is_windows = platform_family?('windows') # Set the correct agent startup action +npm_enabled = node['datadog']['system_probe']['network_enabled'] +usm_enabled = node['datadog']['system_probe']['service_monitoring_enabled'] 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']['service_monitoring_enabled'] || cws_enabled - end +sysprobe_enabled = node['datadog']['system_probe']['enabled'] || npm_enabled || usm_enabled || cws_enabled + sysprobe_agent_start = sysprobe_enabled && node['datadog']['agent_start'] && node['datadog']['agent_enable'] ? :start : :stop # @@ -99,6 +98,5 @@ else supports :restart => true, :status => true, :start => true, :stop => true end - supports :restart => true, :status => true, :start => true, :stop => true subscribes :restart, "template[#{system_probe_config_file}]", :delayed if sysprobe_enabled end diff --git a/spec/security-agent_spec.rb b/spec/security-agent_spec.rb index 5bc3199f..e7d025bf 100644 --- a/spec/security-agent_spec.rb +++ b/spec/security-agent_spec.rb @@ -69,4 +69,59 @@ }) end end + + context 'with CWS enabled on Windows' do + cached(:solo) do + ChefSpec::SoloRunner.new( + platform: 'windows', + version: '2012R2' + ) 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('C:/ProgramData/Datadog/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('C:/ProgramData/Datadog/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/spec/system-probe_spec.rb b/spec/system-probe_spec.rb index e393967c..7bfb8f95 100644 --- a/spec/system-probe_spec.rb +++ b/spec/system-probe_spec.rb @@ -240,4 +240,115 @@ }) end end + + 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)) + solo.resource_collection.insert( + Chef::Resource::Service.new('datadog-agent-security', solo.run_context)) + end + end + + it 'system-probe.yaml is created' do + expect(chef_run).to create_template('/etc/datadog-agent/system-probe.yaml') + end + + it 'system-probe.yaml contains expected YAML configuration' do + expected_yaml = <<-EOF + runtime_security_config: + enabled: true + activity_dump: + enabled: true + system_probe_config: + enabled: false + bpf_debug: false + debug_port: 0 + enable_conntrack: false + sysprobe_socket: '/opt/datadog-agent/run/sysprobe.sock' + EOF + + expect(chef_run).to(render_file('/etc/datadog-agent/system-probe.yaml').with_content { |content| + expect(YAML.safe_load(content).to_json).to be_json_eql(YAML.safe_load(expected_yaml).to_json) + }) + end + end + + context 'with CWS enabled on Windows' do + cached(:solo) do + ChefSpec::SoloRunner.new( + platform: 'windows', + version: '2012R2' + ) 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, + } + } + } + end + end + + cached(:chef_run) do + solo.converge(described_recipe) do + solo.resource_collection.insert( + Chef::Resource::Service.new('datadog-agent', solo.run_context)) + solo.resource_collection.insert( + Chef::Resource::Service.new('datadog-agent-security', solo.run_context)) + end + end + + it 'system-probe.yaml is created' do + expect(chef_run).to create_template('C:/ProgramData/Datadog/system-probe.yaml') + end + + it 'system-probe.yaml contains expected YAML configuration' do + expected_yaml = <<-EOF + runtime_security_config: + enabled: true + system_probe_config: + enabled: false + bpf_debug: false + debug_port: 0 + enable_conntrack: false + sysprobe_socket: 'localhost:3333' + EOF + + expect(chef_run).to(render_file('C:/ProgramData/Datadog/system-probe.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