Skip to content

Commit 05d2265

Browse files
[DSEC-118] Build and ship Rust shared-library checks in agent packages (#53498)
### What does this PR do? - Add [`shared_checks_manifest.yaml`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/pkg/collector/sharedlibrary/rustchecks/shared_checks_manifest.yaml) and a [`rust-shared-checks.build`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/tasks/rust_shared_checks.py) invoke task to build manifest-selected `cdylib` checks and stage them into `checks.d` as `libdatadog-agent-<id>.so` with `0500` permissions (Linux only). - Wire the task into the omnibus agent build ([`datadog-agent.rb`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/omnibus/config/software/datadog-agent.rb#L108-L112)); it runs for Linux only (macOS and Windows excluded). - Move staged shared libs into the Linux package config during [`datadog-agent-finalize.rb`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/omnibus/config/software/datadog-agent-finalize.rb#L99-L103) and enforce owner-only (`0500`) perms. - Re-apply `0500` on the shared-library checks in the [`agent`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/Dockerfiles/agent/Dockerfile#L214) and [`agent-ddot`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/Dockerfiles/agent-ddot/Dockerfile#L21) Dockerfiles after the recursive config `chmod`. - Fix the Rust shared-check FFI macro to resolve types via `$crate::` instead of `core::`. ### Motivation Rust shared-library checks are not built or shipped in agent packages today. This wires the existing rustchecks workspace into omnibus/Docker so checks listed in the manifest can be included in Linux release artifacts. ### Describe how you validated your changes (with example check [enabled](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/pkg/collector/sharedlibrary/rustchecks/shared_checks_manifest.yaml#L4)) #### CI image (omnibus build) Built from CI job [1848750354](https://gitlab.ddbuild.io/DataDog/datadog-agent/-/jobs/1848750354) and ran the agent with: ```yaml services: agent: image: registry.ddbuild.io/ci/datadog-agent/agent:v124269439-678e1467-7-amd64 container_name: new-agent-with-rust-checks environment: DD_API_KEY: *** DD_HOSTNAME: aimene-test-ci-image DD_SITE: datad0g.com DD_APM_ENABLED: "true" DD_APM_NON_LOCAL_TRAFFIC: "true" DD_LOGS_ENABLED: "true" DD_DOGSTATSD_NON_LOCAL_TRAFFIC: "true" DD_LOG_LEVEL: "INFO" DD_SHARED_LIBRARY_CHECK_ENABLED: "true" DD_SHARED_LIBRARY_CHECK_LIBRARY_FOLDER_PATH: "/etc/datadog-agent/checks.d" volumes: - /var/run/docker.sock:/var/run/docker.sock - ./conf.d:/etc/datadog-agent/conf.d:ro ``` `conf.d` contains `example.d/conf.yaml`: ```yaml init_config: instances: - min_collection_interval: 15 ``` The example shared-library check ran successfully and events were received in [Event Explorer (`host:aimene-test-ci-image`)](https://dd.datad0g.com/event/explorer?query=host%3Aaimene-test-ci-image&agg_m=count&agg_m_source=base&agg_t=count&clustering_pattern_field_path=message&cols=&messageDisplay=expanded-lg&options=&refresh_mode=paused&sort=DESC&from_ts=1783634400000&to_ts=1783687020000&live=false). #### Hacky dev image Also tested locally with: ```bash dda env dev run -- dda inv agent.hacky-dev-image-build --target-image agent-with-rust-checks ``` Same check config and expected results (example shared-library check runs and [events are received](https://dd.datad0g.com/event/explorer?query=host%3Aaimene-test-hacky-build&agg_m=count&agg_m_source=base&agg_t=count&clustering_pattern_field_path=message&cols=&messageDisplay=expanded-lg&options=&refresh_mode=paused&sort=DESC&from_ts=1783634400000&to_ts=1783687020000&live=false)). ### Actions - [x] Check selection is driven by [`include_in_build`](https://github.com/DataDog/datadog-agent/blob/aimene.belfodil/dsec/build-rust-checks/pkg/collector/sharedlibrary/rustchecks/shared_checks_manifest.yaml) in the manifest (default `false`). **Before merge:** set `example` to `include_in_build: false` since **the example check should not ship**. ### Question/Response #### Why Only Linux? To keep the scope and risk minimal, this PR targets the Linux platform only. #### Why shipped shared libs need chmod 0500 (according to existing code)? Shared-library loader rejects libs (rust checks) unless the owner is trusted and group/others have no access ([`CheckOwnerAndPermissionsAreRestricted`](https://github.com/DataDog/datadog-agent/blob/main/pkg/util/filesystem/permission_check.go#L14-L22) → [`CheckRights`](https://github.com/DataDog/datadog-agent/blob/main/pkg/util/filesystem/rights_nix.go#L17-L34), called from [`SharedLibraryLoader.Open`](https://github.com/DataDog/datadog-agent/blob/main/pkg/collector/sharedlibrary/ffi/library_loader.go#L111)). Packaging steps that `chmod` config broadly would otherwise leave libs group/world-readable and unloadable. Co-authored-by: aimene.belfodil <aimene.belfodil@datadoghq.com>
1 parent 06ac9ad commit 05d2265

11 files changed

Lines changed: 224 additions & 9 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@
849849
/tasks/static_quality_gates/ @DataDog/agent-build
850850
/tasks/files_inventory.py @DataDog/agent-build
851851
/tasks/rtloader.py @DataDog/agent-runtimes
852+
/tasks/rust_shared_checks.py @DataDog/agent-build @DataDog/agent-runtimes
852853
/tasks/secret_generic_connector.py @DataDog/agent-configuration
853854
/tasks/security_agent.py @DataDog/agent-security
854855
/tasks/sbomgen.py @DataDog/agent-security

Dockerfiles/agent-ddot/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ RUN chmod 755 -R /opt/entrypoints \
1616
&& rm -rf /var/run && mkdir -p /var/run/s6 && mkdir -p /var/run/datadog && mkdir -p /opt/datadog-agent/run \
1717
&& chown -R dd-agent:root /etc/datadog-agent/ /etc/s6/ /var/run/s6/ /var/log/datadog/ /var/run/datadog/ /opt/datadog-agent/run \
1818
&& chmod g+r,g+w,g+X -R /etc/datadog-agent/ /etc/s6/ /var/run/s6/ /var/log/datadog/ /var/run/datadog/ /opt/datadog-agent/run
19+
20+
# Set owner-only (0500) perms on built-in Rust shared-library checks (no-op when none are present).
21+
RUN find /etc/datadog-agent/checks.d -name 'libdatadog-agent-*.so' -exec chmod 0500 {} \;

Dockerfiles/agent/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ RUN adduser --system --no-create-home --disabled-password --ingroup root dd-agen
210210
# copy it on OpenShift where containers run with a random UID in the root group.
211211
&& chmod u=r,g=r,o= /etc/datadog-agent/private-action-runner/*
212212

213+
# Set owner-only (0500) perms on built-in Rust shared-library checks (no-op when none are present).
214+
RUN find /etc/datadog-agent/checks.d -name 'libdatadog-agent-*.so' -exec chmod 0500 {} \;
215+
213216
# Check that the UID of dd-agent is still 100.
214217
#
215218
# The exact numeric value of the UID of the dd-agent user shouldn’t matter.

omnibus/config/software/datadog-agent-finalize.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@
9494
mkdir "#{output_config_dir}/etc/datadog-agent/checks.d"
9595
mkdir "/var/log/datadog"
9696

97+
# Move the built-in shared-library checks (built under install_dir) into the
98+
# package's checks.d and set owner-only permissions on them.
99+
Dir.glob("#{install_dir}/etc/datadog-agent/checks.d/libdatadog-agent-*.so").each do |lib|
100+
dest = "#{output_config_dir}/etc/datadog-agent/checks.d/#{File.basename(lib)}"
101+
move lib, dest, :force => true
102+
command "chmod 0500 #{dest}"
103+
end
104+
97105
# Process manager config directory (read-only, under install dir)
98106
mkdir "#{install_dir}/processes.d"
99107

omnibus/config/software/datadog-agent.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@
109109
else
110110
conf_dir = "#{install_dir}/etc/datadog-agent"
111111
end
112+
113+
# Stage Rust shared-library checks (Linux only).
114+
if linux_target?
115+
command "dda inv -- -e rust-shared-checks.build --checks-d-dir=\"#{conf_dir}/checks.d\"",
116+
env: env,
117+
:live_stream => Omnibus.logger.live_stream(:info)
118+
end
112119
# TODO(agent-build): sort out the use of bin/agen/dist/conf.d
113120
# dda inv agent.build leaves many files in bin/agen/dist/conf.d
114121
# Now we place them into the pacakge via the //packages/agent/product:post_build_install

pkg/collector/sharedlibrary/rustchecks/core/src/ffi.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ macro_rules! generate_ffi {
88
check_id_cstr: *const std::ffi::c_char,
99
init_config_cstr: *const std::ffi::c_char,
1010
instance_config_cstr: *const std::ffi::c_char,
11-
aggregator_ptr: *const core::Aggregator,
11+
aggregator_ptr: *const $crate::Aggregator,
1212
error_handler: *mut *mut std::ffi::c_char,
1313
) {
1414
if let Err(e) = create_and_run_check(
@@ -30,22 +30,22 @@ macro_rules! generate_ffi {
3030
check_id_cstr: *const std::ffi::c_char,
3131
init_config_cstr: *const std::ffi::c_char,
3232
instance_config_cstr: *const std::ffi::c_char,
33-
aggregator_ptr: *const core::Aggregator,
33+
aggregator_ptr: *const $crate::Aggregator,
3434
) -> Result<(), Box<dyn std::error::Error>> {
3535
// convert C args to Rust structs
36-
let check_id = core::to_rust_string(check_id_cstr)?;
36+
let check_id = $crate::to_rust_string(check_id_cstr)?;
3737

38-
let init_config_str = core::to_rust_string(init_config_cstr)?;
39-
let init_config = core::Config::from_str(&init_config_str)?;
38+
let init_config_str = $crate::to_rust_string(init_config_cstr)?;
39+
let init_config = $crate::Config::from_str(&init_config_str)?;
4040

41-
let instance_config_str = core::to_rust_string(instance_config_cstr)?;
42-
let instance_config = core::Config::from_str(&instance_config_str)?;
41+
let instance_config_str = $crate::to_rust_string(instance_config_cstr)?;
42+
let instance_config = $crate::Config::from_str(&instance_config_str)?;
4343

44-
let aggregator = core::Aggregator::from_ptr(aggregator_ptr);
44+
let aggregator = $crate::Aggregator::from_ptr(aggregator_ptr);
4545

4646
// create the check instance
4747
let agent_check =
48-
core::AgentCheck::new(check_id, init_config, instance_config, aggregator);
48+
$crate::AgentCheck::new(check_id, init_config, instance_config, aggregator);
4949

5050
// run the custom implementation
5151
$check_function(&agent_check)?;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
checks:
2+
- id: example
3+
crate: example
4+
include_in_build: false
5+
platforms:
6+
- linux
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
enhancements:
3+
- |
4+
The Linux Agent packages can now ship built-in Rust-based checks as shared
5+
libraries under ``/etc/datadog-agent/checks.d``, restricted to owner-only access.
6+
No such check is shipped for now; this only adds the packaging support.

tasks/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
release,
7676
renovate,
7777
rtloader,
78+
rust_shared_checks,
7879
sbomgen,
7980
schema,
8081
secret_generic_connector,
@@ -252,6 +253,7 @@
252253
ns.add_collection(release)
253254
ns.add_collection(renovate)
254255
ns.add_collection(rtloader)
256+
ns.add_collection(rust_shared_checks)
255257
ns.add_collection(system_probe)
256258
ns.add_collection(process_agent)
257259
ns.add_collection(privateactionrunner)

tasks/agent.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,19 @@ def hacky_dev_image_build(
448448
f'perl -0777 -pe \'s|{extracted_python_dir}(/opt/datadog-agent/embedded/lib/python\\d+\\.\\d+/../..)|substr $1."\\0"x length$&,0,length$&|e or die "pattern not found"\' -i dev/lib/libdatadog-agent-three.so'
449449
)
450450

451+
copy_checks_d = ""
452+
copy_checks_d_final = ""
453+
if sys.platform.startswith("linux"):
454+
from tasks.rust_shared_checks import build as rust_shared_checks_build
455+
456+
checks_d_staging = "bin/agent/dist/checks.d"
457+
rust_shared_checks_build(ctx, checks_d_dir=checks_d_staging)
458+
if os.path.isdir(checks_d_staging) and any(
459+
f.startswith("libdatadog-agent-") for f in os.listdir(checks_d_staging)
460+
):
461+
copy_checks_d = f"COPY {checks_d_staging} /etc/datadog-agent/checks.d\n"
462+
copy_checks_d_final = "COPY --from=bin /etc/datadog-agent/checks.d /etc/datadog-agent/checks.d\n"
463+
451464
copy_extra_agents = ""
452465
if security_agent:
453466
from tasks.security_agent import build as security_agent_build
@@ -527,6 +540,7 @@ def hacky_dev_image_build(
527540
528541
COPY bin/agent/agent /opt/datadog-agent/bin/agent/agent
529542
COPY bin/agent/dist/conf.d /etc/datadog-agent/conf.d
543+
{copy_checks_d}
530544
COPY dev/lib/libdatadog-agent-rtloader.so.0.1.0 /opt/datadog-agent/embedded/lib/libdatadog-agent-rtloader.so.0.1.0
531545
COPY dev/lib/libdatadog-agent-three.so /opt/datadog-agent/embedded/lib/libdatadog-agent-three.so
532546
{copy_ebpf_assets}
@@ -564,6 +578,7 @@ def hacky_dev_image_build(
564578
COPY --from=bin /opt/datadog-agent/embedded/lib/libdatadog-agent-rtloader.so.0.1.0 /opt/datadog-agent/embedded/lib/libdatadog-agent-rtloader.so.0.1.0
565579
COPY --from=bin /opt/datadog-agent/embedded/lib/libdatadog-agent-three.so /opt/datadog-agent/embedded/lib/libdatadog-agent-three.so
566580
COPY --from=bin /etc/datadog-agent/conf.d /etc/datadog-agent/conf.d
581+
{copy_checks_d_final}
567582
{copy_extra_agents}
568583
{copy_ebpf_assets_final}
569584
RUN agent completion bash > /usr/share/bash-completion/completions/agent

0 commit comments

Comments
 (0)