feat: Add Support for Proxy with Interceptor (#77)

This commit is contained in:
Abhisek Datta
2025-12-10 08:34:28 +05:30
committed by GitHub
parent a8723cd680
commit 21eb05373f
15 changed files with 1852 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
ca-cert.pem
proxy
+43
View File
@@ -0,0 +1,43 @@
# Proxy Example: HTTPS Logging
This example demonstrates using the PMG proxy server to intercept and log HTTPS requests to package registries.
## Usage
### Build and Run
```bash
cd examples/proxy
go run .
```
### Configure Your Environment
Open a new terminal and configure the proxy:
```bash
export HTTPS_PROXY=http://127.0.0.1:8888
export NODE_EXTRA_CA_CERTS=./ca-cert.pem
export SSL_CERT_FILE=./ca-cert.pem
export PIP_CERT=./ca-cert.pem
export REQUESTS_CA_BUNDLE=./ca-cert.pem
export PIP_PROXY=http://127.0.0.1:8888
```
### Test with Package Managers
Test with `npm`:
```bash
npm --no-cache --prefer-online install express
```
Test with `pip`:
```bash
pip3 --proxy http://127.0.0.1:8888 index versions requests
```
```bash
pip3 install --proxy http://127.0.0.1:8888 --no-cache-dir requests
```
+50
View File
@@ -0,0 +1,50 @@
package main
import (
"fmt"
"time"
"github.com/safedep/pmg/proxy"
)
type loggingInterceptor struct {
domains []string
}
func newLoggingInterceptor() *loggingInterceptor {
return &loggingInterceptor{
domains: []string{
"registry.npmjs.org",
"registry.yarnpkg.com",
"pypi.org",
"files.pythonhosted.org",
},
}
}
func (li *loggingInterceptor) Name() string {
return "logging-interceptor"
}
func (li *loggingInterceptor) ShouldIntercept(ctx *proxy.RequestContext) bool {
for _, domain := range li.domains {
if ctx.Hostname == domain {
return true
}
}
return false
}
func (li *loggingInterceptor) HandleRequest(ctx *proxy.RequestContext) (*proxy.InterceptorResponse, error) {
fmt.Printf("LOGGING INTERCEPTOR: [%s] %s %s %s\n",
ctx.StartTime.Format(time.RFC3339),
ctx.RequestID,
ctx.Method,
ctx.URL.String(),
)
return &proxy.InterceptorResponse{
Action: proxy.ActionAllow,
}, nil
}
+93
View File
@@ -0,0 +1,93 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/safedep/dry/log"
"github.com/safedep/pmg/proxy"
"github.com/safedep/pmg/proxy/certmanager"
)
const (
listenAddr = "127.0.0.1:8888"
connectTimeout = 30 * time.Second
requestTimeout = 5 * time.Minute
)
func main() {
log.InitZapLogger("proxy-example", "dev")
fmt.Println("Generating CA certificate...")
caConfig := certmanager.DefaultCertManagerConfig()
caCert, err := certmanager.GenerateCA(caConfig)
if err != nil {
log.Fatalf("Failed to generate CA: %v", err)
}
// Save CA cert for use with clients
if err := os.WriteFile("ca-cert.pem", caCert.Certificate, 0644); err != nil {
log.Fatalf("Failed to save CA cert: %v", err)
}
fmt.Println("✓ CA certificate saved to ca-cert.pem")
fmt.Println()
fmt.Println("To trust this CA:")
fmt.Println(" Node.js: export NODE_EXTRA_CA_CERTS=./ca-cert.pem")
fmt.Println(" Python: export SSL_CERT_FILE=./ca-cert.pem")
fmt.Println(" System: Add ca-cert.pem to your OS trust store")
fmt.Println()
// Create certificate manager with CA certificate for use with proxy
certMgr, err := certmanager.NewCertificateManagerWithCA(caCert, caConfig)
if err != nil {
log.Fatalf("Failed to create cert manager: %v", err)
}
// Create proxy with certificate manager and logging interceptor
proxyConfig := &proxy.ProxyConfig{
ListenAddr: listenAddr,
CertManager: certMgr,
EnableMITM: true,
Interceptors: []proxy.Interceptor{newLoggingInterceptor()},
ConnectTimeout: connectTimeout,
RequestTimeout: requestTimeout,
}
proxyServer, err := proxy.NewProxyServer(proxyConfig)
if err != nil {
log.Fatalf("Failed to create proxy server: %v", err)
}
// Start proxy
if err := proxyServer.Start(); err != nil {
log.Fatalf("Failed to start proxy: %v", err)
}
fmt.Printf("Proxy listening on %s\n", proxyServer.Address())
fmt.Printf("Configure clients with: export HTTPS_PROXY=http://%s\n", listenAddr)
fmt.Println()
fmt.Println("Press Ctrl+C to stop")
fmt.Println()
// Wait for interrupt signal to gracefully shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
// Graceful shutdown
fmt.Println("\nShutting down proxy...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := proxyServer.Stop(ctx); err != nil {
log.Errorf("Error during shutdown: %v", err)
}
fmt.Println("Proxy stopped")
}