Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-09-16 15:52:05 -04:00
pkg-proxy/config.example.yaml

325 lines
11 KiB
YAML
Raw Permalink Normal View History

# Proxy server configuration
# Copy to config.yaml and modify as needed
# Server listen address
listen: ":8080"
# Public URL where package endpoints are reachable.
# Used for rewriting package metadata URLs and shown in install guide snippets
# so users know what to point their package manager at.
base_url: "http://localhost:8080"
# Timeout for individual upstream HTTP requests made by protocol handlers
# (metadata fetches, pass-through file requests). Uses Go duration syntax.
# Set to "0" to disable the timeout. Default: "30s".
# http_timeout: "30s"
# Public URL where the web UI is reached. Defaults to base_url when unset.
# Set this separately when the UI is served on a different hostname than the
# package endpoints — for example, the UI on a public domain behind auth while
# build machines hit a Docker network alias for the package endpoints.
# ui_base_url: "https://proxy.example.com/ui"
# Artifact storage configuration
storage:
# Storage backend URL
# Supported schemes:
# - file:///path/to/dir - Local filesystem (default)
# - s3://bucket-name - Amazon S3
# - s3://bucket?endpoint=http://localhost:9000 - S3-compatible (MinIO)
# - gs://bucket-name - Google Cloud Storage
# - azblob://container-name - Azure Blob Storage
#
# For S3, configure credentials via environment variables:
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
#
# For GCS, authentication uses Application Default Credentials. On GKE with
# Workload Identity, bind the Kubernetes service account to a Google service
# account that has roles/storage.objectAdmin on the bucket. No extra config
# is needed in this file. For local development, run:
# gcloud auth application-default login
# If direct_serve is enabled, the service account also needs
# roles/iam.serviceAccountTokenCreator on itself so the IAM Credentials
# signBlob API can sign URLs without a private key.
url: ""
# Local filesystem path (used when url is empty)
# Deprecated: Use url with file:// scheme instead
path: "./cache/artifacts"
# Maximum cache size (e.g., "10GB", "500MB")
# When exceeded, least recently used artifacts are evicted
# Empty or "0" means unlimited
max_size: ""
# Redirect cached artifact downloads to presigned storage URLs (HTTP 302)
# instead of streaming through the proxy. Only effective for S3, GCS, and Azure.
# Leave disabled if clients reach the proxy through an authenticating gateway,
# since presigned URLs bypass it.
direct_serve: false
# How long presigned URLs remain valid (e.g. "5m", "1h"). Default: "15m".
direct_serve_ttl: "15m"
# Public base URL to substitute into presigned URLs. Set this when the
# proxy reaches storage at an internal address (127.0.0.1, a Docker
# service name) but clients must use a public hostname. Only scheme and
# host are used; the signed path and query are preserved. For S3/MinIO
# the reverse proxy at this address must forward requests with the
# internal Host header or the SigV4 signature will not validate.
# direct_serve_base_url: "https://minio.example.com"
# Database configuration
database:
# Database driver: "sqlite" (default) or "postgres"
driver: "sqlite"
# SQLite database file path (used when driver is "sqlite")
path: "./cache/proxy.db"
# PostgreSQL connection URL (used when driver is "postgres")
# Example: "postgres://user:password@localhost:5432/proxy?sslmode=disable"
url: ""
# Logging configuration
log:
# Minimum log level: "debug", "info", "warn", "error"
level: "info"
# Log format: "text" or "json"
format: "text"
# JSONL access log. Leave path empty to disable it.
access_log:
path: ""
# Upstream URLs for built-in routes and authentication
upstream:
# Hosts allowed to resolve to private, ULA, or CGNAT addresses
allow_private_hosts: []
# Permit upstream requests and redirects to loopback addresses
allow_loopback: false
# npm registry URL
npm: "https://registry.npmjs.org"
# Always request full npm packuments so served metadata carries publish
# times ("time" map) even when cooldown is disabled. Needed by clients that
# gate on publish age, e.g. Yarn's npmMinimalAgeGate. Default: false.
# npm_full_metadata: true
# Cargo sparse index URL
cargo: "https://index.crates.io"
# Cargo crate download URL
cargo_download: "https://static.crates.io/crates"
# RubyGems registry URL
gem: "https://rubygems.org"
# Go module proxy URL
go: "https://proxy.golang.org"
# Hex repository URL
hex: "https://repo.hex.pm"
# Hex API URL used for package timestamps
hex_api: "https://hex.pm"
# pub registry URL
pub: "https://pub.dev"
# PyPI index and API URL
pypi: "https://pypi.org"
# PyPI package download URL
pypi_download: "https://files.pythonhosted.org"
# Maven repository URL (used by /maven endpoint)
maven: "https://repo1.maven.org/maven2"
# Gradle Plugin Portal Maven URL (fallback for plugin marker artifacts)
gradle_plugin_portal: "https://plugins.gradle.org/m2"
# NuGet API URL
nuget: "https://api.nuget.org"
# NuGet search API URL
nuget_search: "https://azuresearch-usnc.nuget.org"
# Packagist API URL
composer: "https://packagist.org"
# Packagist repository URL
composer_repository: "https://repo.packagist.org"
# Conan registry URL
conan: "https://center.conan.io"
# Conda channel base URL
conda: "https://conda.anaconda.org"
# CRAN mirror URL
cran: "https://cloud.r-project.org"
# Julia package server URL
julia: "https://pkg.julialang.org"
# Swift Package Registry URL (used by /swift endpoint)
swift: "https://tuist.dev/api/registry/swift"
# Default OCI registry URL for unprefixed /v2 requests
oci_default: "https://registry-1.docker.io"
# Debian/APT repository URL (used by /debian endpoint)
debian: "http://deb.debian.org/debian"
# RPM repository URL (used by /rpm endpoint)
rpm: "https://dl.fedoraproject.org/pub/fedora/linux"
2026-09-03 16:59:12 +01:00
# Homebrew JSON API URL (used by /homebrew endpoint)
homebrew_api: "https://formulae.brew.sh/api"
# Homebrew artifact registry URL (used for /v2/homebrew/core requests)
homebrew_artifact: "https://ghcr.io"
# Named HTTP Helm chart repositories (used by /helm/{name}/)
# helm:
# bitnami: "https://charts.bitnami.com/bitnami"
# Named OCI registries. Use the upstream/{name}/ repository prefix, e.g.
# oci://proxy.example.com/upstream/ghcr/owner/chart.
# oci:
# ghcr: "https://ghcr.io"
# Named Alpine APK repositories (used by /apk/{name}/).
# Defaults to {"alpine": "https://dl-cdn.alpinelinux.org/alpine"} when empty;
# configuring any entry replaces that default.
# apk:
# alpine: "https://dl-cdn.alpinelinux.org/alpine"
# private: "https://apk.example.com"
Add generic HTTP download proxy for GitHub release assets (mise/aqua) (#302) * Add generic HTTP download proxy for GitHub release assets Adds a /generic/{name}/ route backed by a new upstream.generic named-upstream map, so tools that download from fixed URL shapes (mise's aqua backend fetching GitHub release assets, and its tag lookups on api.github.com) can be pointed at the proxy with client-side URL rewriting. Only configured upstreams are reachable, so this is not an open HTTP proxy. Paths shaped like {owner}/{repo}/releases/download/{tag}/{asset} are version-pinned and go through the artifact cache: fetched once, hashed, served without revalidation, and still served when the upstream is down. Every other path goes through the metadata cache with the client's Accept header and query string replayed, so API responses are fresh within metadata_ttl, revalidated after that, and served stale when the upstream fails or rate-limits the request. Tests cover path classification, unknown upstreams and traversal, cache hits with the upstream down, HEAD, 404 pass-through, Accept and query forwarding, stale-on-429, cache isolation between upstreams, and that an upstream token scoped to the release host is not sent to the object store it redirects to. Closes #183. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RDjDeq27CKzEP2o3GWBY7F * Use fixed Accept header for generic metadata --------- Co-authored-by: Giles Westwood <giles@gileswestwood.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 16:44:54 +01:00
# Named generic HTTP upstreams (used by /generic/{name}/). The remaining
# request path and query are appended to the upstream URL. GitHub release
# assets ({owner}/{repo}/releases/download/{tag}/{asset}) are cached
# immutably; other paths use the metadata cache with stale-on-error.
# generic:
# github: "https://github.com"
# github-api: "https://api.github.com"
# Authentication for upstream registries
# Keys are absolute URL scopes. Scheme, host, effective port, and path
# segment boundaries must match; the longest matching scope wins.
# Values can reference environment variables using ${VAR_NAME} syntax.
#
# Supported auth types:
# - bearer: Authorization header with Bearer token
# - basic: Authorization header with Basic auth (username:password)
# - header: Custom header name and value
# - ecr: AWS ECR auto-refreshing token via the AWS SDK credential chain
auth:
# Example: npm with bearer token
# "https://registry.npmjs.org":
# type: bearer
# token: "${NPM_TOKEN}"
# Example: GitHub npm registry
# "https://npm.pkg.github.com":
# type: bearer
# token: "${GITHUB_TOKEN}"
# Example: PyPI with basic auth
# "https://pypi.org":
# type: basic
# username: "__token__"
# password: "${PYPI_TOKEN}"
# Example: Custom header for private registry
# "https://maven.mycompany.com":
# type: header
# header_name: "X-Auth-Token"
# header_value: "${MAVEN_TOKEN}"
# Example: private AWS ECR registry (12h tokens auto-refreshed via
# ecr:GetAuthorizationToken; credentials come from the AWS SDK default
# chain, so IRSA / instance profiles / AWS_* env vars all work; the region
# is inferred from the private ECR hostname)
# "https://123456789012.dkr.ecr.eu-west-1.amazonaws.com":
# type: ecr
# Gradle HttpBuildCache configuration
gradle:
build_cache:
# Set to true to disable PUT uploads (read-only cache mode)
read_only: false
# Maximum accepted Gradle cache upload body size
# Required and must be > 0
max_upload_size: "100MB"
# Evict entries older than this age (set to "0" to disable age-based eviction)
max_age: "168h"
# Cap total Gradle cache size; oldest entries are deleted first
# ("0" disables size-based eviction)
# max_size: "20GB"
# How often eviction runs when max_age or max_size is set
sweep_interval: "10m"
Add storage backend probe to /health (closes #73) (#119) * config: add Health.StorageProbeInterval * metrics: add proxy_health_probe_failures_total counter * server: add storageProbe with happy-path test * server: add storageProbe failure-mode tests * server: add healthCache with TTL, single-flight, transition logging * server: wire storage probe into /health * server: update TestHealthEndpoint for JSON; wire healthCache into newTestServer Also fix Windows file-locking issue in storageProbe: close the reader explicitly before Delete so the file handle is released prior to os.Remove. * server: clean up stale comment in storageProbe * docs: document storage health probe and new metric * docs: regenerate Swagger for /health JSON response * server: simplify rc.Close error handling in storageProbe * server: defer probe cleanup so size/open/read/verify failures don't leak objects Previously, storageProbe only called Delete on the success path. Any failure between Store and the final Delete (size mismatch, Open error, mid-stream read failure, content mismatch) left the probe object orphaned in the storage backend. With caching disabled and Kubernetes-rate probing, the leak could accumulate noticeably on backends like S3. Use a named return + defer to attempt Delete after every successful Store. The earlier-step failure remains the primary error; Delete failure only surfaces as step="delete" when nothing else went wrong. Add a table-driven test that asserts cleanup runs for each non-delete failure path. Reported by Copilot on #119. * config: validate health.storage_probe_interval in Config.Validate The new duration field was only validated at use time in newHealthCache. The existing codebase already validates other duration fields (MetadataTTL, DirectServeTTL, Gradle.MaxAge, Gradle.SweepInterval) in Config.Validate() so misconfiguration fails fast at startup with a config-key-specific error. Match that pattern. The parse-at-use code in newHealthCache stays as a safety net, mirroring the MetadataTTL precedent. Reported by Copilot on #119. * docs: lowercase "counter" in metrics table for consistency Other rows in the table use lowercase type names (counter/gauge/histogram). Match that style. Reported by Copilot on #119. * docs: include size-check step in /health probe description The probe is write → size-check → read → verify → delete; the architecture note was missing the size-check step. Reported by Copilot on #119. * server: address andrew's review on #119 - Drop unused callerCtx parameter from healthCache.Check (Check is now parameter-less; the comment-only "accepted for symmetry" justification wasn't carrying its weight). - Emit "storage": {"status": "skipped"} on DB short-circuit instead of omitting the key, so monitors expecting a fixed key set keep working. - Reject negative storage_probe_interval at config validation time (previously parsed and silently behaved like "0"). - Extract HealthConfig.Validate to keep Config.Validate under the gocognit threshold and match the existing GradleBuildCacheConfig pattern. - README Health Check section: note that /health is intended as a readiness probe rather than a liveness probe (Check holds a mutex for up to the 10s probe timeout). - cmd/proxy/main.go godoc: column-align the new env var with the surrounding Gradle entries. Reported by andrew on #119.
2026-05-22 14:14:01 +03:00
# Health endpoint configuration.
health:
# Minimum time between storage backend probes.
# The /health endpoint runs a write/read/verify/delete round-trip
# against the configured storage backend and caches the result for
# this interval. Set to "0" to probe on every request.
# Default: "30s".
storage_probe_interval: "30s"
# Version cooldown configuration
# Hides package versions published too recently, giving the community time
# to spot malicious releases before they're pulled into projects.
# Supported durations: "7d" (days), "48h" (hours), "30m" (minutes), "0" (disabled)
cooldown:
# Global default cooldown for all ecosystems
# default: "3d"
# Per-ecosystem overrides
# ecosystems:
# npm: "7d"
# cargo: "0"
# Per-package overrides (keyed by PURL). Keys are normalized, so npm scopes
# may use either @scope or the canonical %40scope form.
# packages:
# "pkg:npm/lodash": "0"
# "pkg:npm/@babel/core": "14d"
Add pre-cache artifact scanning hook (trivy/ClamAV/Wiz/custom) (#298) Runs fetched artifacts through pluggable external scanners after they're staged in storage but before they're committed to the cache DB, so a block verdict deletes the object instead of ever exposing it to a client. Scanners pull the staged bytes themselves via a short-lived HMAC-signed internal route rather than the proxy pushing bytes to them, keeping the mechanism storage-backend-agnostic and avoiding uploading potentially huge artifacts through the proxy's own egress. Hardening baked in from the start: the internal scan-fetch route is gated both at router-mount time and in the handler so it's inert whenever scanning is disabled or unsigned; the signing key is mandatory whenever scanning is enabled, enforced directly in scanner.NewGroup rather than relying on callers to invoke config validation; the scan call and the delete-on-block cleanup both run on a context detached from the client's, so a client disconnecting mid-scan can't be mistaken for a scanner failure, doesn't cause a legitimate artifact to be deleted, and doesn't leave a genuinely blocked artifact's bytes orphaned in storage; and scanner infrastructure errors (connection failures, internal hostnames) are never forwarded verbatim to anonymous clients, only a generic message. The scan-error metric also correctly distinguishes a scanner's own timeout from being cancelled because a sibling scanner already decided the verdict. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 05:06:44 -04:00
# Pre-cache artifact scanning. When enabled, every artifact is staged into
# storage and scanned by the configured scanners before it is committed to
# the cache and served to clients. Scanners never receive artifact bytes
# directly — each notify call includes a short-lived signed URL that the
# scanner fetches itself, so the proxy stays agnostic to trivy/ClamAV/Wiz/
# any custom service. Scanners run concurrently; the first "block" verdict
# wins and cancels the rest.
# scanning:
# enabled: true
# fail_open: false
# timeout: 30s
#
# # Authenticates pull requests to the internal scan-fetch route.
# # Required whenever enabled is true. Supports ${VAR_NAME} expansion.
# signing_key: ${PROXY_SCANNING_SIGNING_KEY}
#
# # Address scanners use to reach this proxy to pull staged artifacts.
# # Defaults to base_url.
# # fetch_base_url: http://proxy.internal:8080
#
# scanners:
# - name: clamav
# url: http://clamav-adapter:8080/scan
# mode: block
# - name: trivy
# url: http://trivy-adapter:8081/scan
# mode: monitor
# ecosystems: [npm, pypi]