mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* 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
138 lines
3.7 KiB
Markdown
138 lines
3.7 KiB
Markdown
# 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](https://github.com/elazarl/goproxy) library.
|
|
|
|
## Configuration
|
|
|
|
Proxy behavior is configured under the `proxy:` section in `config.yml`:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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
|
|
|
|
```mermaid
|
|
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](../examples/proxy/README.md) for a complete example.
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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")
|
|
```
|
|
|