Skip to content

codozor/fwkeeper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fwkeeper

Go Report Card Build Coverage Status Version License Go Version

Port forwarding made easy. A Kubernetes port forwarding tool that automatically maintains persistent port forwards to pods with automatic reconnection and failure recovery.

fwkeeper architecture

Features

  • 🔄 Automatic Port Forwarding: Maintains persistent port forwards to Kubernetes pods
  • 🔁 Automatic Reconnection: Automatically reconnects on pod restarts or connection failures
  • ⚙️ Easy Configuration: CUE-based configuration for simple pod forwarding setup
  • 📊 Structured Logging: Comprehensive logging with configurable levels and pretty-printing
  • 🔐 Kubernetes Integration: Seamless integration with your Kubernetes cluster (local or in-cluster)
  • 🚀 Multiple Forwards: Support for multiple simultaneous port forwards

Installation

Prerequisites

  • Go 1.25.1 or later - Download Go
  • Access to a Kubernetes cluster (local kubeconfig or in-cluster)

Verify Go is installed:

go version

Install

Install fwkeeper directly:

go install github.com/codozor/fwkeeper@latest

This installs the binary to $GOPATH/bin (typically ~/go/bin), which should already be in your PATH.

Verify installation:

fwkeeper --version
fwkeeper --help

If fwkeeper is not found, ensure $GOPATH/bin is in your PATH:

# Add to ~/.bashrc, ~/.zshrc, or ~/.bash_profile
export PATH="$HOME/go/bin:$PATH"

Build from Source (Development)

For development or building locally:

git clone https://github.com/codozor/fwkeeper.git
cd fwkeeper
go build -o fwkeeper ./
./fwkeeper run -c fwkeeper.cue

Quick Start

1. Create a Configuration File

Create a fwkeeper.cue file:

logs: {
  level: "debug"
  pretty: true
}

forwards: [
  {
    name: "postgres"
    ports: ["5432"]
    namespace: "default"
    resource: "postgres-pod"
  },
  {
    name: "api"
    ports: ["8080:8080", "9000:9000"]
    namespace: "api"
    resource: "api-server-deployment-abc123"
  }
]

2. Run fwkeeper

./fwkeeper run -c fwkeeper.cue

Or using the default fwkeeper.cue:

./fwkeeper run

3. Access Your Services

The port forwards are now active. Connect to your services:

# Connect to postgres
psql -h localhost -p 5432

# Connect to API
curl http://localhost:8080

Configuration

Configuration File Format (CUE)

fwkeeper uses CUE for configuration validation and schema enforcement.

Top-Level Structure

logs: { ... }
forwards: [ ... ]

Logs Configuration

logs: {
  level: "debug"      # "error", "warn", "info", "debug", "trace" (default: "info")
  pretty: true        # Pretty-print logs to console (default: false)
}

Port Forward Configuration

forwards: [
  {
    name: "service-name"              # Unique identifier for this forward
    ports: ["8080", "9000:3000"]      # Local:remote port mappings
    namespace: "default"              # Kubernetes namespace
    resource: "pod-name"              # Pod, Service, Deployment, StatefulSet, or DaemonSet
  },
  # ... more forwards
]

Resource Reference Syntax:

  • "pod-name" - Direct pod reference
  • "svc/service-name" or "service/service-name" - Kubernetes Service
  • "dep/deployment-name" or "deployment/deployment-name" - Kubernetes Deployment
  • "sts/statefulset-name" or "statefulset/statefulset-name" - Kubernetes StatefulSet
  • "ds/daemonset-name" or "daemonset/daemonset-name" - Kubernetes DaemonSet

When using a Service, Deployment, StatefulSet, or DaemonSet, fwkeeper automatically finds and connects to the first running pod that matches the resource's selector.

Port Mapping Syntax:

  • "8080" - Forward local port 8080 to pod port 8080
  • "8080:9000" - Forward local port 8080 to pod port 9000

Validation Rules:

  • Port numbers must be between 1 and 65535
  • Each forward must have a unique name
  • Namespace and resource must be specified
  • Local ports must be unique across all forwards

Environment Variables

# Override kubeconfig location
KUBECONFIG=/path/to/kubeconfig ./fwkeeper run

# Use default kubeconfig locations
# 1. $KUBECONFIG environment variable
# 2. ~/.kube/config
# 3. In-cluster config (if running inside Kubernetes)

Usage

Basic Commands

# Show help
./fwkeeper --help

# Run with default config (fwkeeper.cue)
./fwkeeper run

# Run with custom config file
./fwkeeper run -c /path/to/config.cue

# Show command help
./fwkeeper run --help

Exit and Shutdown

Press Ctrl+C to gracefully stop fwkeeper. It will:

  1. Cancel all active port forwards
  2. Close connections
  3. Print shutdown message

Configuration Hot-Reload

fwkeeper automatically detects changes to your configuration file and reloads without interrupting active port forwards (when possible).

Automatic Reload:

  • fwkeeper watches your config file for changes
  • When the file is saved, the configuration is automatically reloaded
  • New forwards are started, removed forwards are stopped, modified forwards are restarted

Manual Reload: Send a SIGHUP signal to trigger manual reload:

# In another terminal, while fwkeeper is running
kill -HUP <pid>

# Or using pkill
pkill -HUP fwkeeper

Config Reload Behavior:

  • Invalid config → Current configuration continues, error logged, reload skipped
  • New forwards → Started automatically
  • Removed forwards → Stopped gracefully
  • Modified forwards → Restarted with new configuration
  • Unchanged forwards → Continue running without interruption

Example:

  1. Start fwkeeper: ./fwkeeper run -c fwkeeper.cue
  2. Edit fwkeeper.cue (add, remove, or modify forwards)
  3. Save the file
  4. fwkeeper detects the change and updates automatically
  5. View the logs to see what changed

Examples

Example 1: Database Access

Forward to a PostgreSQL database pod:

logs: {
  level: "info"
  pretty: true
}

forwards: [
  {
    name: "database"
    ports: ["5432"]
    namespace: "databases"
    resource: "postgres-primary"
  }
]

Then connect:

psql -h localhost -p 5432 -U myuser -d mydb

Example 2: Development Environment

Forward multiple services for local development:

logs: {
  level: "debug"
  pretty: true
}

forwards: [
  {
    name: "api-server"
    ports: ["8000:8000"]
    namespace: "development"
    resource: "api-server"
  },
  {
    name: "frontend"
    ports: ["3000:3000"]
    namespace: "development"
    resource: "frontend"
  },
  {
    name: "postgres"
    ports: ["5432:5432"]
    namespace: "databases"
    resource: "postgres-dev"
  },
  {
    name: "redis"
    ports: ["6379:6379"]
    namespace: "databases"
    resource: "redis-dev"
  }
]

Example 3: Multiple Ports on Same Pod

Forward multiple ports from a single pod:

forwards: [
  {
    name: "api-services"
    ports: ["8000:8000", "9000:9000", "5000:5000"]
    namespace: "api"
    resource: "api-pod"
  }
]

Example 4: Using Deployments and StatefulSets

Forward to pods managed by Deployments, StatefulSets, or DaemonSets:

logs: {
  level: "info"
  pretty: true
}

forwards: [
  {
    name: "api-deployment"
    ports: ["8080:8080"]
    namespace: "production"
    resource: "dep/api-server"        # Deployment: automatically finds a running pod
  },
  {
    name: "postgres-statefulset"
    ports: ["5432:5432"]
    namespace: "databases"
    resource: "sts/postgres-primary"  # StatefulSet: automatically finds a running pod
  },
  {
    name: "monitoring-daemonset"
    ports: ["9090:9090"]
    namespace: "monitoring"
    resource: "ds/prometheus"         # DaemonSet: automatically finds a running pod
  }
]

The resource finder automatically locates the first running pod managed by the specified Deployment, StatefulSet, or DaemonSet. This is useful when you have multiple replicas and want to connect to any available pod.

Logging

Log Levels

  • error: Only errors
  • warn: Warnings and errors
  • info: General information (default)
  • debug: Detailed debugging information
  • trace: Very detailed tracing information

Log Output

Logs are written to stderr. Each log entry includes:

  • Timestamp (Unix milliseconds)
  • Log level
  • Message
  • Error details (when applicable)

Pretty Printing

Enable pretty-printed logs for development:

logs: {
  pretty: true
}

Output will include timestamps and color-coded levels for better readability.

Troubleshooting

"Pod not in running state"

The pod exists but isn't currently running. Check pod status:

kubectl get pods -n <namespace> <pod-name>
kubectl describe pod -n <namespace> <pod-name>

"Connection refused" or "Connection reset"

The pod restarted or the port-forward connection dropped. fwkeeper will automatically reconnect with exponential backoff (starting at 100ms, up to 30s). Check logs for details.

"Unable to connect to kubeconfig"

Verify your kubeconfig:

# Check KUBECONFIG env var
echo $KUBECONFIG

# Test cluster access
kubectl cluster-info

# Set correct kubeconfig
export KUBECONFIG=~/.kube/config
./fwkeeper run

Configuration Validation Errors

Verify your CUE configuration syntax and against the schema:

# Check for CUE syntax errors in your config file
# Ensure all fields match the required structure in schema.cue

Common issues:

  • Missing required fields (name, ports, namespace, resource)
  • Invalid port numbers (not 1-65535)
  • Malformed port specifications (should be "port" or "local:remote")

Debug Logs

Enable debug logging to see detailed information:

logs: {
  level: "debug"
  pretty: true
}

Testing

Running Tests

Run all tests in the project:

go test ./...

Run tests with verbose output:

go test -v ./...

Run specific test package:

go test -v ./internal/config

Run specific test:

go test -v -run TestPortValidation ./internal/config

Test Coverage

Run tests with coverage:

go test -cover ./...
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

Test Status

Phase 1 (Completed):

  • ✅ Config loading and parsing (valid config, parsing errors)
  • ✅ Port validation (range: 1-65535, invalid ports, port conflicts)
  • ✅ Port conflict detection (duplicate local ports across forwards)

Phase 2 (Planned):

  • Locator implementations (Pod, Service, Deployment, StatefulSet, DaemonSet)
  • Forward name uniqueness validation
  • Configuration hot-reload

Phase 3 (Planned):

  • Runner lifecycle (startup, shutdown, error handling)
  • Forwarder reconnection behavior

Development

Project Structure

fwkeeper/
├── cmd/                      # CLI command definitions
│   ├── root.go              # Root command
│   └── run.go               # Run command
├── internal/
│   ├── app/                 # Application orchestration
│   │   └── runner.go        # Main runner and lifecycle management
│   ├── bootstrap/           # Dependency injection setup
│   ├── config/              # Configuration loading and validation
│   │   └── schema.cue       # CUE schema definition
│   ├── forwarder/           # Port forwarding logic
│   │   └── forwarder.go     # Individual pod port forwarder
│   ├── kubernetes/          # Kubernetes client setup
│   ├── locator/             # Pod discovery and location
│   │   └── locator.go       # Pod/service locator implementations
│   └── logger/              # Logging setup
├── main.go                  # Application entry point
├── go.mod                   # Go module definition
├── go.sum                   # Dependency checksums
├── fwkeeper.cue             # Default configuration
└── README.md                # This file

Building

# Build the binary
go build -o fwkeeper ./

# Build with version info
go build -ldflags="-X main.version=v1.0.0" -o fwkeeper ./

Running Tests

# Run all tests
go test ./...

# Run with verbose output
go test -v ./...

# Run specific test
go test -run TestName ./path/to/package

Code Quality

# Format code
go fmt ./...

# Run static analysis
go vet ./...

# Run golangci-lint (if installed)
golangci-lint run ./...

Dependencies

Key dependencies:

Architecture

Key Components

  1. Runner: Orchestrates port forwarders, manages context and graceful shutdown
  2. Forwarder: Implements individual pod port forwarding with automatic reconnection
  3. Config: CUE-based configuration parsing and validation
  4. Logger: Structured logging with zerolog
  5. Kubernetes Integration: Handles kubeconfig loading and client initialization

Port Forward Flow

  1. Read configuration from CUE file
  2. Load Kubernetes credentials
  3. For each forward:
    • Locate the pod
    • Verify pod is running
    • Establish SPDY connection to pod
    • Forward ports
    • Reconnect on failure (exponential backoff: 100ms → 30s with jitter)
  4. Listen for interrupt signal (Ctrl+C)
  5. Gracefully shutdown all forwarders

License

This project is under MIT License

Support

For issues, questions, or contributions, please visit the repository.

About

Stop restarting kubectl port-forward. Switch to Fwkeeper for stable, automated tunnels.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors