AWS CodeArtifact Configuration
AWS CodeArtifact Integration Guide
Guide for putting Socket Registry Firewall in front of AWS CodeArtifact so packages are scanned before they reach developers and CI.
📝 CodeArtifact support is experimental. npm, Maven, and RubyGems are fully scanned and enforced today, and ecosystem coverage is actively expanding. Start with Ecosystem Support to see what each ecosystem does today so you can scope your rollout confidently.
Overview
CodeArtifact is a managed private registry that proxies public registries through an "external connection". Socket Registry Firewall sits between your developers and CodeArtifact, inspects each package version, and blocks the ones your Socket security policy rejects.
CodeArtifact keeps its own public upstreams. The firewall does not replace them, it inspects what crosses it.
Ecosystem Support
| Ecosystem | CodeArtifact format | Installs through firewall | Scanned and enforced | Status |
|---|---|---|---|---|
| npm | npm | Yes | Yes | Ready to deploy |
| Maven | maven | Yes | Yes | Ready to deploy |
| RubyGems | ruby | Yes | Yes | Ready to deploy |
| PyPI | pypi | Metadata only | Yes | In development |
| NuGet | nuget | Yes | Validation in progress | Configure and verify |
| Cargo | cargo | Yes | Validation in progress | Configure and verify |
| Go | none | n/a | n/a | Use a public-registry route |
| Conda | none | n/a | n/a | Use a public-registry route |
npm, Maven, and RubyGems are ready to deploy today. Packages are scanned against your Socket security policy, and disallowed versions are filtered out of the metadata your resolvers see. Start here.
PyPI. Metadata filtering is working, so disallowed versions are already hidden from the resolver. Artifact delivery through the firewall is still being finalized, so for now point PyPI clients directly at your CodeArtifact endpoint and add the firewall route once support lands. Wrapper Mode is a good option if you want PyPI packages scanned against CodeArtifact in the meantime.
NuGet and Cargo. Socket scans both ecosystems, and the firewall supports both as registry kinds. Packages install cleanly through the firewall once configured as shown below. Validation of policy enforcement specifically against CodeArtifact endpoints is still being completed, so confirm in your own environment that decisions are being recorded before you rely on these two for coverage. Verifying Enforcement shows how to check in a couple of minutes.
Go and Conda. CodeArtifact does not offer a repository format for either one, so there is no CodeArtifact endpoint to sit in front of. CodeArtifact provides npm, pypi, maven, nuget, cargo, ruby, swift, and generic. To get Go or Conda scanned, point those clients at the public registries through the firewall using a standard route from the Downstream Deployment Guide. Both ecosystems are fully supported that way.
Deployment Topology
CodeArtifact uses the downstream topology, with the firewall in front of your CodeArtifact endpoints.
Developer / CI ---> Socket Firewall ---> CodeArtifact ---> Public Registry
(this guide) (external connection)
Why Downstream Is the Right Fit
Upstream deployment asks the private registry to fetch through the firewall. Artifactory and Nexus allow a remote repository to point at any URL, so the firewall can be named as their upstream. CodeArtifact takes a different approach: external connections reference a fixed set of AWS-defined registries by name rather than by URL.
$ aws codeartifact associate-external-connection \
--domain my-domain --repository npm-store \
--external-connection https://firewall.company.com/npm
ValidationException: Value 'https://firewall.company.com/npm' at 'externalConnection'
failed to satisfy constraint: Member must satisfy regular expression pattern:
[A-Za-z0-9][A-Za-z0-9._\-:]{1,99}
The accepted values are public:npmjs, public:pypi, public:maven-central, public:maven-googleandroid, public:maven-gradleplugins, public:maven-clojars, public:maven-commonsware, public:nuget-org, public:ruby-gems-org, and public:crates-io.
Middle deployment relies on the same mechanism, so downstream is the topology to use here.
Defining Routes
Routes for CodeArtifact are defined explicitly rather than discovered. The private_registry auto-discovery block covers artifactory and nexus; for CodeArtifact, declare one route per ecosystem as shown below. Endpoints are stable, so this is a one-time setup.
Prerequisites
- Docker and Docker Compose on the firewall host
- Socket.dev API key with scopes
packages:listandentitlements:list, on an org with the firewall entitlement - An AWS CodeArtifact domain and repositories, plus an IAM principal that can call
codeartifact:GetAuthorizationTokenandsts:GetServiceBearerToken - DNS for the firewall hostname, and a TLS certificate for it (see Certificates)
- The firewall reachable on port 443 (see Port Requirement)
Step 1: Prepare CodeArtifact
A two-tier layout keeps the public mirror separate from what developers consume, so you can add internal packages later without changing client config.
export AWS_REGION=us-east-1
DOMAIN=my-domain
aws codeartifact create-domain --domain "$DOMAIN"
# Store repos hold the public external connection. One connection per repo.
for r in npm-store pypi-store maven-central nuget-store cargo-store ruby-store; do
aws codeartifact create-repository --domain "$DOMAIN" --repository "$r"
done
aws codeartifact associate-external-connection --domain "$DOMAIN" \
--repository npm-store --external-connection public:npmjs
aws codeartifact associate-external-connection --domain "$DOMAIN" \
--repository maven-central --external-connection public:maven-central
aws codeartifact associate-external-connection --domain "$DOMAIN" \
--repository ruby-store --external-connection public:ruby-gems-org
aws codeartifact associate-external-connection --domain "$DOMAIN" \
--repository nuget-store --external-connection public:nuget-org
aws codeartifact associate-external-connection --domain "$DOMAIN" \
--repository cargo-store --external-connection public:crates-io
# Team repos are what clients point at, with the store repo as upstream.
aws codeartifact create-repository --domain "$DOMAIN" --repository team-npm \
--upstreams repositoryName=npm-store
aws codeartifact create-repository --domain "$DOMAIN" --repository team-maven \
--upstreams repositoryName=maven-central
aws codeartifact create-repository --domain "$DOMAIN" --repository team-ruby \
--upstreams repositoryName=ruby-storeGet each endpoint. The --format value is CodeArtifact's own name for the ecosystem, which is not always the same as the firewall's registry value.
aws codeartifact get-repository-endpoint --domain "$DOMAIN" \
--repository team-npm --format npm --query repositoryEndpoint --output textEndpoints take the form:
https://<domain>-<account-id>.d.codeartifact.<region>.amazonaws.com/<format>/<repo>/
Step 2: Configure the Firewall
socket.yml:
socket:
api_url: https://api.socket.dev
api_read_timeout: 60
fail_open: false # block installs when the Socket API is unreachable
fail_open_unscanned: true # allow versions Socket has not scanned yet
ports:
http: 8080
https: 8443
ssl:
cert: /etc/nginx/ssl/fullchain.pem
key: /etc/nginx/ssl/privkey.pem
path_routing:
enabled: true
domain: firewall.company.com
config_mode: downstream
client_rewrite_scheme: https
routes:
- path: /npm
upstream: https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/npm/team-npm
registry: npm
- path: /maven
upstream: https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/maven/team-maven
registry: maven
- path: /rubygems
upstream: https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/ruby/team-ruby
registry: rubygems
metadata_filtering:
enabled: true
include_unchecked_versions: true
max_versions: 30
redis:
enabled: false # enable when running two or more instances
nginx:
worker_processes: auto
worker_connections: 32768
worker_rlimit_nofile: 65536The Socket API token comes from SOCKET_SECURITY_API_TOKEN, never the config file.
Route Path Reference
Firewall registry | CodeArtifact endpoint path | Example upstream suffix |
|---|---|---|
npm | /npm/<repo> | /npm/team-npm |
maven | /maven/<repo> | /maven/team-maven |
rubygems | /ruby/<repo> | /ruby/team-ruby |
pypi | /pypi/<repo> | /pypi/team-pypi |
nuget | /nuget/<repo> | /nuget/team-nuget |
cargo | /cargo/<repo> | /cargo/team-cargo |
📝 RubyGems is the one name mismatch worth remembering. The firewall's
registryvalue isrubygems, while CodeArtifact's path segment is/ruby/.
Port Requirement
Terminate the firewall on 443.
The firewall rewrites absolute URLs that CodeArtifact returns, and on a non-default port the rewritten URL omits the port, so clients get a connection refused. npm is affected because CodeArtifact returns absolute tarball URLs in the packument. Put the firewall behind a load balancer on 443, or publish the container on 443 directly.
# Container listens on 8443 internally, published on host 443
ports:
- "443:8443"Route Prefix for NuGet and Cargo
When you add NuGet or Cargo routes, the route path must match the CodeArtifact path exactly, including the repository name:
# Correct for NuGet and Cargo
- path: /nuget/team-nuget
upstream: https://...amazonaws.com/nuget/team-nuget
registry: nuget
- path: /cargo/team-cargo
upstream: https://...amazonaws.com/cargo/team-cargo
registry: cargoBoth ecosystems advertise absolute URLs in their service documents (NuGet's v3/index.json resources, Cargo's config.json dl template). The firewall rewrites the hostname in those documents but keeps the upstream path, so a shorter local prefix produces a 404. Making the prefix identical to the upstream path avoids the mismatch.
📝 CodeArtifact serves Cargo downloads at
/crates/<name>/<version>, without the/downloadsuffix or.crateextension used by crates.io. Confirm decisions are being recorded for Cargo in your environment, as described in Verifying Enforcement.
Certificates
Mount your own certificate. Do not rely on the one the firewall generates when none is provided.
The generated certificate is marked as a certificate authority and carries no serverAuth extended key usage, so pip and other strict clients reject it:
SSLCertVerificationError('"host" certificate is not permitted for this usage')
Issue a leaf certificate from your internal CA:
cat > leaf.ext <<'EOF'
basicConstraints=critical,CA:FALSE
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:firewall.company.com
EOF
openssl req -newkey rsa:2048 -nodes -keyout privkey.pem -out leaf.csr \
-subj "/CN=firewall.company.com"
openssl x509 -req -in leaf.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out leaf.crt -days 825 -sha256 -extfile leaf.ext
cat leaf.crt ca.crt > fullchain.pemThe SAN must list every hostname clients use. Distribute your CA certificate to clients, or add it to each host's trust store. See Generating Keys for more on certificate handling.
Step 3: Client Configuration
Every client authenticates to the firewall with a CodeArtifact authorization token, and the firewall forwards it upstream. Get one with:
export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
--domain my-domain --query authorizationToken --output text)⚠️ CodeArtifact tokens last a maximum of 12 hours and cannot be refreshed in place. Generate client configuration at build time and never commit it. Expect a daily re-auth on developer machines.
Each ecosystem passes the token differently. This trips people up, so it is worth reading the table before configuring anything.
| Ecosystem | Auth form |
|---|---|
| npm | Authorization: Bearer <token>, via _authToken |
| Maven | HTTP Basic, username aws, password <token> |
| RubyGems | HTTP Basic, username aws, password <token> |
| NuGet | HTTP Basic, username aws, password <token> |
| Cargo | Authorization: <token> with no scheme. A Bearer prefix returns 401 |
npm
# .npmrc
registry=https://firewall.company.com/npm/
//firewall.company.com/npm/:_authToken=${CODEARTIFACT_AUTH_TOKEN}
cafile=/path/to/internal-ca.crt📝 Do not set
always-auth=true. Older AWS documentation recommends it, but npm 11 rejects it as an unknown config.
Maven
<!-- settings.xml -->
<settings>
<servers>
<server>
<id>socket-firewall</id>
<username>aws</username>
<password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
</server>
</servers>
<mirrors>
<mirror>
<id>socket-firewall</id>
<url>https://firewall.company.com/maven/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>Java uses its own trust store, so import your CA:
keytool -importcert -trustcacerts -noprompt -cacerts -storepass changeit \
-alias socket-firewall-ca -file /path/to/internal-ca.crtRubyGems
gem sources --remove https://rubygems.org/
gem sources --add "https://aws:${CODEARTIFACT_AUTH_TOKEN}@firewall.company.com/rubygems/"Ruby reads SSL_CERT_FILE, so point it at a bundle containing your CA:
export SSL_CERT_FILE=/path/to/ca-bundle-including-internal-ca.pemVerifying Enforcement
Confirm the firewall is inspecting packages rather than only proxying them. Use a package that carries an alert your security policy blocks. [email protected] carries a critical CVE and works well as a canary if your policy sets criticalCVE to error.
With metadata filtering on, a blocked version is hidden rather than refused, so a range resolves to an allowed version:
npm install 'shell-quote@^1.6.0'
# resolves to 1.10.0, not the blocked 1.6.1Requesting the blocked version directly reports it as nonexistent:
npm error code ETARGET
npm error notarget No matching version found for [email protected].
📝 That message is expected behavior, not a bug. Metadata filtering removes disallowed versions from the metadata the resolver sees, so the client genuinely cannot see the version. It exists and is blocked.
Check the firewall logs to confirm a decision was recorded for the right ecosystem:
docker logs <container> 2>&1 | grep SOCKET_DECISION | tail -5Each decision names the ecosystem in repo, the package in purl, and the outcome in decision. If you see no decisions for an ecosystem after a clean install, the firewall is not evaluating it.
Always control the client cache when testing. A cached package never crosses the network, so a blocked package can appear to install:
export npm_config_cache="$(mktemp -d)" # npm
pip install --no-cache-dir ... # pip
mvn -Dmaven.repo.local="$PWD/repo" ... # MavenTroubleshooting
npm fails with ECONNREFUSED on a tarball URL
The firewall is not on port 443. Rewritten tarball URLs omit a non-default port. See Port Requirement.
pip reports a hash mismatch or possible tampering
ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE.
... someone may have tampered with them.
Your packages are fine. This is the PyPI artifact delivery work described in Ecosystem Support. Point PyPI clients directly at your CodeArtifact endpoint, or use Wrapper Mode, and add the firewall route once PyPI support lands.
NuGet reports NU1101, or Cargo 404s on download
The route path does not match the upstream CodeArtifact path. See Route Prefix for NuGet and Cargo.
Cargo returns 401
Cargo's token must be sent with no scheme. Set token = "<token>" in .cargo/config.toml without a Bearer prefix.
Everything returns 404
The request's Host header does not match path_routing.domain, which becomes the nginx server_name. Clients must reach the firewall by the configured hostname.
401 versus 403
- 401 comes from CodeArtifact and means the authorization token is wrong or expired. Regenerate it.
- 403 comes from Socket Firewall and means a package was blocked by policy.
That split is the fastest first question in any triage.
A package installed that should have been blocked
Check three things in order: that the client cache was empty, that a SOCKET_DECISION line exists for that ecosystem, and that the ecosystem shows Yes under "Scanned and enforced" in Ecosystem Support. If an install succeeds with no decision line for its ecosystem, the request path is not being matched as an artifact request. Share the request path with support and route that ecosystem directly at CodeArtifact until it is resolved.
What to Expect Today
A quick recap so you can scope a rollout:
- npm, Maven, and RubyGems are scanned and enforced. Deploy these with confidence.
- PyPI filters metadata today, with artifact delivery in development. Point PyPI clients at CodeArtifact directly for now.
- NuGet and Cargo install through the firewall. Verify enforcement in your environment before relying on it, using the check in Verifying Enforcement.
- Go and Conda are fully supported against the public registries, since CodeArtifact offers no format for them.
- Routes are declared explicitly rather than auto-discovered.
- Use the downstream topology, terminate on port 443, and mount your own certificate.
- CodeArtifact tokens last up to 12 hours, so generate client configuration at build time.
Coverage is expanding. If a specific ecosystem is blocking your rollout, let your Socket contact know so it can be prioritized.
Updated about 16 hours ago