Documentation
¶
Overview ¶
Package middleware provides helpful functions that implement some common functionalities in http servers. A middleware is a function that takes in a http.Handler as one of its arguments and returns a http.Handler
The middlewares All, Get, Post, Head, Put & Delete wrap other internal middleware. The effect of this is that the aforementioned middleware, in addition to their specialised functionality, will:
- Add logID for traceability.
- Add the "real" client IP address to the request context.
- Add client TLS fingerprint to the request context.
- Recover from panics in the wrappedHandler.
- Log http requests and responses.
- Rate limit requests by IP address.
- Shed load based on http response latencies.
- Handle automatic procurement/renewal of ACME tls certifiates.
- Redirect http requests to https.
- Add some important HTTP security headers and assign them sensible default values.
- Implement Cross-Origin Resource Sharing support(CORS).
- Provide protection against Cross Site Request Forgeries(CSRF).
- Attempt to provide protection against form re-submission when a user reloads an already submitted web form.
- Implement http sessions.
Example (GetCspNonce) ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func loginHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cspNonce := middleware.GetCspNonce(r.Context()) _ = cspNonce _, _ = fmt.Fprint(w, "welcome to your favorite website.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) handler := middleware.Get( loginHandler(), middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l), ) _ = handler // use handler }
Output:
Example (GetCsrfToken) ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func welcomeHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { csrfToken := middleware.GetCsrfToken(r.Context()) _ = csrfToken _, _ = fmt.Fprint(w, "welcome.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) handler := middleware.Get( welcomeHandler(), middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l), ) _ = handler // use handler }
Output:
Index ¶
- Constants
- func All(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func BasicAuth(wrappedHandler http.Handler, user, passwd string) http.HandlerFunc
- func ClientFingerPrint(r *http.Request) string
- func ClientIP(r *http.Request) string
- func Delete(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Get(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func GetCspNonce(c context.Context) string
- func GetCsrfToken(c context.Context) string
- func Head(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Post(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- func Put(wrappedHandler http.Handler, o Opts) http.HandlerFunc
- type ClientIPstrategy
- type Opts
- func New(domain string, acmeEmail string, acmeDirectoryUrl string, httpsPort uint16, ...) Opts
- func WithAcmeOpts(domain string, acmeEmail string, acmeDirectoryUrl string, httpsPort uint16, ...) Opts
- func WithLetsEncryptOpts(domain string, acmeEmail string, httpsPort uint16, secretKey string, ...) Opts
- func WithOpts(domain string, httpsPort uint16, secretKey string, strategy ClientIPstrategy, ...) Opts
Examples ¶
Constants ¶
const ( // DirectIpStrategy derives the client IP from [http.Request.RemoteAddr]. // It should be used if the server accepts direct connections, rather than through a proxy. // // See the warning in [ClientIP] DirectIpStrategy = ClientIPstrategy("DirectIpStrategy") // LeftIpStrategy derives the client IP from the leftmost valid & non-private IP address in the `X-Fowarded-For` or `Forwarded` header. // // See the warning in [ClientIP] LeftIpStrategy = ClientIPstrategy("LeftIpStrategy") // RightIpStrategy derives the client IP from the rightmost valid & non-private IP address in the `X-Fowarded-For` or `Forwarded` header. // // See the warning in [ClientIP] RightIpStrategy = ClientIPstrategy("RightIpStrategy") // ProxyStrategy derives the client IP from the [PROXY protocol v1]. // This should be used when your application is behind a TCP proxy that uses the v1 PROXY protocol. // // See the warning in [ClientIP] // // [PROXY protocol v1]: https://www.haproxy.org/download/2.8/doc/proxy-protocol.txt ProxyStrategy = ClientIPstrategy("ProxyStrategy") )
const ( // CsrfTokenFormName is the name of the html form name attribute for csrf token. CsrfTokenFormName = "csrftoken" // named after what django uses. // CsrfHeader is the name of the http header that Ong uses to store csrf token. CsrfHeader = "X-Csrf-Token" // named after what fiber uses. )
Variables ¶
This section is empty.
Functions ¶
func All ¶
func All(wrappedHandler http.Handler, o Opts) http.HandlerFunc
All is a middleware that allows all http methods.
See the package documentation for the additional functionality provided by this middleware.
Example ¶
package main import ( "context" "io" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func main() { l := log.New(os.Stdout, 100)(context.Background()) opts := middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l) myHandler := http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { _, _ = io.WriteString(w, "Hello from a HandleFunc \n") }, ) handler := middleware.All(myHandler, opts) mux := http.NewServeMux() mux.Handle("/", handler) }
Output:
func BasicAuth ¶
func BasicAuth(wrappedHandler http.Handler, user, passwd string) http.HandlerFunc
BasicAuth is a middleware that protects wrappedHandler using basic authentication.
func ClientFingerPrint ¶ added in v0.0.44
ClientFingerPrint returns the TLS fingerprint of the client. It is provided on a best-effort basis. If a fingerprint is not found, it returns a string that has the substring "NotFound" in it. There are different formats/algorithms of fingerprinting, this library(by design) does not subscribe to a particular format or algorithm.
func ClientIP ¶ added in v0.0.26
ClientIP returns the "real" client IP address. This will be based on the ClientIPstrategy that you chose.
Warning: This should be used with caution. Clients CAN easily spoof IP addresses. Fetching the "real" client is done in a best-effort basis and can be grossly inaccurate & precarious. You should especially heed this warning if you intend to use the IP addresses for security related activities. Proceed at your own risk.
func Delete ¶
func Delete(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Delete is a middleware that only allows http DELETE requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
func Get ¶
func Get(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Get is a middleware that only allows http GET requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
Example ¶
package main import ( "context" "fmt" "net/http" "os" "github.com/komuw/ong/log" "github.com/komuw/ong/middleware" ) func loginHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cspNonce := middleware.GetCspNonce(r.Context()) _ = cspNonce _, _ = fmt.Fprint(w, "welcome to your favorite website.") } } func main() { l := log.New(os.Stdout, 100)(context.Background()) opts := middleware.WithOpts("example.com", 443, "secretKey", middleware.DirectIpStrategy, l) handler := middleware.Get(loginHandler(), opts) _ = handler // use handler }
Output:
func GetCspNonce ¶
GetCspNonce returns the Content-Security-Policy nonce that was set for the http request in question.
func GetCsrfToken ¶
GetCsrfToken returns the csrf token that was set for the http request in question.
func Head ¶
func Head(wrappedHandler http.Handler, o Opts) http.HandlerFunc
Head is a middleware that only allows http HEAD requests and http OPTIONS requests.
See the package documentation for the additional functionality provided by this middleware.
Types ¶
type ClientIPstrategy ¶ added in v0.0.26
type ClientIPstrategy string
ClientIPstrategy is a middleware option that describes the strategy to use when fetching the client's IP address.
func SingleIpStrategy ¶ added in v0.0.26
func SingleIpStrategy(headerName string) ClientIPstrategy
SingleIpStrategy derives the client IP from http header headerName.
headerName MUST NOT be either `X-Forwarded-For` or `Forwarded`. It can be something like `CF-Connecting-IP`, `Fastly-Client-IP`, `Fly-Client-IP`, etc; depending on your usecase.
See the warning in ClientIP
type Opts ¶
type Opts struct {
// contains filtered or unexported fields
}
Opts are the various parameters(optionals) that can be used to configure middlewares.
Use either New or WithOpts to get a valid Opts.
func New ¶ added in v0.0.14
func New( domain string, acmeEmail string, acmeDirectoryUrl string, httpsPort uint16, allowedOrigins []string, allowedMethods []string, allowedHeaders []string, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
New returns a new Opts.
domain is the domain name of your website. It can be an exact domain, subdomain or wildcard. New panics if domain is invalid. acmeEmail is the e-address that will be used if/when procuring certificates from an ACME certificate authority, eg letsencrypt. acmeDirectoryUrl is the URL of the ACME certificate authority's directory endpoint. acmeEmail & acmeDirectoryUrl can be empty if you do not need the services of ACME.
httpsPort is the tls port where http requests will be redirected to.
allowedOrigins, allowedMethods, & allowedHeaders are used by the CORS middleware. If allowedOrigins is nil, all origins are allowed. You can also use * to allow all. If allowedMethods is nil, "GET", "POST", "HEAD" are allowed. Use * to allow all. If allowedHeaders is nil, "Origin", "Accept", "Content-Type", "X-Requested-With" are allowed. Use * to allow all.
secretKey is used for securing signed data. It should be unique & kept secret. If it becomes compromised, generate a new one and restart your application using the new one.
strategy is the algorithm to use when fetching the client's IP address; see ClientIPstrategy. It is important to choose your strategy carefully, see the warning in ClientIP.
func WithAcmeOpts ¶ added in v0.0.56
func WithAcmeOpts( domain string, acmeEmail string, acmeDirectoryUrl string, httpsPort uint16, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
WithAcmeOpts returns a new opts suitable for use in applications that require TLS certificates from ACME. Also see WithLetsEncryptOpts See New for extra documentation.
func WithLetsEncryptOpts ¶ added in v0.0.56
func WithLetsEncryptOpts( domain string, acmeEmail string, httpsPort uint16, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
WithLetsEncryptOpts returns a new opts suitable for use in applications that require TLS certificates from letsencrypt. Also see WithAcmeOpts
func WithOpts ¶
func WithOpts( domain string, httpsPort uint16, secretKey string, strategy ClientIPstrategy, l *slog.Logger, ) Opts
WithOpts returns a new Opts that has sensible defaults. See New for extra documentation.
Example ¶
package main import ( "os" "github.com/komuw/ong/middleware" "golang.org/x/exp/slog" ) func main() { l := slog.New(slog.NewTextHandler(os.Stdout, nil)) opts := middleware.WithOpts( "example.com", 443, "secretKey", // assuming your application is deployed behind cloudflare. middleware.SingleIpStrategy("CF-Connecting-IP"), l, ) _ = opts }
Output: