Enterprise-grade security proxy that protects your package registries (npm, PyPI, Maven, Cargo, RubyGems, OpenVSX, NuGet, Go) by scanning packages with Socket's security API in real-time to block malicious packages before they reach your systems.
- npm (JavaScript/Node.js) -
registry.npmjs.org - PyPI (Python) -
pypi.org - Maven (Java) -
repo1.maven.org - Cargo (Rust) -
crates.io - RubyGems (Ruby) -
rubygems.org - OpenVSX (VS Code Extensions) -
open-vsx.org - NuGet (.NET) -
nuget.org - Go (Go Modules) -
proxy.golang.org - Conda (Python/R/etc.) -
repo.anaconda.com/pkgs/main(treated as PyPI until native support)
✅ Real-time Security - Blocks malicious packages before installation
✅ Multi-Registry - Protects all 8 major package ecosystems
✅ Flexible Routing - Domain-based or path-based routing
✅ Auto-Discovery - Sync routes from Artifactory/Nexus automatically
✅ High Performance - Intelligent caching with Redis support
✅ Enterprise Ready - Outbound proxy, custom CAs, Splunk logging
✅ Zero Config - Works with public registries out-of-the-box
Socket Registry Firewall can be deployed in two ways:
- Pre-built Docker Image (Recommended) - Pull and run the official image
- Tarball Installation - Build your own image using the firewall tarball (for air-gapped or custom environments)
- Sign up at Socket.dev
- Go to Settings → API Keys
- Create API key with scopes:
packages,entitlements:list
# Create .env file with your Socket API token
cat > .env <<EOF
SOCKET_SECURITY_API_TOKEN=your-api-key-here
EOFOr export it in your shell:
export SOCKET_SECURITY_API_TOKEN=your-api-key-hereCreate a docker-compose.yml:
services:
socket-firewall:
image: socketdev/socket-registry-firewall:latest
ports:
- "8080:8080" # HTTP (redirects to HTTPS)
- "8443:8443" # HTTPS
environment:
# Required: Socket.dev API token
- SOCKET_SECURITY_API_TOKEN=${SOCKET_SECURITY_API_TOKEN}
volumes:
# Configuration file
- ./socket.yml:/app/socket.yml:ro
# SSL certificates directory
- ./ssl:/etc/nginx/ssl
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-fk", "https://localhost:8443/health"]
interval: 30s
timeout: 10s
retries: 3Create a minimal socket.yml:
# Minimal configuration - uses defaults for all public registries
# Access registries at: https://localhost:8443/npm/, /pypi/, /maven/, etc.
socket:
api_url: https://api.socket.dev
# Set internal container ports the firewall will bind to
ports:
http: 8080
https: 8443
path_routing:
enabled: true
domain: sfw.your_company.com
routes:
- path: /npm
upstream: https://registry.npmjs.org
registry: npm
# Optional: Customize performance settings
nginx:
worker_processes: 2
worker_connections: 4096sudo sh -c 'printf "127.0.0.1 sfw.your_company.com\n::1 sfw.your_company.com\n" >> /etc/hosts'
docker pull socketdev/socket-registry-firewall
docker compose up -dThat's it! The firewall is now protecting the npm registry at http://sfw.your_company.com:8080/npm/.
For air-gapped environments, custom base images, or when you need to build your own container, Socket provides the firewall as a tarball that can be extracted into any OpenResty-based image.
- Air-gapped environments - No access to Docker Hub
- Custom base images - Need specific OpenResty version or OS distribution
- Security requirements - Must build from source in your own registry
- Custom modifications - Need to add additional tools or configurations
- Obtain the tarball from Socket (e.g.,
socket-firewall-1.1.94.arm64.tgz) - OpenResty base image - Compatible with
openresty/openresty:1.29.2.4-alpineor similar
Create a Dockerfile in your project directory:
FROM openresty/openresty:1.29.2.4-alpine
# Copy and extract the Socket Firewall tarball
COPY socket-firewall-1.1.94.arm64.tgz /app/install/socket-firewall-1.1.94.arm64.tgz
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh \
&& tar -xzf /app/install/socket-firewall-1.1.94.arm64.tgz -C /
# Install basic dependencies
RUN apk add --no-cache curl ca-certificates git openssl bash && \
# Prefer IPv4 over IPv6 to avoid upstream IPv6 connection attempts
printf 'precedence ::ffff:0:0/96 100\n' >> /etc/gai.conf || true
# Install lua-resty libraries
RUN cd /tmp && \
# Install lua-resty-http
git clone https://github.com/ledgetech/lua-resty-http.git && \
cd lua-resty-http && \
cp -r lib/resty/* /usr/local/openresty/lualib/resty/ && \
cd /tmp && \
# Install lua-resty-openssl (needed for HTTPS)
git clone https://github.com/fffonion/lua-resty-openssl.git && \
cd lua-resty-openssl && \
cp -r lib/resty/* /usr/local/openresty/lualib/resty/ && \
cd /tmp && \
# Install lua-resty-redis (needed for Redis caching)
git clone https://github.com/openresty/lua-resty-redis.git && \
cd lua-resty-redis && \
cp lib/resty/redis.lua /usr/local/openresty/lualib/resty/ && \
cd / && \
rm -rf /tmp/lua-resty-http /tmp/lua-resty-openssl /tmp/lua-resty-redis
WORKDIR /app
ENTRYPOINT ["/app/entrypoint.sh"]Note: Adjust the tarball filename to match your version and architecture (e.g., socket-firewall-1.1.94.amd64.tgz for x86_64).
Download the entrypoint.sh script from Socket (provided with the tarball package) or request it from Socket support.
Place it in your project directory and make it executable:
chmod +x entrypoint.shThe entrypoint script handles:
- Configuration generation using
socket-proxy-config-tool - Environment variable loading
- Nginx configuration validation
- Auto-discovery daemon startup (if configured)
- Nginx process management
Create a docker-compose.yml for the tarball-based installation:
services:
socket-firewall:
build:
context: .
dockerfile: Dockerfile
image: socketdev/socket-registry-firewall-tar:latest
ports:
- "8085:8080" # HTTP (redirects to HTTPS)
- "8445:8443" # HTTPS
environment:
# Required: Socket.dev API token
- SOCKET_SECURITY_API_TOKEN=${SOCKET_SECURITY_API_TOKEN}
volumes:
# Configuration file
- ./socket.yml:/app/socket.yml:ro
# SSL certificates directory
- ./ssl:/etc/nginx/ssl
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-fk", "https://localhost:8443/health"]
interval: 30s
timeout: 10s
retries: 1Note: Different ports (8085/8445) are used in this example to avoid conflicts if running both methods simultaneously.
# Build the image
docker compose build
# Start the firewall
docker compose up -d
# View logs
docker compose logs -f socket-firewallThe Socket Firewall tarball includes:
/usr/local/bin/socket-proxy-config-tool- Configuration generation tool/usr/local/openresty/lualib/socket/*.lua- Lua modules for package parsing and security checks/usr/local/openresty/nginx/conf/snippets/- Nginx configuration snippets- Supporting files for all 8 ecosystems (npm, PyPI, Maven, Cargo, RubyGems, OpenVSX, NuGet, Go)
The tarball extracts to standard OpenResty paths, making it compatible with any OpenResty-based image.
When using tarball installation, you can:
- Use different base images: Change the
FROMline to use specific OpenResty versions or distributions - Add custom tools: Install additional packages in the RUN command
- Modify entrypoint: Customize the entrypoint script for your environment
- Layer scanning: Add image scanning tools in your build pipeline
- Internal registries: Push built images to your private container registry
After starting the firewall, verify it's working:
# Check health endpoint
curl -k https://localhost:8445/health
# Expected response:
{"status":"healthy","version":"1.1.94"}
# View startup logs
docker compose logs socket-firewall | grep -i "socket firewall"Both the pre-built image and tarball installation use the same socket.yml configuration file format.
Test npm:
# Configure npm to use the firewall
npm config set registry http://sfw.your_company.com:8080/npm/
npm config set strict-ssl false # Only for self-signed certs
# Install a package
npm install lodash --loglevel verbose
# Try to install a malicious package and watch Socket Firewall block the package.
npm install lodahs- Add more ecosystems into the
socket.yml, then test with these common language samples below.
Test pip:
# Configure pip to use the firewall
pip config set global.index-url https://localhost:8443/pypi/simple
pip config set global.trusted-host "localhost"
# Install a package
pip install requestsTest Maven:
# Add to ~/.m2/settings.xml
cat > ~/.m2/settings.xml <<'EOF'
<settings>
<mirrors>
<mirror>
<id>socket-firewall</id>
<url>https://localhost:8443/maven</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>
EOF
# Build your project
mvn install -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=trueTest Gradle:
edit your settings.gradle:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) // or FAIL_ON_PROJECT_REPOS
repositories {
maven { url "https://localhost:8443/maven" }
}
}Create socket.yml for custom domains:
registries:
npm:
domains:
- npm.company.com
pypi:
domains:
- pypi.company.com
maven:
domains:
- maven.company.comSingle domain with path prefixes:
path_routing:
enabled: true
domain: firewall.company.com
routes:
- path: /npm
upstream: https://registry.npmjs.org
registry: npm
- path: /pypi
upstream: https://pypi.org
registry: pypi
- path: /maven
upstream: https://repo1.maven.org/maven2
registry: mavenA path-routing deployment normally answers only to domain, and a request whose
Host doesn't match falls through to the default server and 404s. That becomes a
problem when you run the firewall in several clusters behind a load balancer: the
LB forwards requests to a pod under a per-cluster hostname, which is not the
stable LB hostname. Two options let you handle this:
path_routing:
enabled: true
domain: socket-firewall-registry.apps.company.com # stable LB hostname
# (A) allowed_domain: additional hostnames this server answers to, beyond
# `domain`. Without it, a Host that doesn't match `domain` 404s. Entries may be
# exact names, nginx wildcards (*.cluster.company.com), or a regex (~^.+$) for a
# true catch-all.
allowed_domain:
- socket-firewall-registry.gateway.unified-30.internal.api.company.com
- "*.unified-clusters.internal.api.company.com"
# (B) use_incoming_domain: which host package URLs are rewritten to.
# false (default) -> rewrite using `domain` (the stable LB hostname), so
# follow-up downloads route back through the load balancer and spread across
# clusters even though the request arrived on a per-cluster Host.
# true -> rewrite using the incoming Host header.
use_incoming_domain: false
routes:
- path: /npm
upstream: https://registry.npmjs.org
registry: npmallowed_domain defaults to empty and use_incoming_domain to false. For a
single-domain deployment the incoming Host already equals domain, so the
defaults reproduce today's behavior. For the load-balancer case above,
allowed_domain stops the 404s and the default use_incoming_domain: false makes
the firewall advertise the LB hostname (domain) in rewritten URLs so downloads
balance across clusters rather than pinning to the cluster that served the metadata.
Automatically sync repository routes:
path_routing:
enabled: true
domain: firewall.company.com
mode: nexus # or 'artifactory'
private_registry:
api_url: https://nexus.company.com
api_key: your-nexus-api-token
interval: 5m # Auto-sync every 5 minutes
# Optional filters
include_pattern: ".*"
exclude_pattern: "(tmp|test)-.*"Routes update automatically when you add/remove repositories - no manual configuration!
See docs/AUTO-DISCOVERY.md for details.
The firewall auto-generates self-signed certificates on first run. Trust them:
macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ssl/fullchain.pemLinux:
sudo cp ssl/fullchain.pem /usr/local/share/ca-certificates/socket-firewall.crt
sudo update-ca-certificatesPlace your certificates in the ssl/ directory:
cp /path/to/cert.pem ssl/fullchain.pem
cp /path/to/key.pem ssl/privkey.pemmkdir -p ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ssl/privkey.pem \
-out ssl/fullchain.pem \
-subj "/CN=*.company.com" \
-addext "subjectAltName=DNS:firewall.company.com,DNS:npm.company.com,DNS:pypi.company.com"Route all upstream traffic through a corporate proxy:
socket:
outbound_proxy: http://proxy.company.com:3128
no_proxy: localhost,127.0.0.1,internal.company.comsocket:
outbound_proxy: http://proxy.company.com:3128
# Verify SSL with corporate CA (default: false)
api_ssl_verify: true
api_ssl_ca_cert: /path/to/corporate-ca.crt
# Apply same CA to upstream registries (defaults to api_ssl_verify)
# (or set upstream_ssl_verify separately if different)For distributed deployments:
redis:
enabled: true
host: redis.company.com
port: 6379
password: your-redis-password
ttl: 86400 # 24 hours# Match worker_processes to CPU cores
worker_processes: 4
worker_connections: 8192
proxy:
connect_timeout: 60
send_timeout: 60
read_timeout: 60socket:
fail_open: true # Allow packages if Socket API is down (default)
# fail_open: false # Block all packages if Socket API is downOverride configuration via environment variables:
# Core settings
SOCKET_SECURITY_API_TOKEN=your-api-key # Required
SOCKET_API_URL=https://api.socket.dev # Default
SOCKET_CACHE_TTL=600 # Seconds, default: 600
SOCKET_FAIL_OPEN=true # Allow on API error
# Ports
HTTP_PORT=8080 # Default: 8080
HTTPS_PORT=8443 # Default: 8443
# Redis (optional)
REDIS_ENABLED=true
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=secret
# Proxy (optional)
SOCKET_OUTBOUND_PROXY=http://proxy:3128
SOCKET_NO_PROXY=localhost,127.0.0.1# Health endpoint (no auth required)
curl https://localhost:8443/health
# Expected response (plain text):
# SocketFirewall/1.1.23 - Health OK - npm (registry.npmjs.org)# All logs
docker compose logs -f socket-firewall
# Errors only
docker compose logs socket-firewall | grep -i error
# Security events
docker compose logs socket-firewall | grep -i blockForward security events to Splunk:
splunk:
enabled: true
hec_url: https://splunk.company.com:8088/services/collector/event
hec_token: your-splunk-hec-token
index: security
source: socket-firewallSee docs/SPLUNK.md for details.
# Check logs
docker compose logs socket-firewall
# Verify environment variables
docker compose exec socket-firewall env | grep SOCKET
# Test config generation
docker compose exec socket-firewall socket-proxy-config-tool generate --config /app/socket.yml# Check if package is blocked
docker compose logs socket-firewall | grep -i block
# Verify firewall is reachable
curl -I https://localhost:8443/health
# Test upstream connectivity from container
docker compose exec socket-firewall curl -I https://registry.npmjs.org# For testing, bypass SSL verification:
# npm
npm config set strict-ssl false
# pip
PIP_TRUSTED_HOST='localhost' pip install package
# Maven (add flags)
mvn install -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true
# For production, trust the CA certificate (see SSL/TLS section above)# Verify API token is set
docker compose exec socket-firewall env | grep SOCKET_SECURITY_API_TOKEN
# Check API connectivity (from container)
docker compose exec socket-firewall curl https://api.socket.dev/v0/health
# Review fail_open setting
cat socket.yml | grep fail_openRemove blocked/warned packages from registry metadata responses:
metadata_filtering:
enabled: true
filter_blocked: true # Remove blocked packages
filter_warn: false # Keep warned packages (show warnings only)For 50+ routes, use external file:
path_routing:
enabled: true
domain: firewall.company.com
routes_file: /config/routes.ymlSee docs/EXTERNAL-ROUTES.md for format.
Client (npm/pip/mvn)
↓
Socket Firewall (this)
↓
Socket.dev API (security check)
↓
Upstream Registry (npmjs.org, pypi.org, etc.)
Request Flow:
- Client requests package from firewall
- Firewall extracts package name/version
- Firewall checks Socket API for security issues
- If safe: proxy to upstream and return package
- If malicious: return 403 Forbidden with reason
- Getting Started: This file
- Auto-Discovery: docs/AUTO-DISCOVERY.md
- External Routes: docs/EXTERNAL-ROUTES.md
- Redis Caching: docs/REDIS.md
- Splunk Integration: docs/SPLUNK.md
- Artifactory Auth: docs/ARTIFACTORY-AUTH.md
# Minimal config - no custom domains needed
# Just start with docker compose up -d
# Access at https://localhost:8443/npm/, /pypi/, /maven/, etc.registries:
npm:
domains: [npm.company.com]
pypi:
domains: [pypi.company.com]
maven:
domains: [maven.company.com]
cargo:
domains: [cargo.company.com]
rubygems:
domains: [rubygems.company.com]
openvsx:
domains: [vsx.company.com]
nuget:
domains: [nuget.company.com]
go:
domains: [go.company.com]path_routing:
enabled: true
domain: packages.company.com
routes:
- { path: /npm, upstream: https://registry.npmjs.org, registry: npm }
- { path: /pypi, upstream: https://pypi.org, registry: pypi }
- { path: /maven, upstream: https://repo1.maven.org/maven2, registry: maven }
- { path: /cargo, upstream: https://index.crates.io, registry: cargo }
- { path: /rubygems, upstream: https://rubygems.org, registry: rubygems }
- { path: /openvsx, upstream: https://open-vsx.org, registry: openvsx }
- { path: /nuget, upstream: https://api.nuget.org, registry: nuget }
- { path: /go, upstream: https://proxy.golang.org, registry: go }path_routing:
enabled: true
domain: firewall.company.com
mode: artifactory # or 'nexus'
private_registry:
api_url: https://artifactory.company.com/artifactory
api_key: your-artifactory-api-key
interval: 5m
default_registry: maven # Fallback for unknown repos- GitHub Issues: https://github.com/SocketDev/socket-nginx-firewall/issues
- Email: support@socket.dev
- Documentation: https://docs.socket.dev
- Socket Dashboard: https://socket.dev/dashboard
Proprietary - Socket Security Inc.
Need help? Check docs/ for detailed guides or contact support@socket.dev