Enterprise-grade security proxy that protects your package registries (npm, PyPI, Maven, Cargo, RubyGems, OpenVSX, VS Code Marketplace, NuGet, Go, Conda, HuggingFace) 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 - VS Code Marketplace (VS Code Extensions) -
marketplace.visualstudio.com - 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 10 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 EOF
Or 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: 3
Create 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: 4096
sudo 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 -d
That'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.31.1.1-3-alpineor similar
Create a Dockerfile in your project directory:
FROM openresty/openresty:1.31.1.1-3-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.sh
The 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: 1
Note: 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-firewall
The 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 11 ecosystems (npm, PyPI, Maven, Cargo, RubyGems, OpenVSX, VS Code Marketplace, NuGet, Go, Conda, HuggingFace)
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 requests
Test 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=true
Test 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.com
Single 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: maven
A 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: npm
allowed_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.pem
Linux:
sudo cp ssl/fullchain.pem /usr/local/share/ca-certificates/socket-firewall.crt sudo update-ca-certificates
Place your certificates in the ssl/ directory:
cp /path/to/cert.pem ssl/fullchain.pem cp /path/to/key.pem ssl/privkey.pem
mkdir -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.com
socket: outbound_proxy: http://proxy.company.com:3128 # Verify SSL with corporate CA (default: true) api_ssl_verify: true api_ssl_ca_cert: /path/to/corporate-ca.crt # Apply same setting to upstream registries (defaults to api_ssl_verify) # upstream_ssl_verify: false # set false only when upstream uses untrusted/self-signed certs without ssl.ca_cert
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: 60
socket: fail_open: true # Allow packages if Socket API is down (default) # fail_open: false # Block all packages if Socket API is down
Override 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 block
Forward 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-firewall
See 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_open
Remove 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.yml
See 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] vscode: domains: [vscode.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: /vscode, upstream: https://marketplace.visualstudio.com, registry: vscode } - { 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: [email protected]
- Documentation: https://docs.socket.dev
- Socket Dashboard: https://socket.dev/dashboard
Proprietary - Socket Security Inc.
Need help? Check docs/ for detailed guides or contact [email protected]