v0.1.1 Latest Latest

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

Go to latest
Published: Oct 12, 2023 License: ISC Imports: 14 Imported by: 0



Package bastion runs a reverse proxy service that allows un-addressable applications (for example those running behind a firewall or a NAT, or where the operator doesn't wish to take the DoS risk of being reachable from the Internet) to accept HTTP requests.

Backends are identified by an Ed25519 public key, they authenticate with a self-signed TLS 1.3 certificate, and are reachable at a sub-path prefixed by the key hash.


package main

import (


func main() {
	// This example shows how to serve on the same address both a bastion
	// endpoint, and an unrelated HTTPS server.

	m := &autocert.Manager{
		Cache:      autocert.DirCache("/var/lib/example-autocert/"),
		Prompt:     autocert.AcceptTOS,
		Email:      "",
		HostPolicy: autocert.HostWhitelist("", ""),

	var allowedBackendsMu sync.RWMutex
	var allowedBackends map[[sha256.Size]byte]bool
	b, err := bastion.New(&bastion.Config{
		AllowedBackend: func(keyHash [sha256.Size]byte) bool {
			defer allowedBackendsMu.RUnlock()
			return allowedBackends[keyHash]
		GetCertificate: m.GetCertificate,
	if err != nil {
		log.Fatalf("failed to load bastion: %v", err)

	mux := http.NewServeMux()
	// Note the use of a host-specific pattern to route HTTP requests for the
	// bastion endpoint to the Bastion implementation.
	mux.Handle("", b)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "<p>Hello, world")

	hs := &http.Server{
		Addr:         "",
		Handler:      http.MaxBytesHandler(mux, 10*1024),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		TLSConfig:    m.TLSConfig(),
	// ConfigureServer sets up TLSNextProto and a tls.Config.GetConfigForClient
	// for backend connections.
	if err := b.ConfigureServer(hs); err != nil {
		log.Fatalln("failed to configure bastion:", err)
	// HTTP/2 needs to be explicitly re-enabled if desired because it's only
	// configured automatically by net/http if TLSNextProto is nil.
	if err := http2.ConfigureServer(hs, nil); err != nil {
		log.Fatalln("failed to configure HTTP/2:", err)




This section is empty.


This section is empty.


This section is empty.


type Bastion

type Bastion struct {
	// contains filtered or unexported fields

A Bastion keeps track of backend connections, and serves HTTP requests by routing them to the matching backend.

func New

func New(c *Config) (*Bastion, error)

New returns a new Bastion.

The Config must not be modified after the call to New.

func (*Bastion) ConfigureServer

func (b *Bastion) ConfigureServer(srv *http.Server) error

ConfigureServer sets up srv to handle backend connections to the bastion. It wraps TLSConfig.GetConfigForClient to intercept backend connections, and sets TLSNextProto for the bastion ALPN protocol. The original tls.Config is still used for non-bastion backend connections.

Note that since TLSNextProto won't be nil after a call to ConfigureServer, the caller might want to call http2.ConfigureServer as well.

func (*Bastion) ServeHTTP

func (b *Bastion) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP serves requests rooted at "/<hex key hash>/" by routing them to the backend that authenticated with that key. Other requests are served a 404 Not Found status.

type Config

type Config struct {
	// GetCertificate returns the certificate for bastion backend connections.
	GetCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)

	// AllowedBackend returns whether the backend is allowed to
	// serve requests. It's passed the hash of its Ed25519 public key.
	// AllowedBackend may be called concurrently.
	AllowedBackend func(keyHash [sha256.Size]byte) bool

	// Log is used to log backend connections and errors in forwarding requests.
	// If nil, [log.Default] is used.
	Log *log.Logger

Config provides parameters for a new Bastion.

Jump to

Keyboard shortcuts

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