---
updatedAt: 2026-09-02T14:32:35.000Z
agentTools:
  projectIndex: https://docs.socket.dev/llms.txt
---

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

> :memo: **CodeArtifact support is experimental.** npm, Maven, and RubyGems are fully scanned and enforced today, and ecosystem coverage is actively expanding. Start with [Ecosystem Support](#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](https://docs.socket.dev/docs/socket-firewall-enterprise-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](#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](https://docs.socket.dev/docs/socket-firewall-enterprise-registry-mode-setup-downstream). 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](https://docs.socket.dev/docs/socket-firewall-enterprise-registry-mode-upstream-deployment-guide) 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](#certificates))
* The firewall reachable on port **443** (see [Port Requirement](#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.

```bash
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.

```bash
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:**

```yaml
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 `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`       |

> :memo: 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.

```yaml
# 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:

```yaml
    # 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.

> :memo: 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](#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:

```bash
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](https://docs.socket.dev/docs/socket-firewall-enterprise-proxy-mode-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:

```bash
export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
  --domain my-domain --query authorizationToken --output text)
```

> :warning: 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

```ini
# .npmrc
registry=https://firewall.company.com/npm/
//firewall.company.com/npm/:_authToken=${CODEARTIFACT_AUTH_TOKEN}
cafile=/path/to/internal-ca.crt
```

> :memo: Do not set `always-auth=true`. Older AWS documentation recommends it, but npm 11 rejects it as an unknown config.

### Maven

```xml
<!-- 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:

```bash
keytool -importcert -trustcacerts -noprompt -cacerts -storepass changeit \
  -alias socket-firewall-ca -file /path/to/internal-ca.crt
```

### RubyGems

```bash
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:

```bash
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. `shell-quote@1.6.1` 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:

```bash
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 shell-quote@1.6.1.
```

> :memo: 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:

```bash
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:

```bash
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](#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](#ecosystem-support). Point PyPI clients directly at your CodeArtifact endpoint, or use [Wrapper Mode](https://docs.socket.dev/docs/socket-firewall-enterprise-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](#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](#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](#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.