update: reorganize imports, add URL rewriting support, and minor refactoring
- cmd/lerproxy/reverse/proxy.go - Reorganized imports for logical grouping. - cmd/lerproxy/main.go - Added URL rewriting capability and updated command-line usage documentation. - Reorganized imports for consistency. - Replaced `context.T` with `context.Context` for standardization. - Updated timeout handling logic to use `context.WithTimeout`. - pkg/protocol/ws/connection.go - Replaced `fmt.Errorf` with `errorf.E` for error formatting. - cmd/lerproxy/util/util.go - Renamed file for better clarity. - Removed unnecessary package documentation. - cmd/lerproxy/hsts/proxy.go - Removed redundant package comments. - cmd/lerproxy/tcpkeepalive/listener.go - Removed redundant package comments. - Adjusted import order. - cmd/lerproxy/buf/bufpool.go - Removed unnecessary package comments. - cmd/lerproxy/README.md - Updated package usage examples and installation instructions. - Removed outdated and unnecessary instructions.
This commit is contained in:
0
cmd/lerproxy/LICENSE
Normal file → Executable file
0
cmd/lerproxy/LICENSE
Normal file → Executable file
21
cmd/lerproxy/README.md
Normal file → Executable file
21
cmd/lerproxy/README.md
Normal file → Executable file
@@ -6,12 +6,12 @@ DNS verification [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.
|
||||
|
||||
## Install
|
||||
|
||||
go install lerproxy.mleku.dev@latest
|
||||
go install mleku.dev/lerproxy@latest
|
||||
|
||||
## Run
|
||||
|
||||
```
|
||||
Usage: lerproxy.mleku.dev [--listen LISTEN] [--map MAP] [--rewrites REWRITES] [--cachedir CACHEDIR] [--hsts] [--email EMAIL] [--http HTTP] [--rto RTO] [--wto WTO] [--idle IDLE] [--cert CERT]
|
||||
Usage: mleku.dev/lerproxy [--listen LISTEN] [--map MAP] [--rewrites REWRITES] [--cachedir CACHEDIR] [--hsts] [--email EMAIL] [--http HTTP] [--rto RTO] [--wto WTO] [--idle IDLE] [--cert CERT]
|
||||
|
||||
Options:
|
||||
--listen LISTEN, -l LISTEN
|
||||
@@ -49,7 +49,7 @@ as:
|
||||
* in the launch parameters for `lerproxy` you can now add any number of `--cert` parameters with
|
||||
the domain (including for wildcards), and the path to the `.crt`/`.key` files:
|
||||
|
||||
lerproxy.mleku.dev --cert <domain>:/path/to/TLS_cert
|
||||
mleku.dev/lerproxy --cert <domain>:/path/to/TLS_cert
|
||||
|
||||
this will then, if found, load and parse the TLS certificate and secret key if the suffix of
|
||||
the domain matches. The certificate path is expanded to two files with the above filename
|
||||
@@ -58,17 +58,6 @@ as:
|
||||
> Note that the match is greedy, so you can explicitly separately give a subdomain
|
||||
certificate and it will be selected even if there is a wildcard that also matches.
|
||||
|
||||
# IMPORTANT
|
||||
|
||||
With Comodo SSL (sectigo RSA) certificates you also need to append the intermediate certificate
|
||||
to the `.crt` file in order to get it to work properly with openssl library based tools like
|
||||
wget, curl and the go tool, which is quite important if you want to do subdomains on a wildcard
|
||||
certificate.
|
||||
|
||||
Probably the same applies to some of the other certificate authorities. If you sometimes get
|
||||
issues with CLI tools refusing to accept these certificates on your web server or other, this
|
||||
may be the problem.
|
||||
|
||||
## example mapping.txt
|
||||
|
||||
nostr.example.com: /path/to/nostr.json
|
||||
@@ -96,7 +85,7 @@ Description=lerproxy
|
||||
[Service]
|
||||
Type=simple
|
||||
User=username
|
||||
ExecStart=/usr/local/bin/lerproxy.mleku.dev -m /path/to/mapping.txt -l xxx.xxx.xxx.xxx:443 --http xxx.xxx.xxx.6:80 -m /path/to/mapping.txt -e email@example.com -c /path/to/letsencrypt/cache --cert example.com:/path/to/tls/certs
|
||||
ExecStart=/usr/local/bin/mleku.dev/lerproxy -m /path/to/mapping.txt -l xxx.xxx.xxx.xxx:443 --http xxx.xxx.xxx.6:80 -m /path/to/mapping.txt -e email@example.com -c /path/to/letsencrypt/cache --cert example.com:/path/to/tls/certs
|
||||
Restart=on-failure
|
||||
Wants=network-online.target
|
||||
After=network.target network-online.target wg-quick@wg0.service
|
||||
@@ -114,7 +103,7 @@ a tunnel, such as your dev machine (something I do for nostr relay development)
|
||||
|
||||
The simplest way to allow `lerproxy` to bind to port 80 and 443 is as follows:
|
||||
|
||||
setcap 'cap_net_bind_service=+ep' /path/to/lerproxy.mleku.dev
|
||||
setcap 'cap_net_bind_service=+ep' /path/to/mleku.dev/lerproxy
|
||||
|
||||
## todo
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Package buf implements a simple concurrent safe buffer pool for raw bytes.
|
||||
package buf
|
||||
|
||||
import "sync"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Package hsts implements a HTTP handler that enforces HSTS.
|
||||
package hsts
|
||||
|
||||
import "net/http"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Command lerproxy implements https reverse proxy with automatic LetsEncrypt
|
||||
// usage for multiple hostnames/backends,your own SSL certificates, nostr NIP-05
|
||||
// DNS verification hosting and Go vanity redirects.
|
||||
// usage for multiple hostnames/backends, and URL rewriting capability.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
@@ -15,14 +15,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"orly.dev/cmd/lerproxy/buf"
|
||||
"orly.dev/cmd/lerproxy/hsts"
|
||||
"orly.dev/cmd/lerproxy/reverse"
|
||||
"orly.dev/cmd/lerproxy/tcpkeepalive"
|
||||
"orly.dev/cmd/lerproxy/util"
|
||||
"orly.dev/pkg/utils/chk"
|
||||
"orly.dev/pkg/utils/context"
|
||||
"orly.dev/pkg/utils/log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -34,14 +26,22 @@ import (
|
||||
"github.com/alexflint/go-arg"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"orly.dev/cmd/lerproxy/buf"
|
||||
"orly.dev/cmd/lerproxy/hsts"
|
||||
"orly.dev/cmd/lerproxy/reverse"
|
||||
"orly.dev/cmd/lerproxy/tcpkeepalive"
|
||||
"orly.dev/cmd/lerproxy/util"
|
||||
"orly.dev/pkg/utils/chk"
|
||||
"orly.dev/pkg/utils/log"
|
||||
)
|
||||
|
||||
//go:embed favicon.ico
|
||||
var defaultFavicon []byte
|
||||
|
||||
type runArgs struct {
|
||||
Addr string `arg:"-l,--listen" default:":https" help:"address to listen at"`
|
||||
Conf string `arg:"-m,--map" default:"mapping.txt" help:"file with host/backend mapping"`
|
||||
Addr string `arg:"-l,--listen" default:":https" help:"address to listen at"`
|
||||
Conf string `arg:"-m,--map" default:"mapping.txt" help:"file with host/backend mapping"`
|
||||
// Rewrites string `arg:"-r,--rewrites" default:"rewrites.txt"`
|
||||
Cache string `arg:"-c,--cachedir" default:"/var/cache/letsencrypt" help:"path to directory to cache key and certificates"`
|
||||
HSTS bool `arg:"-h,--hsts" help:"add Strict-Transport-Security header"`
|
||||
Email string `arg:"-e,--email" help:"contact email address presented to letsencrypt CA"`
|
||||
@@ -49,22 +49,21 @@ type runArgs struct {
|
||||
RTO time.Duration `arg:"-r,--rto" default:"1m" help:"maximum duration before timing out read of the request"`
|
||||
WTO time.Duration `arg:"-w,--wto" default:"5m" help:"maximum duration before timing out write of the response"`
|
||||
Idle time.Duration `arg:"-i,--idle" help:"how long idle connection is kept before closing (set rto, wto to 0 to use this)"`
|
||||
Certs []string `arg:"--cert,separate" help:"certificates and the domain they match: eg: orly.dev:/path/to/cert - this will indicate to load two, one with extension .key and one with .crt, each expected to be PEM encoded TLS private and public keys, respectively"`
|
||||
// Rewrites string `arg:"-r,--rewrites" default:"rewrites.txt"`
|
||||
Certs []string `arg:"--cert,separate" help:"certificates and the domain they match: eg: mleku.dev:/path/to/cert - this will indicate to load two, one with extension .key and one with .crt, each expected to be PEM encoded TLS private and public keys, respectively"`
|
||||
}
|
||||
|
||||
var args runArgs
|
||||
|
||||
func main() {
|
||||
arg.MustParse(&args)
|
||||
ctx, cancel := signal.NotifyContext(context.Bg(), os.Interrupt)
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
if err := run(ctx, args); chk.T(err) {
|
||||
if err := run(ctx, args); err != nil {
|
||||
log.F.Ln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(c context.T, args runArgs) (err error) {
|
||||
func run(ctx context.Context, args runArgs) (err error) {
|
||||
|
||||
if args.Cache == "" {
|
||||
err = log.E.Err("no cache specified")
|
||||
@@ -83,7 +82,7 @@ func run(c context.T, args runArgs) (err error) {
|
||||
if args.WTO > 0 {
|
||||
srv.WriteTimeout = args.WTO
|
||||
}
|
||||
group, ctx := errgroup.WithContext(c)
|
||||
group, ctx := errgroup.WithContext(ctx)
|
||||
if args.HTTP != "" {
|
||||
httpServer := http.Server{
|
||||
Addr: args.HTTP,
|
||||
@@ -100,8 +99,8 @@ func run(c context.T, args runArgs) (err error) {
|
||||
group.Go(
|
||||
func() error {
|
||||
<-ctx.Done()
|
||||
ctx, cancel := context.Timeout(
|
||||
context.Bg(),
|
||||
ctx, cancel := context.WithTimeout(
|
||||
context.Background(),
|
||||
time.Second,
|
||||
)
|
||||
defer cancel()
|
||||
@@ -137,7 +136,9 @@ func run(c context.T, args runArgs) (err error) {
|
||||
group.Go(
|
||||
func() error {
|
||||
<-ctx.Done()
|
||||
ctx, cancel := context.Timeout(context.Bg(), time.Second)
|
||||
ctx, cancel := context.WithTimeout(
|
||||
context.Background(), time.Second,
|
||||
)
|
||||
defer cancel()
|
||||
return srv.Shutdown(ctx)
|
||||
},
|
||||
@@ -328,13 +329,12 @@ func setProxy(mapping map[string]string) (h http.Handler, err error) {
|
||||
)
|
||||
fin := hn + "/favicon.ico"
|
||||
var fi []byte
|
||||
if fi, err = os.ReadFile(fin); chk.E(err) {
|
||||
if fi, err = os.ReadFile(fin); !chk.E(err) {
|
||||
fi = defaultFavicon
|
||||
}
|
||||
mux.HandleFunc(
|
||||
hn+"/favicon.ico",
|
||||
func(writer http.ResponseWriter, request *http.Request) {
|
||||
log.T.F("serving favicon to %s", hn)
|
||||
if _, err = writer.Write(fi); chk.E(err) {
|
||||
return
|
||||
}
|
||||
@@ -376,12 +376,12 @@ func setProxy(mapping map[string]string) (h http.Handler, err error) {
|
||||
)
|
||||
// req.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
req.Header.Set("Access-Control-Allow-Origin", "*")
|
||||
log.I.Ln(req.URL, req.RemoteAddr)
|
||||
log.D.Ln(req.URL, req.RemoteAddr)
|
||||
},
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(c context.T, n, addr string) (
|
||||
net.Conn, error,
|
||||
) {
|
||||
DialContext: func(
|
||||
ctx context.Context, n, addr string,
|
||||
) (net.Conn, error) {
|
||||
return net.DialTimeout(network, ba, 5*time.Second)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Package reverse is a copy of httputil.NewSingleHostReverseProxy with addition
|
||||
// of "X-Forwarded-Proto" header.
|
||||
package reverse
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"orly.dev/cmd/lerproxy/util"
|
||||
"orly.dev/pkg/utils/log"
|
||||
|
||||
"orly.dev/cmd/lerproxy/util"
|
||||
)
|
||||
|
||||
// NewSingleHostReverseProxy is a copy of httputil.NewSingleHostReverseProxy
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// Package tcpkeepalive implements a net.TCPListener with a singleton set period
|
||||
// for a default 3 minute keep-aline.
|
||||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"net"
|
||||
"orly.dev/cmd/lerproxy/timeout"
|
||||
"orly.dev/pkg/utils/chk"
|
||||
"time"
|
||||
|
||||
"orly.dev/cmd/lerproxy/timeout"
|
||||
)
|
||||
|
||||
// Period can be changed prior to opening a Listener to alter its'
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// Package timeout provides a simple extension of a net.TCPConn with a
|
||||
// configurable read/write deadline.
|
||||
package timeout
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Package util provides some helpers for lerproxy, a tool to convert maps of
|
||||
// strings to slices of the same strings, and a helper to avoid putting two / in
|
||||
// a URL.
|
||||
package util
|
||||
|
||||
import "strings"
|
||||
@@ -3,10 +3,10 @@ package ws
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"orly.dev/pkg/utils/context"
|
||||
"orly.dev/pkg/utils/errorf"
|
||||
"orly.dev/pkg/utils/units"
|
||||
"time"
|
||||
|
||||
@@ -40,7 +40,7 @@ func (c *Connection) WriteMessage(
|
||||
ctx context.T, data []byte,
|
||||
) (err error) {
|
||||
if err = c.conn.Write(ctx, ws.MessageText, data); err != nil {
|
||||
err = fmt.Errorf("failed to write message: %w", err)
|
||||
err = errorf.E("failed to write message: %w", err)
|
||||
return
|
||||
}
|
||||
return nil
|
||||
@@ -52,11 +52,11 @@ func (c *Connection) ReadMessage(
|
||||
) (err error) {
|
||||
var reader io.Reader
|
||||
if _, reader, err = c.conn.Reader(ctx); err != nil {
|
||||
err = fmt.Errorf("failed to get reader: %w", err)
|
||||
err = errorf.E("failed to get reader: %w", err)
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(buf, reader); err != nil {
|
||||
err = fmt.Errorf("failed to read message: %w", err)
|
||||
err = errorf.E("failed to read message: %w", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user