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

EcosystemCodeArtifact formatInstalls through firewallScanned and enforcedStatus
npmnpmYesYesReady to deploy
MavenmavenYesYesReady to deploy
RubyGemsrubyYesYesReady to deploy
PyPIpypiMetadata onlyYesIn development
NuGetnugetYesValidation in progressConfigure and verify
CargocargoYesValidation in progressConfigure and verify
Gononen/an/aUse a public-registry route
Condanonen/an/aUse 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:list and entitlements:list, on an org with the firewall entitlement
  • An AWS CodeArtifact domain and repositories, plus an IAM principal that can call codeartifact:GetAuthorizationToken and sts: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-store

Get 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 text

Endpoints 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: 65536

The Socket API token comes from SOCKET_SECURITY_API_TOKEN, never the config file.

Route Path Reference

Firewall registryCodeArtifact endpoint pathExample 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 registry value is rubygems, 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: cargo

Both 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 /download suffix or .crate extension 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.pem

The 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.

EcosystemAuth form
npmAuthorization: Bearer <token>, via _authToken
MavenHTTP Basic, username aws, password <token>
RubyGemsHTTP Basic, username aws, password <token>
NuGetHTTP Basic, username aws, password <token>
CargoAuthorization: <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.crt

RubyGems

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.pem

Verifying 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.1

Requesting 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 -5

Each 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" ...          # Maven

Troubleshooting

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.


Did this page help you?