Documentation ¶
Overview ¶
Package router is a client for creating/maintaining http(s) proxies.
Certificates ¶
Certificates are stored as a KeyPair which contains the key and certificate. A tls.Certificate is created and stored separately and used for serving https clients. If a certificate is added, a tls listener will be created, and the X-Forwarded-Proto header will be set to "https" before the request gets proxied to the target. The certificate's Common Name must be set in order for the router to serve it to a matching incoming request.
Start secure routing as follows:
StartTLS("0.0.0.0:443")
Set certificates as follows:
UpdateCerts([]KeyPair{KeyPair{Key: "abcd123", Cert: "1234abc"}})
Get certificates (key/cert pairs) as follows:
keys := Keys()
Routes ¶
Routes have 2 implicit parts: matching criteria and action definitions. The matching portion includes subdomain, domain, and path. The matching algorighm is recursive, so a request to "admin.test.com" would still match the registered route `Route{Domain:"test.com"}`. Precedence is given to routes that match the request's subdomain (if any). Routes with the longest matching path are also prioritized (e.g. a request to "test.com/admin" would match "/admin" in `[]Route{Route{Path:"/"},Route{Path:"/admin"}}` because "/admin" is longer than "/").
The action portion includes targets, fwdpath, and page. If page is specified, it gets served to the client when the request matches that route. Targets is a list of backend servers to proxy to. A target can include a path which, by default, will be prepended to the request's path prior to proxying. If fwdpath is set, it will be appended to any target path and used as the path forwarded to the target.
Start routing as follows:
StartHTTP("0.0.0.0:80")
Set routes as follows:
UpdateRoutes([]Route{Route{Domain: "test.com", Page: "Hello World!\n"}})
Get registered routes as follows:
routes := Routes()
Matching Scenarios ¶
Requests will always match the route with the longest path defined.
ROUTES SUB DOMAIN PATH PAGE "" test.com / "test" "" test.com /admin "admin" CURL REQUEST RESPONSE test.com/admin "admin" test.com/admin/me "admin" admin.test.com "test" test.com/admins "test"
A path can include a "*" at the end to match similar requests.
ROUTES SUB DOMAIN PATH PAGE "" test.com / "test" "" test.com /a* "a is for apple" "" test.com /b/ "b things" CURL REQUEST RESPONSE test.com/a "a is for apple" test.com/ant "a is for apple" test.com/ant/man "a is for apple" test.com/b "test" test.com/b/bear "b things"
A subdomain match takes precedence over a domain/path match.
ROUTES SUB DOMAIN PATH PAGE admin test.com / "admin" "" test.com /bill "Buffalo Bill" CURL REQUEST RESPONSE admin.test.com/bill "admin" users.test.com/bill "Buffalo Bill"
If a Route's matcher has a subdomain only, then all requests with that particular subdomain will have the Route's defined action applied.
ROUTES SUB DOMAIN PATH PAGE admin "" / "admin" "" test1.com / "test1" CURL REQUEST RESPONSE admin.test1.com "admin" admin.test2.com "admin"
Logging ¶
In order to view logs embedded within nanobox-router, you must:
import "github.com/jcelliott/lumber"
and set the level of logging desired (see lumber docs for more info)
lumber.Level(lumber.LvlInt("INFO"))
Index ¶
- Variables
- func NewReverseProxy(target *url.URL, fwdPath string) *httputil.ReverseProxydeprecated
- func NewSingleHostReverseProxy(target *url.URL, fwdPath string, ignore bool, prefixPath string) *httputil.ReverseProxy
- func ServeWS(rw http.ResponseWriter, req *http.Request, p *httputil.ReverseProxy)
- func SetDefaultCert(cert, key string) error
- func Start(httpAddress, tlsAddress string) error
- func StartHTTP(address string) error
- func StartHealth(pulse int)
- func StartTLS(addr string) error
- func UpdateCerts(newKeys []KeyPair) error
- func UpdateRoutes(newRoutes []Route) error
- type KeyPair
- type NoHealthy
- type NoRoutes
- type Route
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoHealthy []byte
ErrNoHealthy is for setting a custom error message/html page if no servers are healthy
var ErrNoRoutes []byte
ErrNoRoutes is for setting a custom error message/html page if no routes are configured
var ErrorHandler http.Handler
allows defining an error and how its handled
var IgnoreUpstreamCerts bool
ignore checking upstream cert (likely will be more granular later)
Functions ¶
func NewReverseProxy
deprecated
func NewReverseProxy(target *url.URL, fwdPath string) *httputil.ReverseProxy
Deprecated: Use NewSingleHostReverseProxy instead
NewReverseProxy is a customized copy of httputil.NewSingleHostReverseProxy that allows optional nginx 'sub_filter'-like behavior (customize "path" forwarded to target)
func NewSingleHostReverseProxy ¶
func NewSingleHostReverseProxy(target *url.URL, fwdPath string, ignore bool, prefixPath string) *httputil.ReverseProxy
NewSingleHostReverseProxy is a customized copy of httputil.NewSingleHostReverseProxy that allows optional nginx 'sub_filter'-like behavior (customize "path" forwarded to target) as well as optionally ignoring upstream cert checking
func ServeWS ¶
func ServeWS(rw http.ResponseWriter, req *http.Request, p *httputil.ReverseProxy)
ServeWS is a combination of ReverseProxy.ServeHTTP from `net/http/httputil` and piping logic from `nanopack/redundis`. It doesn't exactly match rfc spec for html proxying, but since it's more an http connection turned bare tcp, I don't feel bad.
func SetDefaultCert ¶
SetDefaultCert sets the default cert.
func StartHealth ¶
func StartHealth(pulse int)
StartHealth starts the health checking for all registered routes. Only routes with 'Endpoint' defined will get checked. 'pulse' is the delay (in seconds) between health checks (default 60?)
func StartTLS ¶
Start listening for secure connection. The web server is split out from the much simpler form of
http.ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler)
because we needed to handle multiple certs all at the same time and we needed to be able to change the set of certs without restarting the server this can be done by establishing a tls listener seperate form the http Server.
func UpdateCerts ¶
UpdateCerts replaces registered certificates with a new set and restart the secure web server
func UpdateRoutes ¶
UpdateRoutes replaces registered routes with a new set and initializes their proxies, if needed
Types ¶
type KeyPair ¶
A KeyPair contains a key and certificate used to create a tls.Certificate
func Keys ¶
func Keys() []KeyPair
Keys returns registered keys
Example ¶
// self-signed key/cert key := "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDboW1FcXq8rJX\nDwGZ2+solI9YR73/uqG0tp2WzPIMUSQY1FbvD9GO8wSToWdnDHW9M15eiLrk1TAn\nuo99phAovlw5RAsv5vopCf13MKVuWXaSwp6bB52qqLnr5SI2wtJBe5/+LzqUNq5/\nnfsUH0dEBc6hOOUeQPVcd8zAQJblKzg5O90wplqy5Iki4xfGrcF2paB8D4I91X7e\n+JRRVZA79zSzZ4x/opV/fsyL5tfRxoCNn9wnDH2KPR2k/e+A4Tw1fo6TisH4scSp\nMRLjf4Xg7+M72E7SDQ3/5+9d5egynzjT2LjHty8Le5J4fV42jtCQrB/PGys1B8Cx\npNtjo1gvAgMBAAECggEAXFZ7HF1mPyVeuB2h/wVWrbzLocV78zlGMDFcciTxdHpe\nGNEzJg8OT4FpNyu6xIixlKyRuQ7XZ0mHUC4ooBB3cBjJUFFjC8YRipRqywcUEvh4\nOs1zzQIjL8A64EdKDB+u4ju8E4hTIDZZ6nhFanOA45Xu1GQidVHx3DfKaUfbQ/l9\nX+AesqN+fpQBsxfKvYPtaKH8OMjcpLmlSns96r7IY5GQQv1Egy4M1W+Urljgcqim\nFblFOOIFD65nTLsGz6VhENc7gF/ueIv2hrlMYvSQQIM9IdrzGfCYLWzDhzY1x9r3\nvh9Erqn0rub0Rap5Wi7gdM8KIqJEjzp0mYvv2j9hmQKBgQDgLFkIE5j2AQn4S4+n\nFP9GHwgzrFuYOe9FAuoeIeVwcb6eNU6B2ptL3PJ/Pbd1dHcmef9pXUa2cpMo682D\ndQOc1h4kl9mNIvxVIj9Vu6fW0PrOBavGyJLsas0iKxiwzzF9bMt9aqcDphu/hfbB\nnXk70eRG9rUdn6EmvkbtEzSBbQKBgQDfLY4DMq2hhpHeRdLsxMYT3OPyeOcV9boD\nB3bVkxy61XTzFTaVyh6gWx9gxpY9mmv5yH96e93rQaqs5ScIuXrBTvSBTOyRTTw1\nzoZeiH0jN/nMV4x7sdhcrXo7hu7OjqcWGFzMiAYH44E277mrx56dvAgigrIJgPBY\njjX6w2waiwKBgDEwCekHw8xWtgVRLxgON2T/ciFEdGSWcbXGyfAKp/lgO98i+zLq\n8KBYvqzEsfiHsY0zv6My4E0wHrIf61wo1L4ZDUwiNY4OWyei+BqrrkwoVp/WBrb7\nU6GkXZZdtnE1RTqsIIpIWJUoYXZIwrgBAZTqnRglEeCKIiYKIi3qxN6RAoGBAKxX\nsG/1xbGTirdbsjtW5SNXk8ud483IeUF3lSPuu+PnjK1et01KzQXF+GAyWrjts+4r\nD45VcxUGG7fyKYeKPCplP1lOPu0h+JoQhyEfQ4tb4ZIUFY870joXWOn5FBb8gDkG\nzTrA2+9hl1oGG5p0x59FIf8McFH4eSHZiAPCv4trAoGBANw+K8+qmVCLOIpGGYqd\nRl2c2V35Qf17bXlLhv+fEliCI6ixp1fLfglE0IXcGtnSnnUH2cWpC5dlythEfyPH\nAfnZHDvuJ6K0uDgDq90EmwKyHQxihUF57D6oR6FZ3MPqmj41umeQyxC/HGtJm6po\na1Zn/gvZVeitHeVAeDJfJ/J8\n-----END PRIVATE KEY-----" cert := "-----BEGIN CERTIFICATE-----\nMIIDbTCCAlWgAwIBAgIJAM/PXFTYkPDoMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJJRDETMBEGA1UECgwKbmFub2JveC5pbzEcMBoGA1UE\nAwwTbmFub2JveC1yb3V0ZXIudGVzdDAeFw0xNjAzMjIxODQyMTJaFw0xNzAzMjIx\nODQyMTJaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJJRDETMBEGA1UECgwKbmFu\nb2JveC5pbzEcMBoGA1UEAwwTbmFub2JveC1yb3V0ZXIudGVzdDCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMNuhbUVxeryslcPAZnb6yiUj1hHvf+6obS2\nnZbM8gxRJBjUVu8P0Y7zBJOhZ2cMdb0zXl6IuuTVMCe6j32mECi+XDlECy/m+ikJ\n/XcwpW5ZdpLCnpsHnaqouevlIjbC0kF7n/4vOpQ2rn+d+xQfR0QFzqE45R5A9Vx3\nzMBAluUrODk73TCmWrLkiSLjF8atwXaloHwPgj3Vft74lFFVkDv3NLNnjH+ilX9+\nzIvm19HGgI2f3CcMfYo9HaT974DhPDV+jpOKwfixxKkxEuN/heDv4zvYTtINDf/n\n713l6DKfONPYuMe3Lwt7knh9XjaO0JCsH88bKzUHwLGk22OjWC8CAwEAAaNQME4w\nHQYDVR0OBBYEFMRZye+7JAUv7l/44AVnocivjzJ7MB8GA1UdIwQYMBaAFMRZye+7\nJAUv7l/44AVnocivjzJ7MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB\nAH2ygiWZs8pRYWQy6PKj3arci4diFkBiISGTFoAeE1tYkZVE6fM5acPaOV1z7/Fr\nSKeiRhlC7sfcRURaDPDy0of5V83PazQqs3+SNV4KR+O2PNZk6DalKmtwOlNHRKkJ\n5s79rWgqY1wEt4s5atIwVEgdg7WRz41V7WK5Q9IMkFqYVn8MHVKd0k3nuA9ksfXA\nQPBypyOEJGx7EML6Tena/YerpTmcw2Xt4ssxiZQIn/wP3dyqISGark8BNWK6y7iG\nWkt2VZCvKXhb5Q+s4IlxA58InR1b+8/NauYyL1bUgcc3LBHN5Ty6nMUUeb2WPQ32\n4qod6vx2rJfj718EYjrWdaI=\n-----END CERTIFICATE-----" // configure a cert certs := []router.KeyPair{router.KeyPair{Key: key, Cert: cert}} // update the certs router.UpdateCerts(certs) // get certs savedCerts := router.Keys() if len(savedCerts) < 1 { return } fmt.Printf("%v\n", savedCerts[0].Key)
Output: -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDboW1FcXq8rJX DwGZ2+solI9YR73/uqG0tp2WzPIMUSQY1FbvD9GO8wSToWdnDHW9M15eiLrk1TAn uo99phAovlw5RAsv5vopCf13MKVuWXaSwp6bB52qqLnr5SI2wtJBe5/+LzqUNq5/ nfsUH0dEBc6hOOUeQPVcd8zAQJblKzg5O90wplqy5Iki4xfGrcF2paB8D4I91X7e +JRRVZA79zSzZ4x/opV/fsyL5tfRxoCNn9wnDH2KPR2k/e+A4Tw1fo6TisH4scSp MRLjf4Xg7+M72E7SDQ3/5+9d5egynzjT2LjHty8Le5J4fV42jtCQrB/PGys1B8Cx pNtjo1gvAgMBAAECggEAXFZ7HF1mPyVeuB2h/wVWrbzLocV78zlGMDFcciTxdHpe GNEzJg8OT4FpNyu6xIixlKyRuQ7XZ0mHUC4ooBB3cBjJUFFjC8YRipRqywcUEvh4 Os1zzQIjL8A64EdKDB+u4ju8E4hTIDZZ6nhFanOA45Xu1GQidVHx3DfKaUfbQ/l9 X+AesqN+fpQBsxfKvYPtaKH8OMjcpLmlSns96r7IY5GQQv1Egy4M1W+Urljgcqim FblFOOIFD65nTLsGz6VhENc7gF/ueIv2hrlMYvSQQIM9IdrzGfCYLWzDhzY1x9r3 vh9Erqn0rub0Rap5Wi7gdM8KIqJEjzp0mYvv2j9hmQKBgQDgLFkIE5j2AQn4S4+n FP9GHwgzrFuYOe9FAuoeIeVwcb6eNU6B2ptL3PJ/Pbd1dHcmef9pXUa2cpMo682D dQOc1h4kl9mNIvxVIj9Vu6fW0PrOBavGyJLsas0iKxiwzzF9bMt9aqcDphu/hfbB nXk70eRG9rUdn6EmvkbtEzSBbQKBgQDfLY4DMq2hhpHeRdLsxMYT3OPyeOcV9boD B3bVkxy61XTzFTaVyh6gWx9gxpY9mmv5yH96e93rQaqs5ScIuXrBTvSBTOyRTTw1 zoZeiH0jN/nMV4x7sdhcrXo7hu7OjqcWGFzMiAYH44E277mrx56dvAgigrIJgPBY jjX6w2waiwKBgDEwCekHw8xWtgVRLxgON2T/ciFEdGSWcbXGyfAKp/lgO98i+zLq 8KBYvqzEsfiHsY0zv6My4E0wHrIf61wo1L4ZDUwiNY4OWyei+BqrrkwoVp/WBrb7 U6GkXZZdtnE1RTqsIIpIWJUoYXZIwrgBAZTqnRglEeCKIiYKIi3qxN6RAoGBAKxX sG/1xbGTirdbsjtW5SNXk8ud483IeUF3lSPuu+PnjK1et01KzQXF+GAyWrjts+4r D45VcxUGG7fyKYeKPCplP1lOPu0h+JoQhyEfQ4tb4ZIUFY870joXWOn5FBb8gDkG zTrA2+9hl1oGG5p0x59FIf8McFH4eSHZiAPCv4trAoGBANw+K8+qmVCLOIpGGYqd Rl2c2V35Qf17bXlLhv+fEliCI6ixp1fLfglE0IXcGtnSnnUH2cWpC5dlythEfyPH AfnZHDvuJ6K0uDgDq90EmwKyHQxihUF57D6oR6FZ3MPqmj41umeQyxC/HGtJm6po a1Zn/gvZVeitHeVAeDJfJ/J8 -----END PRIVATE KEY-----
type Route ¶
type Route struct { // defines match characteristics SubDomain string `json:"subdomain"` // subdomain to match on - "admin" Domain string `json:"domain"` // domain to match on - "myapp.com" Path string `json:"path"` // route to match on - "/admin" // defines actions Targets []string `json:"targets"` // ips of servers - ["http://127.0.0.1:8080/app1","http://127.0.0.2"] (optional) FwdPath string `json:"fwdpath"` // path to forward to targets - "/goadmin" incoming req: test.com/admin -> 127.0.0.1/goadmin (optional) Page string `json:"page"` // page to serve instead of routing to targets - "<HTML>We are fixing it</HTML>" (optional) // defines health check Endpoint string `json:"endpoint"` // url path to check for health (todo: what to do when fwdpath is set) (non blank enables health checks) ExpectedCode int `json:"expected_code"` // expected http response code (default 200) ExpectedBody string `json:"expected_body"` // expected body ExpectedHeader string `json:"expected_header"` // expected http header (field:value) Host string `json:"host"` // 'host' header to use when performing health check Timeout int `json:"timeout"` // milliseconds before connection times out (default 3000 (3s)) (health check) Attempts int `json:"attempts"` // number of times to try before marking dead (health check) // contains filtered or unexported fields }
A Route contains the routing rules for a specific match. A match is based on the subdomain, domain, and path. If no path is specified, subdomain/domain matching will be used. A path may include the "*" wildcard character at the end of the path ("/admin*") for a broader match.
"Targets" are not required if a "Page" is defined. A target is a list of servers to proxy a request to. A page gets served to clients upon a successful domain/path match. Pages take precedence over targets.
FwdPath is similar to nginx's "sub_filter" as it allows the user to specify what query path gets forwarded to the client.
func Routes ¶
func Routes() []Route
Routes returns registered routes
Example ¶
// configure a route routes := []router.Route{router.Route{Domain: "nanobox-router.test", Targets: []string{"http://127.0.0.1:8088"}}} // update the routes router.UpdateRoutes(routes) // get routes savedRoutes := router.Routes() if len(savedRoutes) < 1 { return } fmt.Printf("%v\n", savedRoutes[0].Domain)
Output: nanobox-router.test