* feat: add ProxyConfig struct with per-PM skip_commands and legacy fallback * feat: consolidate proxy config into structured section with backward compat Replaces flat proxy_mode/proxy_install_only keys with a structured proxy section supporting per-package-manager skip_commands. Legacy keys are respected via fallback when user's config lacks the new proxy section. Removes deprecated experimental_proxy_mode config and flag. * fix: env var resolution for nested config keys and deduplicate skip command matching - Add "." to "_" in Viper env key replacer so nested keys like sandbox.enabled resolve from PMG_SANDBOX_ENABLED (was silently broken) - Export IsFirstNonFlagArgInList and remove duplicate from proxy_flow.go - Add table-driven tests for skip command matching with real-world cases - Remove redundant env var test * docs: update proxy configuration and env var documentation Update config.md env var table to reflect new proxy.enabled and proxy.install_only keys. Add proxy configuration section to proxy.md covering config structure, per-PM skip commands, CLI flags, and env vars. * fix: legacy fallback precedence
3.7 KiB
PMG Proxy
A generic, extensible HTTP/HTTPS proxy server with man-in-the-middle (MITM) capabilities for intercepting and analyzing package manager traffic. Built with goproxy library.
Configuration
Proxy behavior is configured under the proxy: section in config.yml:
proxy:
enabled: true
install_only: false
policies:
npm:
skip_commands: ["my-script"]
| Key | Default | Description |
|---|---|---|
enabled |
true |
Enable proxy-based interception. When false, PMG falls back to guard-based analysis. |
install_only |
false |
When true, only install commands are proxied. Other commands (e.g., npm ls, pip list) bypass the proxy and execute directly. |
policies |
{} |
Per-package-manager policies. Each entry maps a package manager name to a policy with skip_commands. |
Per-package-manager skip commands
The policies section lets you define additional commands that should bypass the proxy for specific package managers:
proxy:
policies:
npm:
skip_commands: ["dev", "my-script"]
pip:
skip_commands: ["list", "show"]
Commands in skip_commands are matched against the first non-flag argument. For example, npm dev would match dev, but npm install dev would not since install is the first non-flag argument.
CLI flags
| Flag | Description |
|---|---|
--proxy-mode |
Override proxy.enabled |
Environment variables
| Variable | Description |
|---|---|
PMG_PROXY_ENABLED |
Override proxy.enabled |
PMG_PROXY_INSTALL_ONLY |
Override proxy.install_only |
Legacy variables PMG_PROXY_MODE and PMG_PROXY_INSTALL_ONLY (for the old flat config keys) are still supported when the proxy: section does not exist in the config file.
Features
- Selective interception of HTTPS traffic
- Pluggable interceptors for different use cases
- Certificate generation and management for HTTPS interception (MITM)
Architecture
flowchart TD
A[Package Manager]
A -- HTTPS_PROXY --> B[Proxy Server]
B --> C{Match?}
C -- Yes --> D[MITM]
D --> E[Inspect]
C -- No --> F[TCP Tunnel]
subgraph inside_proxy [ ]
G[Interceptor<br/>Chain]
end
B --> inside_proxy
Example
See examples/proxy for a complete example.
Quick Start
package main
import (
"github.com/safedep/pmg/proxy"
"github.com/safedep/pmg/proxy/certmanager"
)
func main() {
// Generate CA certificate
caCert, _ := certmanager.GenerateCA(certmanager.DefaultCertManagerConfig())
// Create certificate manager
certMgr, _ := certmanager.NewCertificateManagerWithCA(caCert, certmanager.DefaultCertManagerConfig())
// Create proxy
proxyServer, _ := proxy.NewProxyServer(&proxy.ProxyConfig{
ListenAddr: "127.0.0.1:8888",
CertManager: certMgr,
EnableMITM: true,
Interceptors: []proxy.Interceptor{NewMyInterceptor()},
})
// Start proxy
proxyServer.Start()
// ... wait for shutdown signal ...
proxyServer.Stop(context.Background())
}
Certificate Manager
The certmanager package provides certificate generation and caching.
Usage
import "github.com/safedep/pmg/proxy/certmanager"
// Generate a new CA certificate
config := certmanager.DefaultCertManagerConfig()
caCert, err := certmanager.GenerateCA(config)
// Handle persistence (example)
os.WriteFile("ca-cert.pem", caCert.Certificate, 0644)
// Create certificate manager with CA
certMgr, err := certmanager.NewCertificateManagerWithCA(caCert, config)
// Generate host certificates (automatically cached)
hostCert, err := certMgr.GenerateCertForHost("registry.npmjs.org")