vproxy

package module
v0.12.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2023 License: MIT Imports: 28 Imported by: 0

README

vproxy

A zero-config service mesh for local development

Stop trying to remember port numbers and create secure endpoints for your services. Automatically updates your hosts file, generates TLS certificates, (via mkcert/truststore), and binds a reverse proxy with a single command.

No third-party dependencies. No Docker. No Kubernetes.

Quickstart

brew install jittering/kegs/vproxy
vproxy caroot --create
sudo brew services start vproxy
vproxy connect hello.local:8888 -- vproxy hello

Now open https://hello.local in your browser!

Installation

via homebrew (mac or linux):

brew install jittering/kegs/vproxy

or manually (macOS, linux, windows):

  • Download a pre-built binary
  • Install in proper location, e.g., /usr/local/bin/ or %SYSTEMROOT%\System32\

or build it from source:

go install github.com/jittering/vproxy/bin/vproxy@master
Initialize local root CA

Create and install a new local root CA in your system:

vproxy caroot --create

Usage

vproxy can run in two modes, depending on the use case: single-process for proxying a single service or with a standalone daemon for proxying more than one service.

vproxy consists of two processes: daemon and client.

  • The daemon serves as the primary host of the HTTP & HTTPS endpoints for your applications.
  • The client registers a service with the daemon and relays all access logs to the current terminal. It can also optionally run the service for you.

A single daemon is required per-host, while clients can be run multiple times.

In single-process mode, skip the daemon section.

daemon

If installed via homebrew on macOS, running it as a service is easy:

sudo brew services start vproxy

Note that you must run as root to bind to privileged ports (hence the use of sudo above).

Or run it manually:

$ vproxy daemon
[*] rerunning with sudo
Password:
[*] starting proxy: http://127.0.0.1:80
[*] starting proxy: https://127.0.0.1:443
client

Use the connect command to bind a hostname to a local port:

$ vproxy connect --bind foo.local.com:5000
[*] registering vhost: foo.local.com:5000

The daemon will automatically:

  • Issue a TLS cert for foo.local.com
  • Add foo.local.com to your hosts file (e.g., /etc/hosts)
  • Add a reverse proxy vhost connecting foo.local.com to port 5000

You can even run the underlying service with one command, for ease of use:

$ vproxy connect --bind foo.local.com:5000 -- flask run
[*] running command: /usr/local/bin/flask run
[*] registering vhost: foo.local.com:5000
 * Serving Flask app "app/main.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Now visit https://foo.local.com to access your application originally running on http://127.0.0.1:5000

When you stop the client process (i.e., by pressing ^C), vproxy will deregister the vhost with the daemon and send a TERM signal to it's child process.

Permissions

A couple of notes on permissions. The vproxy daemon must be run with elevated privileges for the following reasons:

  • macOS or Linux: binding on privileged ports 80/443 and modifying /etc/hosts
  • Windows: modifying %SYSTEMROOT%\System32\drivers\etc\hosts
  • All: installing our local CA into the system trust stores (system prompt)

On mac or linux this means running as root (or via sudo).

On Windows this means running via an elevated command prompt.

License

MIT, (c) 2022, Pixelcop Research, Inc.

Originally based on simpleproxy - MIT (c) 2016 aybars badur.

Documentation

Index

Constants

View Source
const PONG = "hello from vproxy"

PONG server identifier

Variables

View Source
var VERBOSE = false

Controls whether verbose messages should be printed

Functions

func CARootPath

func CARootPath() string

func CertPath

func CertPath() string

func CreateProxy

func CreateProxy(targetURL url.URL, vhost string) *httputil.ReverseProxy

CreateProxy with custom http.RoundTripper impl. Sets proper host headers using given vhost name.

func InitTrustStore added in v0.10.1

func InitTrustStore() error

func InstallTrustStore added in v0.10.1

func InstallTrustStore() error

func MakeCert

func MakeCert(host string) (certFile string, keyFile string, err error)

MakeCert for the give hostname, if it doesn't already exist.

func StartHello added in v0.10.1

func StartHello(host string, port int) error

StartHello world service on the given host/port

This is a simple service which always responds with 'Hello World'. It's mainly here to serve as a simple demo of vproxy's abilities (see readme).

func UninstallTrustStore added in v0.10.1

func UninstallTrustStore() error

Types

type Client added in v0.10.1

type Client struct {
	Addr string
	// contains filtered or unexported fields
}

func (*Client) AddBinding added in v0.12.1

func (c *Client) AddBinding(bind string, detach bool)

Add single binding. Blocks when detach=false

func (*Client) AddBindings added in v0.10.1

func (c *Client) AddBindings(binds []string, detach bool, args []string)

func (*Client) IsDaemonRunning added in v0.10.1

func (c *Client) IsDaemonRunning() bool

IsDaemonRunning tries to check if a vproxy daemon is already running on the given addr

func (*Client) RemoveVhost added in v0.10.1

func (c *Client) RemoveVhost(hostname string, all bool)

func (*Client) RunCommand added in v0.12.1

func (c *Client) RunCommand(args []string)

func (*Client) Tail added in v0.10.1

func (c *Client) Tail(hostname string, follow bool)

type Daemon added in v0.4.1

type Daemon struct {
	// contains filtered or unexported fields
}

Daemon service which hosts all the virtual reverse proxies

proxy chain: daemon -> mux (LoggedHandler) -> /* -> VhostMux -> Vhost -> ReverseProxy -> upstream service

func NewDaemon added in v0.4.1

func NewDaemon(lh *LoggedHandler, listen string, httpPort int, httpsPort int) *Daemon

NewDaemon

func (*Daemon) Run added in v0.4.1

func (d *Daemon) Run()

Run the daemon service. Does not return until the service is killed.

type LogListener added in v0.10.1

type LogListener chan string

type LogRecord

type LogRecord struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

LogRecord is a thin wrapper around http.ResponseWriter which allows us to capture the number of response bytes written and the http status code.

func (*LogRecord) Hijack added in v0.6.1

func (r *LogRecord) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error)

Hijack wrapper

func (*LogRecord) Write

func (r *LogRecord) Write(p []byte) (int, error)

Write wrapper that counts bytes

func (*LogRecord) WriteHeader

func (r *LogRecord) WriteHeader(status int)

WriteHeader wrapper that captures status code

type LoggedHandler added in v0.6.2

type LoggedHandler struct {
	*http.ServeMux
	// contains filtered or unexported fields
}

LoggedHandler is an extension of http.ServeMux which multiplexes requests to the vhost backends (via a handler) and logs each request. TODO: replace ServeMux with a proper router (chi?)

func NewLoggedHandler added in v0.6.2

func NewLoggedHandler(vm *VhostMux) *LoggedHandler

NewLoggedHandler wraps the given handler with a request/response logger

func (*LoggedHandler) AddVhost added in v0.6.2

func (lh *LoggedHandler) AddVhost(vhost *Vhost)

func (*LoggedHandler) CreateTLSConfig added in v0.6.2

func (lh *LoggedHandler) CreateTLSConfig() *tls.Config

Create multi-certificate TLS config from vhost config

func (*LoggedHandler) DumpServers added in v0.6.2

func (lh *LoggedHandler) DumpServers(w io.Writer)

DumpServers to the given writer

func (*LoggedHandler) GetVhost added in v0.10.1

func (lh *LoggedHandler) GetVhost(host string) *Vhost

func (*LoggedHandler) RemoveVhost added in v0.6.3

func (lh *LoggedHandler) RemoveVhost(host string)

func (*LoggedHandler) ServeHTTP added in v0.6.2

func (lh *LoggedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Vhost

type Vhost struct {
	Host string // virtual host name

	ServiceHost string // service host or IP
	Port        int    // service port

	Handler http.Handler
	Cert    string // TLS Certificate
	Key     string // TLS Private Key
	// contains filtered or unexported fields
}

Vhost represents a single backend service

func CreateVhost

func CreateVhost(input string, useTLS bool) (*Vhost, error)

CreateVhost for the host:port pair, optionally with a TLS cert

func (*Vhost) BufferAsString added in v0.10.1

func (v *Vhost) BufferAsString() string

func (*Vhost) Close added in v0.10.1

func (v *Vhost) Close()

func (*Vhost) NewLogListener added in v0.10.1

func (v *Vhost) NewLogListener() LogListener

func (*Vhost) RemoveLogListener added in v0.10.1

func (v *Vhost) RemoveLogListener(logChan LogListener)

type VhostMux

type VhostMux struct {
	Servers map[string]*Vhost
}

VhostMux is an http.Handler whose ServeHTTP forwards the request to backend Servers according to the incoming request URL

func CreateVhostMux

func CreateVhostMux(bindings []string, useTLS bool) *VhostMux

CreateVhostMux config, optionally initialized with a list of bindings

func (*VhostMux) DumpServers added in v0.10.1

func (v *VhostMux) DumpServers(w io.Writer)

DumpServers to the given writer

func (*VhostMux) ServeHTTP

func (v *VhostMux) ServeHTTP(w http.ResponseWriter, r *http.Request)

Directories

Path Synopsis
bin

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL