Documentation
¶
Index ¶
- Variables
- func EnsureCA(certDir string) (*x509.Certificate, *ecdsa.PrivateKey, error)
- func EnsureDaemon(cfg config.Config, log *logger.Logger) error
- func GenerateCorefile(rules []config.EgressRule, healthPort int) ([]byte, error)
- func GenerateDomainCert(caCert *x509.Certificate, caKey *ecdsa.PrivateKey, domain string) (certPEM, keyPEM []byte, err error)
- func GenerateEnvoyConfig(rules []config.EgressRule, ports EnvoyPorts) ([]byte, []string, error)
- func GetDaemonPID(pidFile string) int
- func IsDaemonRunning(pidFile string) bool
- func NewRulesStore(cfg config.Config) (*storage.Store[EgressRulesFile], error)
- func ProjectRules(cfg config.Config) []config.EgressRule
- func RegenerateDomainCerts(rules []config.EgressRule, certDir string, caCert *x509.Certificate, ...) error
- func RotateCA(certDir string, rules []config.EgressRule) error
- func StopDaemon(pidFile string) error
- type Daemon
- type DaemonOption
- type EgressRulesFile
- type EnvoyPorts
- type FirewallManager
- type FirewallStatus
- type HealthTimeoutError
- type Manager
- func (m *Manager) AddRules(ctx context.Context, rules []config.EgressRule) error
- func (m *Manager) Bypass(ctx context.Context, containerID string, timeout time.Duration) error
- func (m *Manager) CoreDNSIP() string
- func (m *Manager) Disable(ctx context.Context, containerID string) error
- func (m *Manager) Enable(ctx context.Context, containerID string) error
- func (m *Manager) EnsureRunning(ctx context.Context) error
- func (m *Manager) EnvoyIP() string
- func (m *Manager) IsRunning(ctx context.Context) bool
- func (m *Manager) List(_ context.Context) ([]config.EgressRule, error)
- func (m *Manager) NetCIDR() string
- func (m *Manager) Reload(ctx context.Context) error
- func (m *Manager) RemoveRules(ctx context.Context, rules []config.EgressRule) error
- func (m *Manager) Status(ctx context.Context) (*FirewallStatus, error)
- func (m *Manager) Stop(ctx context.Context) error
- func (m *Manager) WaitForHealthy(ctx context.Context) error
- type NetworkInfo
- type TCPMapping
Constants ¶
This section is empty.
Variables ¶
var ( ErrEnvoyUnhealthy = errors.New("envoy not healthy") ErrCoreDNSUnhealthy = errors.New("coredns not healthy") )
Sentinel errors for health check failures.
Functions ¶
func EnsureCA ¶
func EnsureCA(certDir string) (*x509.Certificate, *ecdsa.PrivateKey, error)
EnsureCA creates a self-signed CA keypair if none exists under certDir, or loads the existing one.
func EnsureDaemon ¶
EnsureDaemon checks if the firewall daemon is running and spawns it if not. Returns immediately — does not wait for the daemon to become healthy.
func GenerateCorefile ¶
func GenerateCorefile(rules []config.EgressRule, healthPort int) ([]byte, error)
GenerateCorefile produces a CoreDNS Corefile from the given egress rules. healthPort is the port the CoreDNS health plugin listens on (inside the container).
Only "allow" rules with domain destinations (not IPs/CIDRs) get forward zones. Each allowed domain gets its own zone forwarding to Cloudflare malware-blocking DNS. The catch-all "." zone returns NXDOMAIN for everything else.
func GenerateDomainCert ¶
func GenerateDomainCert(caCert *x509.Certificate, caKey *ecdsa.PrivateKey, domain string) (certPEM, keyPEM []byte, err error)
GenerateDomainCert signs a per-domain certificate for MITM inspection. The certificate is signed by the given CA and has the domain as a SAN. Returns PEM-encoded cert and key bytes.
func GenerateEnvoyConfig ¶
func GenerateEnvoyConfig(rules []config.EgressRule, ports EnvoyPorts) ([]byte, []string, error)
GenerateEnvoyConfig produces an Envoy static bootstrap YAML from egress rules. Returns the YAML bytes and a list of warnings (non-fatal issues).
func GetDaemonPID ¶
GetDaemonPID returns the PID of the running firewall daemon, or 0 if not running.
func IsDaemonRunning ¶
IsDaemonRunning checks if the firewall daemon is running via PID file.
func NewRulesStore ¶
NewRulesStore creates a storage.Store[EgressRulesFile] for egress-rules.yaml. The store uses the firewall data subdirectory for file discovery.
func ProjectRules ¶
func ProjectRules(cfg config.Config) []config.EgressRule
ProjectRules builds the full rule set from project config and required rules. Used by CLI code to sync rules into the store before the daemon starts.
func RegenerateDomainCerts ¶
func RegenerateDomainCerts(rules []config.EgressRule, certDir string, caCert *x509.Certificate, caKey *ecdsa.PrivateKey) error
RegenerateDomainCerts generates certificates for all rules that have PathRules, storing them in certDir/<domain>-cert.pem and <domain>-key.pem. Rules without PathRules are skipped (SNI passthrough, no MITM needed).
func RotateCA ¶
func RotateCA(certDir string, rules []config.EgressRule) error
RotateCA regenerates the CA keypair and all domain certificates. The old CA files are overwritten. Any running containers will need the new CA injected to trust the regenerated domain certs.
func StopDaemon ¶
StopDaemon sends SIGTERM to the firewall daemon. No-op if not running.
Types ¶
type Daemon ¶
type Daemon struct {
// contains filtered or unexported fields
}
Daemon manages the firewall stack as a background process. It starts the Envoy+CoreDNS containers, monitors their health, and auto-exits (tearing down the stack) when no clawker containers are running.
type DaemonOption ¶
type DaemonOption func(*Daemon)
DaemonOption is a functional option for overriding daemon config values.
func WithDaemonGracePeriod ¶
func WithDaemonGracePeriod(d time.Duration) DaemonOption
WithDaemonGracePeriod overrides the initial grace period before container watching.
func WithDaemonPollInterval ¶
func WithDaemonPollInterval(d time.Duration) DaemonOption
WithDaemonPollInterval overrides the container watcher poll interval.
func WithHealthCheckInterval ¶
func WithHealthCheckInterval(d time.Duration) DaemonOption
WithHealthCheckInterval overrides the healthcheck poll interval.
type EgressRulesFile ¶
type EgressRulesFile struct {
Rules []config.EgressRule `yaml:"rules" label:"Rules" desc:"Active egress firewall rules"`
}
EgressRulesFile is the top-level document type for storage.Store[T]. It persists the active set of project-level egress rules to disk.
func (EgressRulesFile) Fields ¶ added in v0.6.0
func (f EgressRulesFile) Fields() storage.FieldSet
Fields implements storage.Schema for EgressRulesFile.
type EnvoyPorts ¶
type EnvoyPorts struct {
TLSPort int // Listener port for TLS egress (SNI passthrough + MITM).
TCPPortBase int // Starting port for TCP/SSH listeners.
HTTPPort int // Listener port for plain HTTP egress (Host header domain detection).
}
EnvoyPorts holds the port configuration for the Envoy proxy, sourced from config.Config.
type FirewallManager ¶
type FirewallManager interface {
// EnsureRunning starts the firewall stack (Envoy + CoreDNS containers) if not already running.
EnsureRunning(ctx context.Context) error
// Stop tears down the firewall stack.
Stop(ctx context.Context) error
// IsRunning reports whether the firewall stack is currently running.
IsRunning(ctx context.Context) bool
// WaitForHealthy polls until both firewall services pass health probes (TCP+HTTP)
// or the context expires. Timeout should be set on the context by the caller.
WaitForHealthy(ctx context.Context) error
// AddRules adds individual egress rules (CLI "firewall add").
// Writes to store, regenerates configs, restarts containers if running.
AddRules(ctx context.Context, rules []config.EgressRule) error
// RemoveRules deletes egress rules (CLI "firewall remove").
RemoveRules(ctx context.Context, rules []config.EgressRule) error
// Reload force-regenerates envoy.yaml and Corefile from current rules
// and restarts the Envoy container. CoreDNS auto-reloads via reload plugin.
Reload(ctx context.Context) error
// List returns all currently active egress rules.
List(ctx context.Context) ([]config.EgressRule, error)
// Disable flushes iptables rules in the container, giving unrestricted egress.
Disable(ctx context.Context, containerID string) error
// Enable applies iptables rules in the container with current network info.
Enable(ctx context.Context, containerID string) error
// Bypass disables firewall and schedules re-enable after timeout.
// Uses detached docker exec: sleep <timeout> && firewall.sh enable <args>.
// Returns immediately — timer runs inside the container.
// To cancel early: call Enable() directly (idempotent, re-applies rules).
Bypass(ctx context.Context, containerID string, timeout time.Duration) error
// Status returns a health snapshot of the firewall stack.
Status(ctx context.Context) (*FirewallStatus, error)
// EnvoyIP returns the static IP assigned to the Envoy proxy container.
EnvoyIP() string
// CoreDNSIP returns the static IP assigned to the CoreDNS container.
CoreDNSIP() string
// NetCIDR returns the CIDR block of the isolated Docker firewall network.
NetCIDR() string
}
FirewallManager is the interface for managing the Envoy+CoreDNS firewall stack. Concrete implementation: DockerFirewallManager. Mock: mocks.FirewallManagerMock.
type FirewallStatus ¶
type FirewallStatus struct {
Running bool
EnvoyHealth bool
CoreDNSHealth bool
RuleCount int
EnvoyIP string
CoreDNSIP string
NetworkID string
}
FirewallStatus is a health snapshot of the Envoy+CoreDNS firewall stack.
type HealthTimeoutError ¶
type HealthTimeoutError struct {
Timeout time.Duration
Err error // wraps one or both sentinel errors
}
HealthTimeoutError is returned when WaitForHealthy exceeds its deadline.
func (*HealthTimeoutError) Error ¶
func (e *HealthTimeoutError) Error() string
func (*HealthTimeoutError) Unwrap ¶
func (e *HealthTimeoutError) Unwrap() error
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager implements FirewallManager using the Docker API. It manages Envoy and CoreDNS containers on the clawker-net Docker network.
func NewManager ¶
NewManager creates a new DockerFirewallManager.
func (*Manager) AddRules ¶
AddRules adds individual egress rules to the running firewall (CLI "firewall add"). Deduplicates against existing rules, persists, regenerates configs, and hot-reloads.
func (*Manager) Bypass ¶
Bypass disables iptables rules and schedules re-enable after timeout. Uses detached docker exec: sleep <timeout> && firewall.sh enable <args>.
func (*Manager) Disable ¶
Disable disconnects a container from the firewall network, blocking all egress through the firewall stack.
func (*Manager) Enable ¶
Enable re-applies DNAT + firewall DNS in an agent container via docker exec. Reads the current rules from the store to compute TCP port mappings.
func (*Manager) EnsureRunning ¶
EnsureRunning starts the firewall stack if not already running. Generates Envoy/CoreDNS configs from whatever rules are in the store, and ensures containers are running. Rule syncing is a CLI responsibility (call SyncRules before EnsureDaemon). Idempotent — safe to call on every container startup.
func (*Manager) Reload ¶
Reload force-regenerates envoy.yaml and Corefile from current rules and restarts the Envoy container.
func (*Manager) RemoveRules ¶
RemoveRules deletes egress rules from the running firewall (CLI "firewall remove"). Persists, regenerates configs, and hot-reloads.
func (*Manager) Status ¶
func (m *Manager) Status(ctx context.Context) (*FirewallStatus, error)
Status returns a health snapshot of the firewall stack.
func (*Manager) Stop ¶
Stop tears down the firewall stack (containers only, not the network). The network is left in place because monitoring or agent containers may be attached.
func (*Manager) WaitForHealthy ¶
WaitForHealthy polls until both firewall services pass health probes (TCP+HTTP) or the context expires. Probes go through published localhost ports so they work from the host (macOS Docker Desktop doesn't route to container IPs).
- Envoy: TCP connect to localhost:18901 (published TLS listener).
- CoreDNS: HTTP GET to localhost:18902/health (published health endpoint).
type NetworkInfo ¶
NetworkInfo holds discovered state about the firewall Docker network.
type TCPMapping ¶
type TCPMapping struct {
Dst string // Destination domain or IP.
DstPort int // Original destination port (e.g. 22, 8080).
EnvoyPort int // Envoy listener port (TCPPortBase + index).
}
TCPMapping describes a per-destination iptables DNAT entry for non-TLS traffic. Each TCP/SSH rule gets a dedicated Envoy listener port.
func HTTPMappings ¶
func HTTPMappings(rules []config.EgressRule, httpPort int) []TCPMapping
HTTPMappings computes HTTP port mappings from egress rules. Unlike TCP mappings (one listener per rule), all HTTP rules share a single listener because the Host header provides domain detection. Multiple rules on the same port produce a single DNAT entry.
func TCPMappings ¶
func TCPMappings(rules []config.EgressRule, ports EnvoyPorts) []TCPMapping
TCPMappings computes TCP port mappings from egress rules. The result is deterministic for a given rule set — same rules produce same mappings. Used by both GenerateEnvoyConfig (to build listeners) and Enable (to build iptables args).