Skip to content

[akamai] - Replace CEL input with OpenTelemetry akamai_siem receiver - #19496

Merged
ShourieG merged 10 commits into
elastic:mainfrom
ShourieG:feature/akamaisiem_otel
Aug 10, 2026
Merged

[akamai] - Replace CEL input with OpenTelemetry akamai_siem receiver#19496
ShourieG merged 10 commits into
elastic:mainfrom
ShourieG:feature/akamaisiem_otel

Conversation

@ShourieG

@ShourieG ShourieG commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Type of change

  • Enhancement
  • Breaking Change
  • Documentation

Proposed commit message

akamai: replace CEL input with OpenTelemetry akamai_siem receiver

• siem_otel data stream: native OTel receiver replaces the CEL input
  as the API collection method. otelcol now backs the "Collect Akamai
  SIEM logs via API" policy-template option beside GCS. Breaking
  change → package 4.0.0. Requires stack 9.5.0; format_version
  3.6.0 (new SVR00006/SVR00009 checks excluded — legacy pipeline).

• Separate data stream because Fleet gives any otelcol-containing
  data stream a .otel-suffixed template pattern, which would orphan
  logs-akamai.siem-* for existing users.

• Routing: data_stream.dataset var + bodymap docs → events land in
  logs-akamai.siem-<ns>, existing pipeline reused.
  dynamic_dataset/namespace widen the API key to logs-*-* (Fleet
  scopes otelcol grants to .otel datasets that bodymap never writes
  to); the changelog documents the widened grant.

• Agent template: bodymap serializes only the body, so a transform
  writes tags (and the preserve_original_event tag) into the body map
  and seeds @timestamp from the record time — the pipeline only sets
  it when httpMessage.start is present (500s omit it), so those
  events were otherwise rejected by the data stream. String vars are
  quoted (all-digit values ≠ YAML ints); persist_cursor (on by
  default) declares a file_storage extension and sets
  storage: file_storage on the receiver so the poll cursor survives
  agent restarts.

• Tests: policy tests pin the compiled config — logs-*-* grant,
  storage wiring, tags/preserve_original_event rendering, cursor off.
  System test runs against a SIEM API emulator but is skipped pending
  elastic-package#2109 (asserting on a redirected dataset);
  initial_interval is capped at 12h — the receiver rejects larger
  values rather than clamping.

• Breaking-change changelog + README: existing API policies must be
  reconfigured after upgrade, within 12 hours — the SIEM API only
  serves the previous 12h of events; SSL options must be rewritten in
  OTel tls schema. README reorganised around two collection methods
  (API/GCS); cursor persistence ("Persist Cursor" option) not
  guaranteed across stack upgrades and breaking changes.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

NOTE:

Upgrade path was tested locally before going for a breaking-change

Steps:

  • Version 3.2.0 (CEL Input) was configured with akamai mock server and ingestion started.
  • Initially a version 3.3.0 was made with CEL being replaced by otel receiver and the input was enabled by default.
    In this scenario, while upgrading we saw the following error :-
Screenshot 2026-07-24 at 5 09 13 PM

This is because the separate data stream approach ( adopted due to fleet limitations) changed the fleet computed attribute paths and they were no longer configured.

  • Finally we created a breaking change with version 4.0.0 and kept the input disabled by default and the upgrade happened without any errors.
  • After reconfiguring the updated integration, ingestion started as usual but with much higher throughput.

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Screenshots

All Inputs

Screenshot 2026-07-24 at 6 31 37 PM

Receiver Config

Screenshot 2026-07-24 at 6 32 36 PM
@ShourieG ShourieG self-assigned this Jun 11, 2026
@ShourieG ShourieG added enhancement New feature or request Integration:akamai Akamai Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] docs labels Jun 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Elastic Docs Style Checker (Vale)

Summary: 1 warning, 1 suggestion found

⚠️ Warnings (1): Fix when the suggestion improves clarity or correctness.
File Line Rule Message
packages/akamai/_dev/build/docs/README.md 25 Elastic.DirectionalLanguage Don't use directional language. Use 'the following steps' instead of 'the below steps'.
💡 Suggestions (1): Optional style improvements. Apply when helpful.
File Line Rule Message
packages/akamai/data_stream/siem_otel/manifest.yml 103 Elastic.WordChoice Consider using 'deactivated, deselected, hidden, turned off, unavailable' instead of 'Disabled', unless the term is in the UI.

The Vale linter checks documentation changes against the Elastic Docs style guide. To use Vale locally or report issues, refer to Elastic style guide for Vale.

@github-actions

Copy link
Copy Markdown
Contributor

TL;DR

buildkite/integrations failed primarily because packages/akamai/changelog.yml contains a PR link to .../pull/99999 instead of this PR (.../pull/19496). Update that link and re-run CI first; the second failed job log is truncated and does not include its underlying error.

Remediation

  • In packages/akamai/changelog.yml, replace the new entry’s link value https://github.com/elastic/integrations/pull/99999 with https://github.com/elastic/integrations/pull/19496.
  • Re-run the Buildkite build after the changelog fix. If Check integrations akamai still fails, pull the full step log/artifacts for that job (the attached excerpt ends at teardown and omits the original failure line).
Investigation details

Root Cause

The :scroll: Check changelog PR links step validates that modified changelog.yml entries point to the current PR number. It failed on packages/akamai/changelog.yml because the file contains a hardcoded mismatched PR link (99999).

The separate Check integrations akamai log provided in this workflow artifact does not contain the actual failing command output (only teardown + artifact upload), so its exact root cause cannot be confirmed from the available data.

Evidence

  • Build: https://buildkite.com/elastic/integrations/builds/44413
  • Job/step: :scroll: Check changelog PR links
  • Key log excerpt:
    ERROR: unexpected link: 'https://github.com/elastic/integrations/pull/99999'
           expected:         'https://github.com/elastic/integrations/pull/19496'
    --- 1 changelog link(s) do not match this PR
    - `packages/akamai/changelog.yml`
    
  • Job/step: Check integrations akamai
  • Key log excerpt:
    --- [akamai] failed
    🚨 Error: The command exited with status 1
    user command error: exit status 1
    
    (No preceding stack trace/assertion/error line is present in the provided log file.)

Verification

  • Not run locally: the Buildkite wrapper script requires CI-provided environment/tooling not present in this runner (YQ_VERSION was unset), so local reproduction was not representative of CI.

Follow-up

After correcting the changelog link, if the Akamai integration step still fails, share the full untruncated step log (or failed JUnit XML content) and I can map it to the exact source file/line for a second-pass fix recommendation.

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

What is this? | From workflow: PR Buildkite Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@andrewkroh andrewkroh added the documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. label Jun 11, 2026
@ShourieG

ShourieG commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@jamiehynds, wanted to get your opinion on this change from an user perspective.

With the current limitations imposed by Fleet, having an otel receiver config within an existing data stream breaks it for existing users since fleet renames the index template by appending an .otel suffix to the template name. Because of this I needed to create a separate data stream for the otel receiver and then route the data to the existing data stream to avoid ingest pipeline duplication.

The user sees this as a new input type in addition to the existing cel and gcs inputs as shown in the screenshots. Upon configuring and using it the data lands in the original akamai.siem data stream. The issue is now we have almost two similar inputs with almost identical configurations, the CEL API based Input and the OpenTelemetry receiver that also uses the api underneath but with custom Go code that makes it way more stable and performant compared to the CEL input. We went the OTEL route since Akamai pull api has a lot of issues and the CEL implementation suffers a lot from these limitations with various reported and escalated in the past.

The issue now is about user perception. We end up having two similar looking inputs from the outside CEL API and OpenTelemetry receiver but the latter is much more stable an performant.

Do we keep both of these or phase out the older CEL implementation slowly or what pitch do we make to users to it's less confusing on their side.

cc: @narph

@jamiehynds

Copy link
Copy Markdown

Collection transport - I'm assuming none of the managed OTel paths apply, since we're still hitting the same Akamai SIEM API and Akamai isn't pushing to us. mOTLP (the Managed OTLP Endpoint) is out - that's a destination you send to, not something that polls, so Akamai can't push to a receiver like that; ECF doesn't fit for the same reason. We need a durable collector to pull Akamai's API on an interval. Right?

Agentless — Today agentless is limited to cel/httpjson, so I assume the gap is that the agentless runtime doesn't yet run the EDOT Collector / otel input type? Worth flagging: the CEL Akamai input can likely run agentless today, so if we make the OTel receiver the recommended path but it forces a self-managed agent, that's a regression for those users. What would it take to get the otel input supported in agentless?

Packaging — would an OTel input package (akamai_input_otel) be a better fit than bolting the receiver into the existing data stream? My assumption is no: Akamai isn't OTel-native, we're only using the Collector to hit their API, and input packages store data OTel-native rather than ECS in logs-akamai.siem-* - so we'd lose pipeline/dashboard/detection-rule continuity for existing users and need a migration path, whereas your current design deliberately routes back through the existing ECS pipeline to keep all that working.

cc @qcorporation @andrewkroh as this may relate to ingest hub onboarding, and it's the first example I'm aware of using an OTel collector but mapping back to ECS

@qcorporation

Copy link
Copy Markdown
Contributor

Collection transport - I'm assuming none of the managed OTel paths apply, since we're still hitting the same Akamai SIEM API and Akamai isn't pushing to us. mOTLP (the Managed OTLP Endpoint) is out - that's a destination you send to, not something that polls, so Akamai can't push to a receiver like that; ECF doesn't fit for the same reason. We need a durable collector to pull Akamai's API on an interval. Right?

Agentless — Today agentless is limited to cel/httpjson, so I assume the gap is that the agentless runtime doesn't yet run the EDOT Collector / otel input type? Worth flagging: the CEL Akamai input can likely run agentless today, so if we make the OTel receiver the recommended path but it forces a self-managed agent, that's a regression for those users. What would it take to get the otel input supported in agentless?

Packaging — would an OTel input package (akamai_input_otel) be a better fit than bolting the receiver into the existing data stream? My assumption is no: Akamai isn't OTel-native, we're only using the Collector to hit their API, and input packages store data OTel-native rather than ECS in logs-akamai.siem-* - so we'd lose pipeline/dashboard/detection-rule continuity for existing users and need a migration path, whereas your current design deliberately routes back through the existing ECS pipeline to keep all that working.

cc @qcorporation @andrewkroh as this may relate to ingest hub onboarding, and it's the first example I'm aware of using an OTel collector but mapping back to ECS

Really interesting conversation cc.ing @nimarezainia as well

@ShourieG

Copy link
Copy Markdown
Contributor Author

Collection transport - I'm assuming none of the managed OTel paths apply, since we're still hitting the same Akamai SIEM API and Akamai isn't pushing to us. mOTLP (the Managed OTLP Endpoint) is out - that's a destination you send to, not something that polls, so Akamai can't push to a receiver like that; ECF doesn't fit for the same reason. We need a durable collector to pull Akamai's API on an interval. Right?

Agentless — Today agentless is limited to cel/httpjson, so I assume the gap is that the agentless runtime doesn't yet run the EDOT Collector / otel input type? Worth flagging: the CEL Akamai input can likely run agentless today, so if we make the OTel receiver the recommended path but it forces a self-managed agent, that's a regression for those users. What would it take to get the otel input supported in agentless?

Packaging — would an OTel input package (akamai_input_otel) be a better fit than bolting the receiver into the existing data stream? My assumption is no: Akamai isn't OTel-native, we're only using the Collector to hit their API, and input packages store data OTel-native rather than ECS in logs-akamai.siem-* - so we'd lose pipeline/dashboard/detection-rule continuity for existing users and need a migration path, whereas your current design deliberately routes back through the existing ECS pipeline to keep all that working.

cc @qcorporation @andrewkroh as this may relate to ingest hub onboarding, and it's the first example I'm aware of using an OTel collector but mapping back to ECS

@jamiehynds,

  1. Yes currently Akamai otel receiver also uses a polling logic and it has local cursor state.

  2. I'm working on a fix to make sure we can enable Agentless with the akamai receiver here. Once done this thould help other otel receivers to also work in agentless mode.

  3. Making a separate package will just end up duplicating the ingest pipelines without any real benefit since we do not output actual otel format atm.

@vera-review-bot

Copy link
Copy Markdown

👀 I have started reviewing the PR

Comment thread packages/akamai/manifest.yml
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml Outdated
Comment thread packages/akamai/changelog.yml Outdated
@andrewkroh

Copy link
Copy Markdown
Contributor

My take is this shouldn't be an additional input. The receiver should replace the CEL input with an invisible migration, so we have one API collection mechanism and one code path.

  • For invisible migration we use Fleet's migrate_from feature. The CEL config maps over almost one-to-one (we need to align the names for api_hostendpoint, intervalpoll_interval, initial_intervalinitial_lookback, but the rest are identical), so an existing CEL user shouldn't have to touch anything on upgrade.
  • The .otel suffix is a blocker. And we need to solve this with the Fleet team in Kibana. It's applied in EPM purely because the input type is otelcol. Our receiver emits ECS (bodymap), not semconv, so the data belongs in logs-akamai.siem-* as it does today. I'd rather get Fleet to let an otelcol input write to a classic non-.otel dataset than ship any workaround.
  • Losing the cursor on cutover is low impact. Akamai's offset token is very short-lived, and the existing pipeline already fingerprints event.original into _id in the same index, so re-fetched data is deduplicated. But only if the event.original values are exactly the same, so we need to check this.
  • The akamai package doesn't support agentless today so there are no agentless CEL users to regress. Therefore we are not blocked on the missing OTel storage extension that would let the receiver persist state to ES.

I would like the same package, replaced input, same dataset.

@ShourieG

ShourieG commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

My take is this shouldn't be an additional input. The receiver should replace the CEL input with an invisible migration, so we have one API collection mechanism and one code path.

  • For invisible migration we use Fleet's migrate_from feature. The CEL config maps over almost one-to-one (we need to align the names for api_hostendpoint, intervalpoll_interval, initial_intervalinitial_lookback, but the rest are identical), so an existing CEL user shouldn't have to touch anything on upgrade.
  • The .otel suffix is a blocker. And we need to solve this with the Fleet team in Kibana. It's applied in EPM purely because the input type is otelcol. Our receiver emits ECS (bodymap), not semconv, so the data belongs in logs-akamai.siem-* as it does today. I'd rather get Fleet to let an otelcol input write to a classic non-.otel dataset than ship any workaround.
  • Losing the cursor on cutover is low impact. Akamai's offset token is very short-lived, and the existing pipeline already fingerprints event.original into _id in the same index, so re-fetched data is deduplicated. But only if the event.original values are exactly the same, so we need to check this.
  • The akamai package doesn't support agentless today so there are no agentless CEL users to regress. Therefore we are not blocked on the missing OTel storage extension that would let the receiver persist state to ES.

I would like the same package, replaced input, same dataset.

@andrewkroh, but what about the state ? Currently the CEL state is not compatible with otel receiver, so it will be a breaking change for existing users on older stacks and then upgrading to 9.5 ?

@nimarezainia

Copy link
Copy Markdown
Contributor

Agentless — Today agentless is limited to cel/httpjson, so I assume the gap is that the agentless runtime doesn't yet run the EDOT Collector / otel input type? Worth flagging: the CEL Akamai input can likely run agentless today, so if we make the OTel receiver the recommended path but it forces a self-managed agent, that's a regression for those users. What would it take to get the otel input supported in agentless?

@jamiehynds we are no longer bound by cel/httpjson only inputs, observabilty team has already added aws-metrics (as an example) and worked on supporting many other integrations.

@ishleenk17 and @lalit-satapathy can comment on the otel input and agentless deployment as their teams have been adapting many otel packages to agentless as well.

@andrewkroh

andrewkroh commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Losing the cursor on cutover is low impact. Akamai's offset token is very short-lived, and the existing pipeline already fingerprints event.original into _id in the same index, so re-fetched data is deduplicated. But only if the event.original values are exactly the same, so we need to check this.

@andrewkroh, but what about the state ? Currently the CEL state is not compatible with otel receiver, so it will be a breaking change for existing users on older stacks and then upgrading to 9.5 ?

@ShourieG Isn't the pagination offset token only valid for 2 minutes? This would mean that cursor reset is a very normal occurrence, so a change-over that does not retain state will be indistinguishable from other events like an agent software upgrade that causes more than 2 minutes of downtime.

@ShourieG

ShourieG commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Losing the cursor on cutover is low impact. Akamai's offset token is very short-lived, and the existing pipeline already fingerprints event.original into _id in the same index, so re-fetched data is deduplicated. But only if the event.original values are exactly the same, so we need to check this.

@andrewkroh, but what about the state ? Currently the CEL state is not compatible with otel receiver, so it will be a breaking change for existing users on older stacks and then upgrading to 9.5 ?

@ShourieG Isn't the pagination offset token only valid for 2 minutes? This would mean that cursor reset is a very normal occurrence, so a change-over that does not retain state will be indistinguishable from other events like an agent software upgrade that causes more than 2 minutes of downtime.

Yes this is true, token validity is indeed 2mins atm. But then I think we should explicitly call it out in some upgrade note/description etc, that this will be a breaking change, and increment to version 4.0.0. This is just to preserve clarity with the end user on what's happening.

I have just one concern with out right replacing, that is, is not better to do a bit of real-world testing with end users/customers before completely removing the CEL input ? That way we could potentially identify any issues that might exist. The CEL input is GA and has been for a while. The new input has not been stress tested under real circumstances yet.

@ishleenk17

ishleenk17 commented Jun 22, 2026

Copy link
Copy Markdown
Member

Agentless — Today agentless is limited to cel/httpjson, so I assume the gap is that the agentless runtime doesn't yet run the EDOT Collector / otel input type? Worth flagging: the CEL Akamai input can likely run agentless today, so if we make the OTel receiver the recommended path but it forces a self-managed agent, that's a regression for those users. What would it take to get the otel input supported in agentless?

@jamiehynds we are no longer bound by cel/httpjson only inputs, observabilty team has already added aws-metrics (as an example) and worked on supporting many other integrations.

@ishleenk17 and @lalit-satapathy can comment on the otel input and agentless deployment as their teams have been adapting many otel packages to agentless as well.

Is the question if OTel receivers can be used in agentless mode ?

@narph

narph commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The reason we developed a dedicated Akamai input in OTel was to address known limitations with the Akamai SIEM API and the existing CEL-based implementation. This issue has affected not only us but also other vendors and competitors that have integrated with the API.

Adding both options would likely create confusion for users without really solving the underlying problem. We'd still be offering an ingestion method that we know has limitations and isn't the preferred approach.

We should stay consistent with how we've handled this in the past and replace the existing input rather than add another option. Since the upgrade impact and the effort required to switch inputs are minimal, replacing it gives users a simpler experience and steers them toward the more reliable solution.

@ShourieG

ShourieG commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@narph @andrewkroh, Did an analysis with the results below, the main friction of replacing without breaking changes is that the ssl/tls configs differ a bit in beats vs otel.


Akamai SIEM: TLS config mapping (Beats ssl: → OTel tls:)

When moving the Akamai SIEM collection from the CEL input (data_stream/siem) to the
akamai_siem OTel receiver (data_stream/siem_otel), the transport-security config
has to be re-expressed. Both configure the same protocol — TLS for the outbound HTTPS
connection to the Akamai API. "SSL" is just legacy naming.

Only the config schema differs:

Beats (CEL input) OTel (akamai_siem receiver)
Config block name ssl: tls:
Underlying protocol TLS TLS (identical)
Source schema libbeat SSL common config configtls.ClientConfig v1.60.0 (squashed via confighttp)

The bytes on the wire are the same. The migration cost is translating one YAML schema
into the other
, not changing the encryption.

Beats ssl: (current CEL input)

ssl:
  certificate_authorities:                 # LIST of files or inline PEM
    - /etc/agent/certs/akamai-ca.pem
    - /etc/agent/certs/corp-root.pem
  certificate: /etc/agent/certs/client.pem
  key: /etc/agent/certs/client-key.pem
  key_passphrase: "${KEY_PASS}"
  verification_mode: certificate           # full | strict | certificate | none
  supported_protocols: [TLSv1.2, TLSv1.3]  # explicit list
  cipher_suites:
    - ECDHE-RSA-AES-256-GCM-SHA384         # OpenSSL-style names
  curve_types: [P-256]
  ca_trusted_fingerprint: "9f8b...c2"      # pin by sha256 fingerprint

OTel tls: (akamai_siem receiver)

confighttp.ClientConfig squashes configtls.ClientConfig
at the receiver root, so this block sits directly under the receiver next to endpoint:.
The keys below are defined in configtls.Config (shared) plus the
client-only fields in configtls.ClientConfig.

tls:
  ca_file: /etc/agent/certs/akamai-bundle.pem   # the two CAs above concatenated into ONE file
  cert_file: /etc/agent/certs/client.pem
  key_file: /etc/agent/certs/client-key.pem
  # key_passphrase:        -> no equivalent; key must be decrypted first
  insecure_skip_verify: false                   # only `verification_mode: none` maps here (-> true)
  min_version: "1.2"                            # note "1.2", not "TLSv1.2"
  max_version: "1.3"
  cipher_suites:
    - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384     # IANA/Go names, NOT the Beats names
  curve_preferences: [P-256]
  include_system_ca_certs_pool: true            # see behavioral note below
  # ca_trusted_fingerprint: -> no equivalent

Name changes (mechanical renames)

Beats ssl: OTel tls: Note
certificate_authorities (list) ca_file / ca_pem (single) list → single; concat multiple CAs into one PEM
certificate cert_file / cert_pem rename only
key key_file / key_pem rename only
curve_types curve_preferences rename (value naming also differs)
cipher_suites cipher_suites same key, different value vocabulary (OpenSSL → IANA/Go names)

Lossy conversions (need human judgment)

Beats ssl: OTel tls: Why lossy
verification_mode: none insecure_skip_verify: true enum → bool
verification_mode: full / strict insecure_skip_verify: false (default) enum → bool
supported_protocols: [..] min_version / max_version list → range; can't express non-contiguous sets (e.g. 1.1 + 1.3 but not 1.2); version string format differs

No OTel equivalent

Beats ssl: Status
key_passphrase ❌ no field — passphrase-protected keys won't load; decrypt the key first
verification_mode: certificate ❌ no equivalent — "verify chain but skip hostname" can't be expressed (OTel is verify-all or verify-nothing)
ca_trusted_fingerprint / ca_sha256 ❌ no fingerprint/sha256 CA pinning in configtls
renegotiation ❌ no equivalent

Behavioral changes: system CA pool default flips

In configtls, specifying ca_file/ca_pem means you trust only that CA — the system
root pool is excluded unless you set include_system_ca_certs_pool: true. A Beats user who
added a private CA generally still has system roots available, so a naïve port can silently
break TLS to anything signed by a public CA.


References

OTel side (resolved module versions in this repo):

  • configtls.ClientConfig — the tls: schema (client-only fields: insecure,
    insecure_skip_verify, server_name_override), v1.60.0:
    pkg.go.dev · source
  • configtls.Config — the shared/squashed TLS fields (ca_file, ca_pem, cert_file,
    cert_pem, key_file, key_pem, min_version, max_version, cipher_suites,
    curve_preferences, include_system_ca_certs_pool, reload_interval, …), v1.60.0:
    pkg.go.dev
  • confighttp.ClientConfig — owns the tls field and applies it via ToClient
    (cc.TLS.LoadTLSConfigtransport.TLSClientConfig), v0.154.0:
    pkg.go.dev · ToClient · source

In-repo wiring (this receiver):

  • Squash that exposes tls: at the receiver root — config.go (HTTP confighttp.ClientConfig with ,squash)
  • Where the client (incl. TLS transport) is built — receiver.go (r.cfg.HTTP.ToClient(...))

Beats side:

  • libbeat tlscommon — the ssl: schema source: config.go
  • SSL common config reference (what the CEL ssl var description links to): elastic.co
@ShourieG
ShourieG force-pushed the feature/akamaisiem_otel branch from fc275fc to 0bb8dfc Compare June 26, 2026 10:11
@ShourieG

ShourieG commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@narph @andrewkroh, I've added a key by key explanation for the hard gaps (missing keys) for the ssl block and their possible impact.


Otel vs Beats (TLS Gaps)

Beats ssl: OTel tls: Impact Outcome
key_passphrase nothing can't use an encrypted private key won't start
verification_mode: certificate nothing (server_name_override helps a bit) can't "check the cert but skip the hostname" won't start
ca_trusted_fingerprint / ca_sha256 nothing must supply the full CA file instead won't start
renegotiation nothing handshake may fail with old proxies won't start
(system CAs trusted by default) include_system_ca_certs_pool (off by default) adding a CA quietly drops public trust starts anyway

Examples to explain each scenario

1. key_passphrase — encrypted private key

You use a client certificate to get through a proxy (mTLS), and your security
rules say the private key must be stored encrypted. Beats unlocks it with a
passphrase; the receiver can't.

# Beats — works
ssl: { certificate: client.pem, key: client.key, key_passphrase: "${PASS}" }

# OTel — no passphrase setting; the key has to be unencrypted on disk
tls: { cert_file: client.pem, key_file: client.key }   # encrypted key -> fails to load

2. verification_mode: certificate — check the cert, skip the hostname

You go through a proxy whose certificate is valid (signed by a CA you trust) but
the hostname doesn't match. Beats can still check the certificate and ignore the
hostname. The receiver only does all-or-nothing.

# Beats — check the cert, allow the hostname mismatch
ssl: { verification_mode: certificate }

# OTel — two blunt options:
tls: { insecure_skip_verify: true }             # turns off all checking (weaker)
tls: { server_name_override: "real-cert-name" } # works if you know the name on the cert

3. ca_trusted_fingerprint / ca_sha256 — trust a CA by fingerprint

You trust your proxy's internal CA by its fingerprint instead of copying the whole
certificate file. The receiver has no fingerprint option — you give it the actual
certificate.

# Beats — paste the fingerprint
ssl: { ca_trusted_fingerprint: "9f8b…c2" }

# OTel — point at the real certificate file
tls: { ca_file: /etc/agent/proxy-ca.pem }

4. renegotiation — TLS renegotiation

An old proxy renegotiates the TLS connection partway through (some do this for
client-cert auth). Beats lets you allow it; the receiver doesn't have the setting.
This rarely matters — TLS 1.3 dropped renegotiation.

# Beats
ssl: { renegotiation: freely }

# OTel — no setting; Go's defaults apply

5. include_system_ca_certs_pool

When you add your proxy's private CA, the receiver then trusts only that CA.
Anything signed by a normal public CA stops working — and nothing warns you,
because the config is valid.

# Beats — adding a CA doesn't drop public trust
ssl: { certificate_authorities: [proxy-ca.pem] }

# OTel — turn public CAs back on, or the direct Akamai connection breaks
tls:
  ca_file: proxy-ca.pem
  include_system_ca_certs_pool: true   # leave this out and the Akamai connection fails
@narph

narph commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

This looks like a functionality degradation, but I wonder how often these options are actually used. @andrewkroh, do you think we can proceed with this? It could break existing setups.

@andrewkroh

Copy link
Copy Markdown
Contributor

None of those missing TLS config options seem like blockers because the input is talking to a SaaS API with a CA cert issued by a widely trusted CA. If this is widely shared TLS config in the otel, then we should log issue(s) to close that gap between what the agent offers because it could be important for future use-cases.

@ShourieG
ShourieG force-pushed the feature/akamaisiem_otel branch from 0bb8dfc to 39bfd25 Compare July 24, 2026 12:39
@ShourieG ShourieG changed the title [akamai] - Add OpenTelemetry collection mode via the Akamai Siem Receiver Jul 24, 2026
ShourieG added 4 commits July 29, 2026 14:44
The OpenTelemetry akamai_siem receiver becomes the collection method for
the Akamai SIEM API, replacing the CEL input. The policy template now
offers "Collect Akamai SIEM logs via API" (backed by the receiver)
alongside the Google Cloud Storage method, and the receiver is no longer
labelled a technical preview.

This is a breaking change: existing policies that collect via the API
must be reconfigured after upgrading. The package major version is bumped
to 4.0.0 and the minimum supported Elastic Stack version is raised to
9.5.0.

The README is reorganised around the two collection methods (API and
GCS), and the cursor-persistence note now documents that persistence is
not guaranteed across stack upgrades and breaking changes.
The OpenTelemetry akamai_siem receiver already replaced the CEL input as
the API collection method, so the leftover CEL artifacts are no longer
needed. This removes the CEL stream from the siem data stream manifest,
its cel.yml.hbs template, the CEL system test, and an orphaned CEL
pipeline test config.

The emulator-based system test is re-wired onto the siem_otel data
stream using the otelcol input against the same akamai-siem-emulator
mock. The test is skipped for now because the receiver redirects all
events to the akamai.siem dataset and elastic-package cannot yet assert
hit counts on a redirected dataset (elastic/elastic-package#2109). The
shared akamai.siem ingest pipeline remains covered by the siem pipeline
test.
@ShourieG
ShourieG force-pushed the feature/akamaisiem_otel branch from cd865b7 to 7422279 Compare July 29, 2026 09:16
Comment thread packages/akamai/data_stream/siem_otel/agent/stream/otelcol.yml.hbs Outdated
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml
Comment thread packages/akamai/data_stream/siem_otel/agent/stream/otelcol.yml.hbs Outdated
Comment thread packages/akamai/_dev/build/docs/README.md Outdated
@elastic-vault-github-plugin-prod

elastic-vault-github-plugin-prod Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

🚀 Benchmarks report

Package akamai 👍(0) 💚(0) 💔(1)

Expand to view
Data stream Previous EPS New EPS Diff (%) Result
siem 1090.51 851.79 -238.72 (-21.89%) 💔

To see the full report comment with /test benchmark fullreport

Render the data_stream.dataset var in otelcol.yml.hbs instead of a
hardcoded literal so the dataset declared in the Fleet policy is the
one the receiver writes; the default still targets akamai.siem. Rework
the tags OTTL statement to emit preserve_original_event first so an
empty tags list can no longer render an invalid list literal.

Extend the 4.0.0 breaking-change note with the 9.5.0 minimum stack
version and the Beats ssl to OpenTelemetry tls schema change, add the
disabled-by-default input toggle to the API setup steps, and dedupe
the cursor-persistence note while scoping the replay-deduplication
claim to the same backing index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/akamai/_dev/build/docs/README.md Outdated
@elastic elastic deleted a comment from vera-review-bot Bot Jul 29, 2026
ShourieG and others added 2 commits July 30, 2026 19:26
Address review feedback on the OTel rework. The otelcol collection
path had no executable coverage because the emulator system test is
skipped, so add a policy test that pins the Fleet-rendered collector
config: the injected transform routes events to the akamai.siem
dataset, dynamic_dataset/dynamic_namespace widen the API key grant to
logs-*-*, and the storage reference is rewritten to the renamed
file_storage extension. The expected policy is generated against
9.5.0-SNAPSHOT, the version CI resolves for the ^9.5.0 condition.

Retitle the config_ids variable from "Zone ID" to "Security
Configuration IDs" so the Fleet UI matches the variable description
and the README wording; the old title was inherited from the removed
CEL stream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml
Comment thread packages/akamai/data_stream/siem_otel/_dev/test/system/test-emulator-config.yml Outdated
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml
Comment thread packages/akamai/_dev/build/docs/README.md Outdated
Comment thread packages/akamai/data_stream/siem_otel/agent/stream/otelcol.yml.hbs
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml

@chrisberkhout chrisberkhout left a comment

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.

For the commit message:

  • Has this been manually tested against a live API?
  • Should it be "OpenTelemetry akamai_siem receiver" rather than "OpenTelemetry akamaisiemreceiver"?

Question: Can you please say the reason why the new siem_otel collection can't be done directly in data_stream/siem if it's correct that it wouldn't work? Is it because of a fleet issue during upgrade? If it could be done it would avoid data_steam/siem_otel existing without being populated itself.

Comment thread packages/akamai/data_stream/siem_otel/manifest.yml Outdated
Comment thread packages/akamai/data_stream/siem_otel/manifest.yml Outdated
Comment thread packages/akamai/data_stream/siem_otel/agent/stream/otelcol.yml.hbs Outdated
Comment thread packages/akamai/data_stream/siem_otel/fields/base-fields.yml

The Akamai integration collects events from the Akamai API, specifically reading from the [Akamai SIEM API](https://techdocs.akamai.com/siem-integration/reference/api).

> **Breaking change in 4.0.0**: The backend used to collect Akamai SIEM logs from the API has changed. This version requires Elastic Stack 9.5.0 or later. After upgrading, existing agent policies that collect Akamai SIEM logs via the API must be reconfigured for data collection to resume. If you previously set the advanced **SSL Configuration** option, it must be rewritten using the OpenTelemetry `tls` schema (`ca_file`/`ca_pem`, `cert_file`/`cert_pem`, `key_file`/`key_pem`, `insecure_skip_verify`, `min_version`, `max_version`) rather than the Beats keys (`certificate_authorities`, `verification_mode`, `supported_protocols`). Collection from Google Cloud Storage is unaffected.

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.

Could mention that it's only possible to get 12 hours of historical data, so the new policy should be configured within that time to avoid data gaps.

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.

Don't see this part. Did you mean to commit something for that?

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.

addressed this.

@ShourieG

ShourieG commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

For the commit message:

  • Has this been manually tested against a live API?
  • Should it be "OpenTelemetry akamai_siem receiver" rather than "OpenTelemetry akamaisiemreceiver"?

Question: Can you please say the reason why the new siem_otel collection can't be done directly in data_stream/siem if it's correct that it wouldn't work? Is it because of a fleet issue during upgrade? If it could be done it would avoid data_steam/siem_otel existing without being populated itself.

@chrisberkhout,

This has been live tested against Andrew's mock Akamai API at https://proteus.sit.estc.dev/. There's a mock active, you can check it out. Getting access to a real env is tricky because of setup issues.

We can rename to OpenTelemetry akamai_siem receiver, but kept it akamaisiemreceiver to align to the actual receiver name.

The reason we can't do siem_otel in the existing siem data stream is because fleet current renames the index template of the data stream by adding a .otel suffix to it, if it sees an otelcol type input. So logs-akamai.siem becomes logs-akamai.siem.otel with match pattern logs-akamai.siem.otel-*. Creating a separate data stream avoids this. We would still end up creating a logs-akamai.siem.otel template but nothing would ever write to it due to our custom receiver level routing.

Rename the enable_storage variable to persist_cursor across the
manifest, agent template, and test configurations, and reword its
description and the README to match. Drop the placeholder default for
api_host and render event_limit only when set.

Split the tags transform statement over several lines using template
whitespace trimming so it is easier to read; the rendered policy is
unchanged. Add policy tests covering preserve_original_event with
custom tags and disabled cursor persistence.

Document in the changelog that dynamic dataset/namespace routing
widens the agent output API key scope to logs-*-*.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ShourieG

Copy link
Copy Markdown
Contributor Author

@chrisberkhout, I've addressed all the issues.

Comment thread packages/akamai/data_stream/siem_otel/agent/stream/otelcol.yml.hbs
Comment thread packages/akamai/data_stream/siem_otel/_dev/test/system/test-emulator-config.yml Outdated
@vera-review-bot

Copy link
Copy Markdown

Review summary

Issues found across the latest commits 2f570bf — 1 high, 1 medium
  • 🟠 In bodymap mode nothing puts @​timestamp in the document, so Akamai events that carry no httpMessage.start (the pipeline notes 500s do not) will have no @​timestamp and be rejected by the data stream (link) (Unresolved)
  • 🟡 The system test sets initial_interval: 24h on the claim that the receiver caps it, but the receiver rejects initial_lookback above 12h at config-validation time, so this config would fail to start (link) (Unresolved)
Issues found across earlier commits d47ae09 — 2 medium
  • 🟡 event_limit is rendered unguarded, so clearing the optional var emits event_limit: (null) and the receiver rejects the whole collector config (link) (Resolved)
  • 🟡 dynamic_dataset/dynamic_namespace widen this policy's agent output API key from logs-akamai.siem-<namespace> to logs-*-* (link) (Resolved)
Issues found across earlier commits ef0acd7 — 2 high, 2 medium
  • 🟠 The namespace var defaults to default and is not tied to the policy namespace, so a policy using any other namespace silently writes to logs-akamai.siem-default (link) (Unresolved)
  • 🟠 The system test sets initial_interval: 24h on the claim that the receiver caps it at 12h, but the receiver rejects the value at config validation (link) (Unresolved)
  • 🟡 The new siem_otel data stream ships with no pipeline tests and its only system test is skipped, so the new API collection path has no automated coverage (link) (Unresolved)
  • 🟡 The 4.0.0 breaking-change note only mentions SSL, but the migration also drops four configuration options (link) (Unresolved)
Issues found across earlier commits 3bf57bc — 1 high, 1 medium, 1 low
  • 🟠 The only test for the new otelcol collection path is skipped, and there is no policy test, so nothing in CI verifies the rendered collector config. Add a _dev/test/policy/ test for the siem_otel stream. (link) (Resolved)
  • 🔵 The new setup steps tell users to configure "Security Configuration IDs", but the field is titled "Zone ID" in the UI. Align the README wording with the var title (or rename the var). (link) (Resolved)
Issues found across earlier commits cf99bfa7422279 (29 commits) — 3 medium, 2 low
  • 🟡 The data_stream.dataset variable declared in the siem_otel manifest is never used by otelcol.yml.hbs, which hardcodes dataset: akamai.siem - render the variable instead so the receiver honours a customised dataset. (link) (Resolved)
  • 🟡 The otelcol stream is declared enabled: false, so the API collection path is off by default after upgrading - drop the flag (or document the extra toggle in the README). (link) (Resolved)
  • 🟡 New data stream 'siem_otel' ships without pipeline tests, and its only system test is skipped, so the new collection path has no executable test coverage. (link) (Resolved)
  • 🔵 The tags OTTL statement emits a leading comma before preserve_original_event, producing invalid OTTL ([, "preserve_original_event"]) when the tags list is empty - move the flag inside the loop-aware separator logic. (link) (Resolved)
  • 🔵 The cursor-persistence note repeats the same sentence twice and claims duplicates are always removed - trim the repetition and add the same backing-index caveat the manifest already states. (link) (Resolved)
Issues found across earlier commits 26c576ccd865b7 (250 commits) — 2 medium
  • 🟡 The data_stream.dataset variable is declared required: true in the manifest but never referenced by otelcol.yml.hbs, which hardcodes dataset: akamai.siem. Render the variable instead of the literal, or drop the variable. (link) (Resolved)
  • 🟡 The 4.0.0 upgrade note omits two things an upgrading user hits immediately: the new 9.5.0 minimum stack version, and the fact that any existing ssl: block must be rewritten in the OpenTelemetry tls: schema. Extend the note to cover both. (link) (Resolved)

A new commit triggers another review — at most once every 15 minutes. I skip the PR while it's approved or has merge conflicts.

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

@chrisberkhout

Copy link
Copy Markdown
Contributor

This has been live tested against Andrew's mock Akamai API at https://proteus.sit.estc.dev/. There's a mock active, you can check it out. Getting access to a real env is tricky because of setup issues.

Okay, that's fine. Could optionally note the mock-only testing in the end of the commit message.

The reason we can't do siem_otel in the existing siem data stream is because fleet current renames the index template of the data stream by adding a .otel suffix to it, if it sees an otelcol type input. So logs-akamai.siem becomes logs-akamai.siem.otel with match pattern logs-akamai.siem.otel-*. Creating a separate data stream avoids this. We would still end up creating a logs-akamai.siem.otel template but nothing would ever write to it due to our custom receiver level routing.

Thanks, good explanation. That makes sense. So it's clear in the PR, but for future maintenance it would help to have a comment in the policy config, near the data_stream.dataset part, saying that the receiver's writes are redirected to siem and what Fleet does to the package's siem_otel DS.

We can rename to OpenTelemetry akamai_siem receiver, but kept it akamaisiemreceiver to align to the actual receiver name.

Okay. I see akamai_siem in the README and policy config. So if akamai_siem is the Beats name, maybe that's the one we should prefer?

@chrisberkhout chrisberkhout left a comment

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.

Thanks for the updates and explanations.

Remaining points worth a final look are only about doc/code comments:

  • #19496 (comment)
    (policy update required within max data range of 12h to avoid gaps)
  • #19496 (comment)
    (testing in commit message, maintainer comment in policy, receiver naming)

Otherwise GTG

@mergify

mergify Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request
In bodymap mode the Elasticsearch exporter serializes only the log
record body into the document, and the ingest pipeline sets @timestamp
only when httpMessage.start is present, so events without it (Akamai
internal 500s) were rejected by the data stream. Seed @timestamp from
the record time as a fallback; the pipeline still overwrites it with
the real event time when available.

Also address review feedback: correct the system test fixture and the
initial_interval description to say the receiver rejects lookbacks
over 12h rather than clamping, document receiver-level dataset routing
next to the data_stream block, and note the 12-hour API window in the
upgrade instructions.

Verified against a mock Akamai SIEM API only: 2.4M events ingested end
to end with no failure store entries, pipeline errors, or mapping
conflicts, and event timestamps preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

✅ All changelog entries have the correct PR link.

@ShourieG ShourieG changed the title [akamai] - Replace CEL input with OpenTelemetry akamaisiemreceiver Aug 10, 2026
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @ShourieG

@ShourieG
ShourieG merged commit 3157545 into elastic:main Aug 10, 2026
13 checks passed
@ShourieG
ShourieG deleted the feature/akamaisiem_otel branch August 10, 2026 13:28
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

Package akamai - 4.0.0 containing this change is available at https://epr.elastic.co/package/akamai/4.0.0/

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

Labels

breaking change docs documentation Improvements or additions to documentation. Applied to PRs that modify *.md files. enhancement New feature or request Integration:akamai Akamai Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

8 participants