Skip to content

Add Corgid for new method of Inspector Integration#99

Open
vyaghras wants to merge 1 commit intobottlerocket-os:developfrom
vyaghras:peach_integration
Open

Add Corgid for new method of Inspector Integration#99
vyaghras wants to merge 1 commit intobottlerocket-os:developfrom
vyaghras:peach_integration

Conversation

@vyaghras
Copy link
Copy Markdown
Contributor

@vyaghras vyaghras commented May 4, 2026

Description of changes:
This change adds a rust binary Corgid in the Control container. The binary does following:

  • This converts application inventory on the host to Cyclone Dx sbom
  • Send the Cyslone Dx sbom to the new telemetart API

Testing done:

  • The container builds successfully
make build
DOCKER_BUILDKIT=1 docker build  \
        --tag bottlerocket-control-container-amd64:v0.20.14-b9821b70-dev \
        --build-arg IMAGE_VERSION="v0.20.14" \
        --build-arg SSM_AGENT_VERSION="3.3.3883.0" \
        -f Dockerfile . >&2
[+] Building 0.2s (36/36) FINISHED                                                                                                                                              docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                      0.0s
 => => transferring dockerfile: 6.09kB                                                                                                                                                    0.0s
 => [internal] load metadata for public.ecr.aws/amazonlinux/amazonlinux:2023                                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                                                                         0.0s
 => => transferring context: 2B                                                                                                                                                           0.0s
 => [internal] load build context                                                                                                                                                         0.0s
 => => transferring context: 7.14kB                                                                                                                                                       0.0s
 => [builder  1/11] FROM public.ecr.aws/amazonlinux/amazonlinux:2023                                                                                                                      0.0s
 => CACHED [stage-1  2/21] RUN :     "v0.20.14"     "3.3.3883.0"                                                                                                                          0.0s
 => CACHED [stage-1  3/21] COPY ./hashes/ssm ./hashes                                                                                                                                     0.0s
 => CACHED [stage-1  4/21] COPY ./gpg-keys/amazon-ssm-agent.gpg ./amazon-ssm-agent.gpg                                                                                                    0.0s
 => CACHED [stage-1  5/21] RUN dnf update -y &&     dnf install -y         crypto-policies-scripts         jq         libutempter         screen         shadow-utils         &&     dnf  0.0s
 => CACHED [builder  2/11] RUN dnf install -y     'dnf-command(download)'     cpio     gcc     pkg-config     tar     gzip     cmake     clang     clang-devel                            0.0s
 => CACHED [builder  3/11] RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable                                                         0.0s
 => CACHED [builder  4/11] RUN rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl                                                                                     0.0s
 => CACHED [builder  5/11] RUN curl -L https://musl.cc/x86_64-linux-musl-cross.tgz | tar -xz -C /opt &&     curl -L https://musl.cc/aarch64-linux-musl-cross.tgz | tar -xz -C /opt        0.0s
 => CACHED [builder  6/11] WORKDIR /root/build/util-linux                                                                                                                                 0.0s
 => CACHED [builder  7/11] RUN dnf download util-linux &&     rpm2cpio util-linux-*.rpm | cpio -idmv                                                                                      0.0s
 => CACHED [builder  8/11] WORKDIR /root/build                                                                                                                                            0.0s
 => CACHED [builder  9/11] COPY ./sources/corgid ./corgid/                                                                                                                                0.0s
 => CACHED [builder 10/11] WORKDIR /root/build/corgid                                                                                                                                     0.0s
 => CACHED [builder 11/11] RUN cargo build --release --target x86_64-unknown-linux-musl &&     cargo build --release --target aarch64-unknown-linux-musl                                  0.0s
 => CACHED [stage-1  6/21] COPY --from=builder /root/build/util-linux/usr/bin/lscpu /root/build/util-linux/usr/bin/script                     /opt/util-linux/bin/                        0.0s
 => CACHED [stage-1  7/21] COPY --from=builder /root/build/util-linux/usr/share/licenses/util-linux/COPYING.BSD-4-Clause-UC                     /root/build/util-linux/usr/share/license  0.0s
 => CACHED [stage-1  8/21] RUN ln -s /opt/util-linux/bin/* /usr/bin                                                                                                                       0.0s
 => CACHED [stage-1  9/21] COPY --from=builder /root/build/corgid/target/x86_64-unknown-linux-musl/release/corgid /tmp/corgid-x86_64                                                      0.0s
 => CACHED [stage-1 10/21] COPY --from=builder /root/build/corgid/target/aarch64-unknown-linux-musl/release/corgid /tmp/corgid-aarch64                                                    0.0s
 => CACHED [stage-1 11/21] RUN ARCH=$(uname -m) &&     if [ "$ARCH" = "x86_64" ]; then         cp /tmp/corgid-x86_64 /usr/sbin/corgid;     elif [ "$ARCH" = "aarch64" ]; then         cp  0.0s
 => CACHED [stage-1 12/21] RUN /usr/bin/amazon-ssm-agent -version                                                                                                                         0.0s
 => CACHED [stage-1 13/21] RUN /usr/bin/lscpu                                                                                                                                             0.0s
 => CACHED [stage-1 14/21] RUN /usr/bin/script --version                                                                                                                                  0.0s
 => CACHED [stage-1 15/21] RUN rm -f /etc/motd /etc/issue                                                                                                                                 0.0s
 => CACHED [stage-1 16/21] COPY --chown=root:root motd /etc/                                                                                                                              0.0s
 => CACHED [stage-1 17/21] RUN echo "PS1='[\u@control]\$ '" > "/etc/profile.d/bottlerocket-ps1.sh"                                                                                        0.0s
 => CACHED [stage-1 18/21] COPY ./bashrc /etc/skel/.bashrc                                                                                                                                0.0s
 => CACHED [stage-1 19/21] COPY --chmod=755     ./disable-admin-container     ./enable-admin-container     ./enter-admin-container     /usr/bin/                                          0.0s
 => CACHED [stage-1 20/21] RUN groupadd -g 274 api &&     useradd -m -G users,api ssm-user                                                                                                0.0s
 => CACHED [stage-1 21/21] COPY --chmod=755 start_control_ssm.sh /usr/sbin/                                                                                                               0.0s
 => exporting to image                                                                                                                                                                    0.0s
 => => exporting layers                                                                                                                                                                   0.0s
 => => writing image sha256:94e1f45d302b18068f98655bd199e6d668f1c7ccd1a70eb69715b107963b06d7                                                                                              0.0s
 => => naming to docker.io/library/bottlerocket-control-container-amd64:v0.20.14-b9821b70-dev 
  • The container can create and send the CycloneDx SBOM to Inspector
May 05 18:40:43 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:43Z" level=info msg="Image does not exist, proceeding to pull image from source." ref="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.15"
May 05 18:40:43 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:43Z" level=info msg="setting up ECR client" fips=false region=us-west-2
May 05 18:40:43 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:43Z" level=info msg="pulling private ECR image" ref="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.15" region=us-west-2
May 05 18:40:44 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:44Z" level=info msg="pulled image successfully" img="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.15"
May 05 18:40:44 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:44Z" level=info msg="unpacking image..." img="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.15"
May 05 18:40:45 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:45Z" level=info msg="Container does not exist, proceeding to create it" ctr-id=control
May 05 18:40:45 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:45Z" level=info msg="container task does not exist, proceeding to create it" container-id=control
May 05 18:40:45 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: time="2026-05-05T18:40:45Z" level=info msg="successfully started container task"
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:46 [INFO] Fetching metadata
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: Error occurred fetching the seelog config file path:  open /etc/amazon/ssm/seelog.xml: no such file or directory
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: Initializing new seelog logger
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: New Seelog Logger Creation Complete
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5404 INFO Proxy environment variables:
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:46 [INFO] Reading inventory and converting to SBOM
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:46 [INFO] Fetching IAM credentials
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:46 [INFO] Starting session
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:46 [INFO] POST https://inspector2-telemetry.us-west-2.api.aws/telemetry
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5405 INFO http_proxy:
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5405 INFO no_proxy: localhost,127.0.0.1,ef08fb28bffc0e2df845739485068154.gr7.us-west-2.eks.amazonaws.com,.cluster.local
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5405 INFO https_proxy:
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5405 INFO Checking if agent identity type OnPrem can be assumed
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5405 INFO Checking if agent identity type EC2 can be assumed
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5943 INFO Agent will take identity from EC2
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5957 INFO Telemetry initialized
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5962 INFO [amazon-ssm-agent] amazon-ssm-agent - v3.3.3883.0
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5963 INFO [amazon-ssm-agent] OS: linux, Arch: amd64
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5963 INFO [amazon-ssm-agent] Starting Core Agent
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5963 INFO [amazon-ssm-agent] Registrar detected. Attempting registration
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5963 INFO [Registrar] Starting registrar module
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5975 INFO [EC2Identity] Checking disk for registration info
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5978 INFO [EC2Identity] Registration info found for ec2 instance
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5978 INFO [amazon-ssm-agent] Registration attempted. Resuming core agent startup.
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5979 INFO [CredentialRefresher] credentialRefresher has started
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5979 INFO [CredentialRefresher] Credentials ready
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5982 INFO [CredentialRefresher] Starting credentials refresher loop
May 05 18:40:46 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:46.5982 INFO [CredentialRefresher] Next credential rotation will be in 21.1167457962 minutes
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:47 [INFO] StartSession response: {"startSessionResult":{"collectors":["OS","THIRD_PARTY"],"sessionId":"4b1622ab-ae9e-4edd-b8d0-80a5880bb166","sessionMetadata":{"vulnerabilitySessionMetadata":{"lastKnownScanContentHash":""}}}}
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:47 [INFO] Sending SBOM
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:47 [INFO] POST https://inspector2-telemetry.us-west-2.api.aws/telemetry
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:47 [INFO] Stopping session
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 18:40:47 [INFO] POST https://inspector2-telemetry.us-west-2.api.aws/telemetry
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:47.5988 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] [WorkerProvider] Worker ssm-agent-worker is not running, starting worker process
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:47.5993 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] [WorkerProvider] Worker ssm-agent-worker (pid:41) started
May 05 18:40:47 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[6262]: 2026-05-05 18:40:47.5993 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] Monitor long running worker health every 60 seconds
  • If user-data is set, Corgid do not runs
May 04 21:05:05 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:05Z" level=info msg="Image does not exist, proceeding to pull image from source." ref="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.14"
May 04 21:05:05 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:05Z" level=info msg="setting up ECR client" fips=false region=us-west-2
May 04 21:05:05 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:05Z" level=info msg="pulling private ECR image" ref="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.14" region=us-west-2
May 04 21:05:07 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:07Z" level=info msg="pulled image successfully" img="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.14"
May 04 21:05:07 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:07Z" level=info msg="unpacking image..." img="624299524776.dkr.ecr.us-west-2.amazonaws.com/bottlerocket-control:v0.20.14"
May 04 21:05:10 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:10Z" level=info msg="Container does not exist, proceeding to create it" ctr-id=control
May 04 21:05:10 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:10Z" level=info msg="container task does not exist, proceeding to create it" container-id=control
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: time="2026-05-04T21:05:11Z" level=info msg="successfully started container task"
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: Inspector SBOM telemetry disabled via user-data
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: Error occurred fetching the seelog config file path:  open /etc/amazon/ssm/seelog.xml: no such file or directory
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: Initializing new seelog logger
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: New Seelog Logger Creation Complete
May 04 21:05:11 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO Proxy environment variables:
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO http_proxy:
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO no_proxy: localhost,127.0.0.1,ef08fb28bffc0e2df845739485068154.gr7.us-west-2.eks.amazonaws.com,.cluster.local
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO https_proxy:
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO Checking if agent identity type OnPrem can be assumed
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:11.9696 INFO Checking if agent identity type EC2 can be assumed
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0034 INFO Agent will take identity from EC2
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0048 INFO Telemetry initialized
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0053 INFO [amazon-ssm-agent] amazon-ssm-agent - v3.3.3883.0
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0053 INFO [amazon-ssm-agent] OS: linux, Arch: amd64
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0053 INFO [amazon-ssm-agent] Starting Core Agent
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0053 INFO [amazon-ssm-agent] Registrar detected. Attempting registration
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0053 INFO [Registrar] Starting registrar module
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0069 INFO [EC2Identity] Checking disk for registration info
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0071 INFO [EC2Identity] Registration info found for ec2 instance
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0071 INFO [amazon-ssm-agent] Registration attempted. Resuming core agent startup.
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0071 INFO [CredentialRefresher] credentialRefresher has started
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0071 INFO [CredentialRefresher] Credentials ready
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0074 INFO [CredentialRefresher] Starting credentials refresher loop
May 04 21:05:12 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:12.0074 INFO [CredentialRefresher] Next credential rotation will be in 26.663993470183332 minutes
May 04 21:05:13 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:13.0091 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] [WorkerProvider] Worker ssm-agent-worker is not running, starting worker process
May 04 21:05:13 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:13.0099 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] [WorkerProvider] Worker ssm-agent-worker (pid:34) started
May 04 21:05:13 ip-192-168-47-250.us-west-2.compute.internal host-containers@control[3965]: 2026-05-04 21:05:13.0099 INFO [amazon-ssm-agent] [LongRunningWorkerContainer] Monitor long running worker health every 60 seconds
bash-5.2#
  • The container runs successfully even if the Corgid fails to run
  • Validate that the generated SBOM is in expected format
  • Binary runs fine in Fips mode

Terms of contribution:

By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.

@vyaghras vyaghras marked this pull request as draft May 4, 2026 21:26
@vyaghras vyaghras requested a review from ginglis13 May 4, 2026 21:32
@vyaghras vyaghras force-pushed the peach_integration branch 6 times, most recently from 3006143 to e56efdd Compare May 5, 2026 18:35
@vyaghras vyaghras marked this pull request as ready for review May 5, 2026 18:44
@vyaghras vyaghras force-pushed the peach_integration branch 2 times, most recently from 77b9d11 to eb779b2 Compare May 5, 2026 22:56
@vyaghras vyaghras marked this pull request as draft May 6, 2026 17:54
Comment thread sources/corgid/src/inventory.rs Outdated
let inv: Inventory = serde_json::from_str(&data).context(error::ParseInventorySnafu)?;

// Extract Bottlerocket version from the bottlerocket-metadata package
let br_version = inv
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check if br_version get correct value? bottlerocket-metadata package version will always be 1.0 not actual bottlerocket version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the Version string overall looks correct in the SBOM : Eg: "version": "0.0-1.1757634171.11b13177.br1",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should get this value from parsing /etc/bottlerocket-release

@vyaghras vyaghras force-pushed the peach_integration branch from eb779b2 to 1f15bfb Compare May 6, 2026 20:15
@vyaghras vyaghras marked this pull request as ready for review May 6, 2026 20:42
@vyaghras vyaghras force-pushed the peach_integration branch from 1f15bfb to e9ce725 Compare May 6, 2026 20:50
Comment thread sources/corgid/deny.toml Outdated
Comment thread Dockerfile
Comment thread start_control_ssm.sh Outdated
Comment thread README.md Outdated
@vyaghras vyaghras force-pushed the peach_integration branch from e9ce725 to b0798b9 Compare May 6, 2026 21:50
Comment thread README.md Outdated
Comment thread Makefile Outdated
Comment thread sources/corgid/src/error.rs
const DEFAULT_REGION: &str = "us-east-1";
const USER_DATA_PATH: &str = "/.bottlerocket/host-containers/current/user-data";

fn main() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this resolved? This wasn't addressed.

Comment thread sources/corgid/src/main.rs Outdated
Comment on lines +102 to +115
fn inspector_enabled() -> bool {
let Ok(data) = std::fs::read_to_string(USER_DATA_PATH) else {
return true;
};
let Ok(json) = serde_json::from_str::<serde_json::Value>(&data) else {
return true;
};
let val = json.get("inspector").and_then(|v| v.get("upload-sbom"));
match val {
Some(v) if v == false => false,
Some(v) if v == "false" => false,
_ => true,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will prefer this to be a struct CorgidConfig.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this was resolved, and no reason was provided on why and the change wasn't implemented.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what I would like is:

struct CorgidConfig {
    ...fields
}

impl CorgiConfig {
    fn from_path() -> {}

     fn inspector_enabled(&self) -> {}
}

Comment thread sources/corgid/src/main.rs Outdated
publisher: "Amazon Web Services, Inc. (AWS)".into(),
description: "Bottlerocket OS".into(),
licenses: None,
properties: Some(properties),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will properties be none? The only place where it is set is here, and properties always is set.

Copy link
Copy Markdown
Contributor Author

@vyaghras vyaghras May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packages don't have properties, the OS component does, One struct serves both. Hnece we need option

Comment thread Makefile
Comment thread sources/corgid/src/inspector.rs
Comment thread sources/corgid/src/inventory.rs Outdated
Comment thread sources/corgid/src/inventory.rs Outdated
let inv: Inventory = serde_json::from_str(&data).context(error::ParseInventorySnafu)?;

// Extract Bottlerocket version from the bottlerocket-metadata package
let br_version = inv
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should get this value from parsing /etc/bottlerocket-release

corgid reads the Bottlerocket application inventory from the host,
converts it to a CycloneDX SBOM, and uploads it to the Amazon
Inspector API for vulnerability scanning.

- Statically linked with musl via Bottlerocket SDK
- Runs automatically at container startup in the background
- Can be disabled via user-data: {"inspector": {"upload-sbom": false}}
- Includes cargo-deny license policy and bottlerocket-license-scan

Signed-off-by: Shikha Vyaghra <vyaghras@amazon.com>
@vyaghras vyaghras force-pushed the peach_integration branch from b0798b9 to 81d7f9f Compare May 7, 2026 20:56
@jpculp jpculp self-requested a review May 7, 2026 21:13
@KCSesh
Copy link
Copy Markdown
Contributor

KCSesh commented May 7, 2026

Please update your commit to also be signed by you 👍

Comment on lines +88 to +89
/// Unique identifier for this SBOM instance (UUID v4).
pub serial_number: String,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the Uuid type:

Suggested change
/// Unique identifier for this SBOM instance (UUID v4).
pub serial_number: String,
/// Unique identifier for this SBOM instance (UUID v4).
pub serial_number: Uuid,

coupled with the serde feature in the uuid crate:

  uuid = { version = "1", features = ["serde"] }

Comment on lines +156 to +157
// Get Bottlerocket version from the API
let br_version = std::process::Command::new("apiclient")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to just read the file /etc/bottlerocket-release:

let content = fs::read_to_string("/etc/bottlerocket-release")?;

for line in content.lines() {
    if let Some(value) = line.strip_prefix("VERSION_ID=") {
        return Ok(value.to_string());
    }
}

return Ok(());
}

info!("Fetching metadata");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the logging doesn't need to be so verbose, as written there's an info log for every intermediate step. You could make these debug!() and consolidate to perhaps just 2 info logs, like

info!("Starting Inspector SBOM upload process");
...
info!("Inspector SBOM upload completed successfully");

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have an extra space here.

const DEFAULT_REGION: &str = "us-east-1";
const USER_DATA_PATH: &str = "/.bottlerocket/host-containers/current/user-data";

fn main() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this resolved? This wasn't addressed.

#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum Error {
#[snafu(display("Failed to read application inventory file: {source}"))]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you use the report API from snafu, you should be able to remove source from your messages

Comment on lines +102 to +115
fn inspector_enabled() -> bool {
let Ok(data) = std::fs::read_to_string(USER_DATA_PATH) else {
return true;
};
let Ok(json) = serde_json::from_str::<serde_json::Value>(&data) else {
return true;
};
let val = json.get("inspector").and_then(|v| v.get("upload-sbom"));
match val {
Some(v) if v == false => false,
Some(v) if v == "false" => false,
_ => true,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this was resolved, and no reason was provided on why and the change wasn't implemented.

Comment on lines +102 to +115
fn inspector_enabled() -> bool {
let Ok(data) = std::fs::read_to_string(USER_DATA_PATH) else {
return true;
};
let Ok(json) = serde_json::from_str::<serde_json::Value>(&data) else {
return true;
};
let val = json.get("inspector").and_then(|v| v.get("upload-sbom"));
match val {
Some(v) if v == false => false,
Some(v) if v == "false" => false,
_ => true,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what I would like is:

struct CorgidConfig {
    ...fields
}

impl CorgiConfig {
    fn from_path() -> {}

     fn inspector_enabled(&self) -> {}
}

Comment on lines +137 to +138
/// Properties use the `amazon:inspector:sbom_generator:metadata:` prefix
/// to conform to Amazon Inspector's expected schema for host/IMDS data.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary comment here, if anything you should add it to the Property struct.

Comment on lines +235 to +253
components.extend(inv.content.iter().enumerate().map(|(i, p)| Component {
bom_ref: format!("comp-{}", i),
typ: "library",
name: p.name.clone(),
version: format!("{}-{}", p.version, p.release),
purl: Some(format!(
"pkg:rpm/bottlerocket/{}@{}-{}?arch={}&epoch={}&distro={}",
p.name,
p.version,
p.release,
p.architecture,
if p.epoch.is_empty() { "0" } else { &p.epoch },
br_version,
)),
publisher: p.publisher.clone(),
description: p.summary.clone(),
licenses: None,
properties: None,
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More opportunities to be ideomatic:

components.extend(inventory.content.map(|package| { package.into() }));

// You can do this if you implement `From<Package> for Component`. 

/// Property names follow Amazon Inspector's naming convention:
/// `amazon:inspector:sbom_generator:metadata:{category}:{field}`
/// where category is either `host` (uname data) or `imds` (EC2 instance metadata).
pub fn read_and_convert(metadata: &HostMetadata) -> Result<String> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the API I'm thinking is:

let sbom = Sbom::from(metadata, inventory)?;
sbom.to_string()

Comment on lines +30 to +35
const SERVICE: &str = "inspector2-telemetry";
/// SBOM chunks are limited to 390KB to stay within API payload limits.
const CHUNK_SIZE: usize = 390 * 1024;
/// Retry transient failures up to 3 times with exponential backoff.
const MAX_RETRIES: u32 = 3;
const VERSION: &str = "0.1.0";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consts on top.

unreachable!()
}

/// Starts a new vulnerability scan session with Inspector.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API seems like a great candidate to implement the type state pattern:

let client = InspectorClient { ... };
let open_client = client.start_session();
// Then you can only call send_sbom when the client is in the "open state"
open_client.send();

// Then you do, which will literally forbid you from trying to re-use the open client since you will be transferring ownership of client to this function
open_client.close_session();

// Example
struct InspectorClient {
}

impl InspectorClient {
    /// Takes ownership so no more clients can be created out of the original client
    fn start_session(self) -> OpenClient {}
}

impl OpenClient {
    // You can only send SBOMs in an open client
    fn send_sbom(&self) ->
    // You take ownership of OpenClient here, and make sure the session is gone for good
    fn close_session(self) -> Result<()>
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants