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:
2025-08-18 20:13:14 +01:00
parent a928294234
commit a51e86f4c4
10 changed files with 40 additions and 60 deletions

0
cmd/lerproxy/LICENSE Normal file → Executable file
View File

21
cmd/lerproxy/README.md Normal file → Executable file
View 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

View File

@@ -1,4 +1,3 @@
// Package buf implements a simple concurrent safe buffer pool for raw bytes.
package buf
import "sync"

View File

@@ -1,4 +1,3 @@
// Package hsts implements a HTTP handler that enforces HSTS.
package hsts
import "net/http"

View File

@@ -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)
},
},

View File

@@ -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

View File

@@ -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'

View File

@@ -1,5 +1,3 @@
// Package timeout provides a simple extension of a net.TCPConn with a
// configurable read/write deadline.
package timeout
import (

View File

@@ -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"

View File

@@ -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