In real-world security environments, “simple” rarely means simple. As a technical consultant, I often see Elastic Defend deployed in infrastructures that are anything but standard: segmented networks, restricted outbound connectivity, compliance-driven isolation, and operational constraints that make direct internet access impossible for managed endpoints.
One recurring requirement in these environments is the local delivery of Elastic Endpoint artifacts. Elastic documents the general approach for offline endpoints and air-gapped environments here:
In this article, I want to focus on a practical implementation variant in NetEye-based installations: providing Elastic Endpoint artifacts locally through a NetEye Satellite (that already runs the nginx-satellite service).
Rather than building a separate web infrastructure just for artifact delivery, we can use the existing HTTPS service on the satellite and expose the required files in a clean, maintainable way.
This is somehow a ‘sister’ blog of Enabling Elastic Agents Upgrades in Restricted or Closed Networks.
Elastic Endpoint components need access to artifact updates. In connected environments, these are typically retrieved from Elastic-managed infrastructure. In restricted or air-gapped setups, however, this model does not work.
That means we need to:
For NetEye Satellites, this is a natural fit because nginx-satellite is already available and can serve as the local HTTPS endpoint.
In this setup, the NetEye Satellite serves the Elastic Defend artifacts through its existing web server.
The resulting URL looks like this:
https://satname/neteyeshare/defendartifacts
This path points to:
/usr/share/nginx/html/neteyeshare/defendartifacts
So from an implementation perspective, the satellite becomes the local artifact repository exposed over HTTPS.
Before starting, make sure the following prerequisites are met:
nginx-satellite service is active,The key assumption for this blog post is that nginx-satellite is already serving content successfully, and we are extending that setup for Elastic Defend artifacts.
The directory used for publishing the files is:
/usr/share/nginx/html/neteyeshare/defendartifacts
This is exposed through the URL:
https://satname/neteyeshare/defendartifacts
This URL (substitute satname with the hostname of your satellite) has to be used as described by elastic defend documentation:
Set the
advanced.artifacts.global.base_urladvanced setting for each Elastic Defend integration policy that needs to use the mirror. Note that there’s a separate setting for each operating system:
linux.advanced.artifacts.global.base_urlmac.advanced.artifacts.global.base_urlwindows.advanced.artifacts.global.base_urlIf the directory does not yet exist, create it and ensure the files placed there are readable by nginx.
Download the artifacts periodically
A local artifact repository is only useful if it is kept current. This means the artifacts have to be downloaded periodically onto the satellites.
A practical example is:
export ENDPOINT_VERSION=9.3.0 && wget -P downloads/endpoint/manifest https://artifacts.security.elastic.co/downloads/endpoint/manifest/artifacts-$ENDPOINT_VERSION.zip && zcat -q downloads/endpoint/manifest/artifacts-$ENDPOINT_VERSION.zip | jq -r '.artifacts | to_entries[] | .value.relative_url' | xargs -I@ curl "https://artifacts.security.elastic.co@" --create-dirs -o ".@"
Please note that the version should be matching the version of your elastic agents. That version usually corresponds with the version of the elasticsearch installed on your NetEye cluster, so use the following command on elasticsearch nodes, to find out your version:
/usr/share/neteye/elasticsearch/scripts/es_curl.sh -s https://elasticsearch.neteyelocal:9200/ | jq .version.number -r
In practice, you will usually wrap this into an operational procedure or scheduled job so that each satellite receives the required artifact set regularly.
The key point is simple: hosting artifacts locally is not a one-time task. It is an ongoing synchronization responsibility.
After downloading or updating the artifacts, permissions should be fixed so that nginx can actually serve the files on the satellite.
A common approach is to ensure the files and directories are readable and traversable as needed, for example:
chown -R +u+r /usr/share/nginx/html/neteyeshare/defendartifacts
or, where directory traversal permissions are also required:
chown -R +u+rx /usr/share/nginx/html/neteyeshare/defendartifacts
Operationally, the important point is not the exact command variant used in every environment, but that ownership and permissions must be corrected consistently after artifact updates. Otherwise, the files may exist on disk but still not be retrievable through nginx-satellite.
This is the part that deserves special emphasis.
When implementing local artifact hosting for Elastic Endpoint, correct handling of the ETag header is not a cosmetic improvement. It is a critical part of making the locally served artifacts behave as expected.
In NetEye Satellite setups, the main implementation detail is that the ETag generated by nginx may need to be transformed before being returned to the client. If this is overlooked, the endpoint may retrieve artifacts incorrectly or fail to validate them the way the integration expects.
In other words: if you only remember one technical detail from this article, let it be this one – pay close attention to the ETag header and how it is calculated.
Create a file next to the default HTTPS nginx configuration, for example:
map_elastic_etag.conf
Use the following content:
map $sent_http_etag $elastic_etag {
"~(.*)-(.*)" "$1$2";
}
This mapping is the key piece.
What it does is take the ETag value generated by nginx and transform it by removing the separator between the two captured groups. In practical terms, nginx may produce an ETag in a format that is not ideal for this use case, and the map rewrites it into the form expected by the Elastic Endpoint artifact workflow.
That means the calculation and delivery of the ETag are explicitly controlled, instead of just relying on the default behavior.
Next, create a location snippet in the nginx locations directory, for example:
defendartifacts_location.conf
Use content similar to the following:
location /neteyeshare/defendartifacts {
add_header ETag "$elastic_etag";
}
This ensures that requests to the artifact path return the adjusted ETag header.
Again, this deserves emphasis: the location itself is simple, but its purpose is essential. It does not merely expose the files; it makes sure the ETag returned for artifact requests is the mapped one, not just the unmodified default.
Depending on the local nginx structure in your environment, you may want to extend this snippet with additional directives such as autoindex off, explicit MIME handling, or access restrictions. But for the core use case, the critical point is that the location serving /neteyeshare/defendartifacts includes the rewritten ETag header.
After the configuration changes, all satellites should do:
restart service nginx-satellite
This is a straightforward but important operational step. Without restarting the service, the new mapping and location configuration will not become active.
From a consultant’s perspective, this approach is especially useful because it fits neatly into existing NetEye Satellite designs:
In complex customer environments, reducing moving parts is often more valuable than designing a theoretically perfect but operationally heavy solution. Reusing nginx-satellite keeps the architecture understandable and supportable.
After creating the directory, downloading artifacts, fixing permissions, and adding the nginx snippets, validate the setup carefully.
Recommended checks include:
nginx-satellite,https://satname/neteyeshare/defendartifacts is reachable,ETag.A simple test with curl -I from a system that can reach the satellite is usually enough to validate the header behavior.
For example, you should verify that:
A few operational points are worth keeping in mind:
Implementing Elastic’s offline endpoint artifact delivery in a NetEye environment is not just about reproducing vendor documentation. In practice, it is about adapting the concept to the customer’s existing architecture in a way that is robust, simple, and supportable.
When a NetEye Satellite already provides HTTPS via nginx-satellite, using it to publish Elastic Defend artifacts is an efficient solution. The required setup is relatively small:
/neteyeshare/defendartifacts,For complex and segmented environments, this gives you a pragmatic way to provide Elastic Endpoint artifacts locally without introducing unnecessary infrastructure.
If you are implementing this in a customer setup, my recommendation is straightforward: keep it simple, validate headers carefully, and treat ETag calculation as a core part of the solution rather than a minor nginx detail.