-
Notifications
You must be signed in to change notification settings - Fork 3
docs: oci #2178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
danielorihuela
wants to merge
16
commits into
freeze-develop
Choose a base branch
from
docs/oci
base: freeze-develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
docs: oci #2178
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
ef0d47a
docs: oci package structure
danielorihuela 8b0b005
docs: signature verification
danielorihuela 8751b79
docs: garbage collection
danielorihuela 3036423
docs: first round of comments
danielorihuela 82a2f02
docs: second round of comments
danielorihuela f74ae35
docs: third round of comments
danielorihuela f2a429e
docs: rename oci_repository file
danielorihuela 73cd734
docs: re-structure package manager docs
danielorihuela 72cca27
docs: update overview
danielorihuela c68d1a1
docs: manifest and index
danielorihuela ff954f9
docs: garbage collector on restart
danielorihuela c28ca1b
docs: comments
danielorihuela cfb442c
docs: remove annotations
danielorihuela 1286464
docs: artifact type
danielorihuela d280d7a
docs: compatibility
danielorihuela 16f1cad
docs: multiarch support
danielorihuela File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,236 @@ | ||
| # Package Manager | ||
|
|
||
| Agent Control (AC) manages agent packages (and in the future agent types) distributed as OCI ([Open Container Initiative](https://opencontainers.org/)) artifacts. | ||
| The package manager handles download, signature verification, extraction, installation and garbage collection of packages from OCI registries. | ||
|
|
||
| ## Package structure (in the OCI repository) | ||
|
|
||
| The packaged agent must comply with the [OCI image spec](https://github.com/opencontainers/image-spec). The entrypoint can either | ||
| be a manifest JSON file or an index JSON file. | ||
|
|
||
| A [manifest](https://github.com/opencontainers/image-spec/blob/main/manifest.md#oci-image-manifest-specification) file includes the package data and metadata, which is sufficient for an agent that supports a single environment (i.e. OS + architecture). | ||
| In that file, Agent Control expects to find some specific keys and values. | ||
|
|
||
| * `layers/mediaType` must take one of the following values: | ||
|
|
||
| - `application/vnd.newrelic.agent.content.v1.zip` | ||
| - `application/vnd.newrelic.agent.content.v1.tar+gzip` | ||
| - `application/vnd.newrelic.agent-type.content.v1.tar+gzip` | ||
|
|
||
| * `annotations` must contain | ||
| - `com.newrelic.artifact.type` with value `package` or `agent-type` | ||
|
|
||
| Example: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "artifactType": "application/vnd.newrelic.agent.v1", | ||
danielorihuela marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "config": { | ||
| "mediaType": "application/vnd.oci.image.config.v1+json", | ||
| "digest": "sha256:7758599fc4d06bd93a65bf28bc98fbff6c559a9a56be1ec3d75ff6aa8a8cfe6e", | ||
| "size": 39 | ||
| }, | ||
| "layers": [ | ||
| { | ||
| "mediaType": "application/vnd.newrelic.agent.content.v1.zip", | ||
danielorihuela marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "digest": "sha256:2e2e87f3a9403e735bee76c166b7139be36c1a76079f786e21ab2ce138cd9a1a", | ||
| "size": 21678636, | ||
| "annotations": { | ||
| "com.newrelic.artifact.type": "package", | ||
| "org.opencontainers.image.title": "newrelic-infra-amd64.zip", | ||
| "org.opencontainers.image.version": "1.71.3" | ||
| } | ||
| } | ||
| ], | ||
| "annotations": { | ||
| "org.opencontainers.image.created": "2026-01-23T08:07:06Z" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Then we have the [index](https://github.com/opencontainers/image-spec/blob/main/image-index.md#oci-image-index-specification) file. This includes a list of manifest files, and it's handy to support multiple environments. | ||
|
|
||
| > [!NOTE] | ||
| > Agent Control uses an [OCI client that supports every architecture and os in the spec](https://docs.rs/oci-client/latest/oci_client/manifest/struct.Platform.html). | ||
sigilioso marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Example: | ||
|
|
||
| ```json | ||
| { | ||
| "schemaVersion": 2, | ||
| "mediaType": "application/vnd.oci.image.index.v1+json", | ||
| "manifests": [ | ||
| { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:82677ba32d1276debe264d14ec5f7b1c61e2a9acbc8c6a6dff779d7133ec8487", | ||
| "size": 617, | ||
| "platform": { | ||
| "architecture": "amd64", | ||
| "os": "linux" | ||
| }, | ||
| "artifactType": "application/vnd.newrelic.agent.v1" | ||
| }, | ||
| { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:5a16021a5101f7ae0583cddae44ea715ad2cfd618b61b8982de1b847958260da", | ||
| "size": 617, | ||
| "platform": { | ||
| "architecture": "arm64", | ||
| "os": "linux" | ||
| }, | ||
| "artifactType": "application/vnd.newrelic.agent.v1" | ||
| }, | ||
| { | ||
| "mediaType": "application/vnd.oci.image.manifest.v1+json", | ||
| "digest": "sha256:13e6d06647bbaf4f44d4c29bb57e1078c9919da92e2aee3443c122c24b86d3cb", | ||
| "size": 502, | ||
| "platform": { | ||
| "architecture": "amd64", | ||
| "os": "windows" | ||
| }, | ||
| "artifactType": "application/vnd.newrelic.agent.v1" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ## Package references | ||
|
|
||
| Package references are constructed from the data configured in the [AgentType `packages` section](./INTEGRATING_AGENTS.md#packages). | ||
|
|
||
| ## Package Installation Process | ||
|
|
||
| When an agent needs to install or update a package, the package manager leverages the following paths: | ||
| ``` | ||
| temp_package_path: <base>/packages/<agent-id>/__temp_packages/<package-id>/<sanitized-ref> | ||
| final_path: <base>/packages/<agent-id>/stored_packages/<package-id>/<sanitized-ref> | ||
| ``` | ||
|
|
||
| The `final_path` location is where the extracted package will reside after installation and can be referenced | ||
| by the agent through the variable `${nr-sub:packages.infra-agent.dir}`. | ||
|
|
||
| **Steps**: | ||
| 1. Create temporary download directory | ||
| 2. Download artifact (expects exactly 1 layer/file), if the file was already downloaded, skip download | ||
| 3. Create final installation directory | ||
| 4. Extract archive based on `PackageType` (tar.gz or zip) derived from the MIME type | ||
| 5. Delete temporary directory (always, even on failure) | ||
|
|
||
| The whole operation blocks the sub-agent thread until it terminates. | ||
| Notice that the old sub-agent (and therefore the binary) is stopped before the new one is downloaded and executed. | ||
| In the next iterations, we will have a non-blocking implementation to avoid the sub-agent being blocked by this operation. | ||
|
|
||
| There is no installation step or script execution, just extraction. We expect to support installation scripts in the future. | ||
| TODO | ||
danielorihuela marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Signature Verification | ||
|
|
||
| AC supports signature verifications. This assures users that a given agent was uploaded by the expected author and it hasn't been tampered. | ||
|
|
||
| The signature in the OCI repository follows the [Simple Signing format](https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md#payloads) (the only supported format) and it's been created with the [external tool process](https://docs.sigstore.dev/cosign/signing/signing_with_containers/#sign-and-upload-a-generated-payload-in-another-format-from-another-tool). | ||
|
|
||
| > [!NOTE] | ||
| > NewRelic uses a private repository. It doesn't need extra-services like [Rekor](https://docs.sigstore.dev/logging/overview/) or [Fulcio](https://docs.sigstore.dev/certificate_authority/overview/). That's the reason why Agent Control uses the external tool process instead of `cosign sign`. | ||
danielorihuela marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| As a result of the "external tool process", the OCI repository will contain two packages. One for the agent and one for the signature. The signature package contains, among other things, the payload that was signed (in json format) and it's signature in base64. Inside the payload, we find the hash of the signed agent package. This is enough to verify the signature of an artifact, as we will see later. | ||
|
|
||
| Public keys are **ALWAYS** downloaded when verifying a signature. This avoids problems with outdated caches, like using a revoked key. | ||
| Public keys **MUST** be in JWKS format. | ||
|
|
||
| ```json | ||
| { | ||
| "keys": [ | ||
| { | ||
| "kty": "OKP", | ||
| "alg": null, | ||
| "use": "sig", | ||
| "kid": "key/0", | ||
| "n": null, | ||
| "e": null, | ||
| "x": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
| "y": null, | ||
| "crv": "Ed25519" | ||
| }, | ||
| { | ||
| "kty": "OKP", | ||
| "alg": null, | ||
| "use": "sig", | ||
| "kid": "key/1", | ||
| "n": null, | ||
| "e": null, | ||
| "x": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", | ||
| "y": null, | ||
| "crv": "Ed25519" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Verification flow: | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant AC as Agent Control | ||
| participant OCI as OCI Repository | ||
| participant JWKS as PublicKey server | ||
|
|
||
| Note over AC, JWKS: Step 1: Check artifact signature | ||
| AC->>JWKS: Download public keys | ||
| JWKS-->>AC: Public keys in JWKS format | ||
| AC->>OCI: Download Signature Package | ||
| OCI-->>AC: Base64 Signature & Payload | ||
| AC->>AC: Verify base64 signature against the payload (holds for at least one public key) | ||
| AC->>AC: Extract artifact hash from payload | ||
|
|
||
| Note over AC, OCI: Step 2: Download signed artifact | ||
| AC->>OCI: Download artifact by hash | ||
| OCI-->>AC: Artifact | ||
|
|
||
| Note over AC: Step 3: Integrity Check | ||
| AC->>AC: Compute downloaded artifact hash | ||
| AC->>AC: Compare computed hash against the hash inside the payload | ||
|
|
||
| Note over AC: Result: Package Verified & Trusted | ||
| ``` | ||
|
|
||
| ## Key Rotation | ||
|
|
||
| What happens during a key rotation? It depends on the specific use case. AC tries to verify the signature with every public key published for that package. This approach avoids downtimes. In some situations, we can't dodge the downtime. | ||
|
|
||
| * The first key was published | ||
| * All keys were revoked | ||
|
|
||
| The client can wait for the signature to be created or disable signature verification. | ||
danielorihuela marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Garbage collection | ||
|
|
||
| Agent Control keeps track of the latest installed package. Each install operation executes an old package purge operation, which retains the latest tracked package (i.e. package currently running) and the new installed package. You can think of it like a FIFO with size 2. | ||
|
|
||
| Example: | ||
|
|
||
| 1. User installs infra agent version 1.0.0 (system stores infra 1.0.0) | ||
| 2. User installs infra agent version 3.0.0 (system stores infra 1.0.0 and 3.0.0) | ||
| 3. User installs infra agent version 2.0.0 (system stores infra 2.0.0 and 3.0.0) | ||
|
|
||
| ## Error Handling | ||
|
|
||
| **Installation Failures**: | ||
| - Download errors → Retry if configured, then fail | ||
sigilioso marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - Invalid artifact (not exactly 1 file) → Fail with `InvalidData` | ||
| - Extraction errors → Delete partial installation directory, fail | ||
| - Temp cleanup errors → Installation fails | ||
|
|
||
| ## Local Development | ||
|
|
||
| If needed, you can run a local OCI registry using [zot](https://github.com/project-zot/zot) with | ||
|
|
||
| ```bash | ||
| $ ./tools/oci-registry.sh run | ||
| ``` | ||
|
|
||
| Notice that AC is already configured to use HTTP as protocol when connecting to `localhost:5001` if executed/built __without__ `--release`. | ||
|
|
||
| ## Agent Types Management | ||
| TODO not implemented yet | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.