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
|
## Install
|
||||||
|
|
||||||
go install lerproxy.mleku.dev@latest
|
go install mleku.dev/lerproxy@latest
|
||||||
|
|
||||||
## Run
|
## 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:
|
Options:
|
||||||
--listen LISTEN, -l LISTEN
|
--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
|
* 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:
|
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
|
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
|
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
|
> 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.
|
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
|
## example mapping.txt
|
||||||
|
|
||||||
nostr.example.com: /path/to/nostr.json
|
nostr.example.com: /path/to/nostr.json
|
||||||
@@ -96,7 +85,7 @@ Description=lerproxy
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=username
|
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
|
Restart=on-failure
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
After=network.target network-online.target wg-quick@wg0.service
|
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:
|
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
|
## todo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// Package buf implements a simple concurrent safe buffer pool for raw bytes.
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// Package hsts implements a HTTP handler that enforces HSTS.
|
|
||||||
package hsts
|
package hsts
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Command lerproxy implements https reverse proxy with automatic LetsEncrypt
|
// Command lerproxy implements https reverse proxy with automatic LetsEncrypt
|
||||||
// usage for multiple hostnames/backends,your own SSL certificates, nostr NIP-05
|
// usage for multiple hostnames/backends, and URL rewriting capability.
|
||||||
// DNS verification hosting and Go vanity redirects.
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -15,14 +15,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"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"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -34,14 +26,22 @@ import (
|
|||||||
"github.com/alexflint/go-arg"
|
"github.com/alexflint/go-arg"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"golang.org/x/sync/errgroup"
|
"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
|
//go:embed favicon.ico
|
||||||
var defaultFavicon []byte
|
var defaultFavicon []byte
|
||||||
|
|
||||||
type runArgs struct {
|
type runArgs struct {
|
||||||
Addr string `arg:"-l,--listen" default:":https" help:"address to listen at"`
|
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"`
|
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"`
|
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"`
|
HSTS bool `arg:"-h,--hsts" help:"add Strict-Transport-Security header"`
|
||||||
Email string `arg:"-e,--email" help:"contact email address presented to letsencrypt CA"`
|
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"`
|
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"`
|
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)"`
|
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"`
|
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"`
|
||||||
// Rewrites string `arg:"-r,--rewrites" default:"rewrites.txt"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var args runArgs
|
var args runArgs
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
arg.MustParse(&args)
|
arg.MustParse(&args)
|
||||||
ctx, cancel := signal.NotifyContext(context.Bg(), os.Interrupt)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := run(ctx, args); chk.T(err) {
|
if err := run(ctx, args); err != nil {
|
||||||
log.F.Ln(err)
|
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 == "" {
|
if args.Cache == "" {
|
||||||
err = log.E.Err("no cache specified")
|
err = log.E.Err("no cache specified")
|
||||||
@@ -83,7 +82,7 @@ func run(c context.T, args runArgs) (err error) {
|
|||||||
if args.WTO > 0 {
|
if args.WTO > 0 {
|
||||||
srv.WriteTimeout = args.WTO
|
srv.WriteTimeout = args.WTO
|
||||||
}
|
}
|
||||||
group, ctx := errgroup.WithContext(c)
|
group, ctx := errgroup.WithContext(ctx)
|
||||||
if args.HTTP != "" {
|
if args.HTTP != "" {
|
||||||
httpServer := http.Server{
|
httpServer := http.Server{
|
||||||
Addr: args.HTTP,
|
Addr: args.HTTP,
|
||||||
@@ -100,8 +99,8 @@ func run(c context.T, args runArgs) (err error) {
|
|||||||
group.Go(
|
group.Go(
|
||||||
func() error {
|
func() error {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
ctx, cancel := context.Timeout(
|
ctx, cancel := context.WithTimeout(
|
||||||
context.Bg(),
|
context.Background(),
|
||||||
time.Second,
|
time.Second,
|
||||||
)
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -137,7 +136,9 @@ func run(c context.T, args runArgs) (err error) {
|
|||||||
group.Go(
|
group.Go(
|
||||||
func() error {
|
func() error {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
ctx, cancel := context.Timeout(context.Bg(), time.Second)
|
ctx, cancel := context.WithTimeout(
|
||||||
|
context.Background(), time.Second,
|
||||||
|
)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return srv.Shutdown(ctx)
|
return srv.Shutdown(ctx)
|
||||||
},
|
},
|
||||||
@@ -328,13 +329,12 @@ func setProxy(mapping map[string]string) (h http.Handler, err error) {
|
|||||||
)
|
)
|
||||||
fin := hn + "/favicon.ico"
|
fin := hn + "/favicon.ico"
|
||||||
var fi []byte
|
var fi []byte
|
||||||
if fi, err = os.ReadFile(fin); chk.E(err) {
|
if fi, err = os.ReadFile(fin); !chk.E(err) {
|
||||||
fi = defaultFavicon
|
fi = defaultFavicon
|
||||||
}
|
}
|
||||||
mux.HandleFunc(
|
mux.HandleFunc(
|
||||||
hn+"/favicon.ico",
|
hn+"/favicon.ico",
|
||||||
func(writer http.ResponseWriter, request *http.Request) {
|
func(writer http.ResponseWriter, request *http.Request) {
|
||||||
log.T.F("serving favicon to %s", hn)
|
|
||||||
if _, err = writer.Write(fi); chk.E(err) {
|
if _, err = writer.Write(fi); chk.E(err) {
|
||||||
return
|
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-Credentials", "true")
|
||||||
req.Header.Set("Access-Control-Allow-Origin", "*")
|
req.Header.Set("Access-Control-Allow-Origin", "*")
|
||||||
log.I.Ln(req.URL, req.RemoteAddr)
|
log.D.Ln(req.URL, req.RemoteAddr)
|
||||||
},
|
},
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: func(c context.T, n, addr string) (
|
DialContext: func(
|
||||||
net.Conn, error,
|
ctx context.Context, n, addr string,
|
||||||
) {
|
) (net.Conn, error) {
|
||||||
return net.DialTimeout(network, ba, 5*time.Second)
|
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
|
package reverse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"orly.dev/cmd/lerproxy/util"
|
|
||||||
"orly.dev/pkg/utils/log"
|
"orly.dev/pkg/utils/log"
|
||||||
|
|
||||||
|
"orly.dev/cmd/lerproxy/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSingleHostReverseProxy is a copy of httputil.NewSingleHostReverseProxy
|
// 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
|
package tcpkeepalive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"orly.dev/cmd/lerproxy/timeout"
|
|
||||||
"orly.dev/pkg/utils/chk"
|
"orly.dev/pkg/utils/chk"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"orly.dev/cmd/lerproxy/timeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Period can be changed prior to opening a Listener to alter its'
|
// 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
|
package timeout
|
||||||
|
|
||||||
import (
|
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
|
package util
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
@@ -3,10 +3,10 @@ package ws
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"orly.dev/pkg/utils/context"
|
"orly.dev/pkg/utils/context"
|
||||||
|
"orly.dev/pkg/utils/errorf"
|
||||||
"orly.dev/pkg/utils/units"
|
"orly.dev/pkg/utils/units"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ func (c *Connection) WriteMessage(
|
|||||||
ctx context.T, data []byte,
|
ctx context.T, data []byte,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
if err = c.conn.Write(ctx, ws.MessageText, data); err != nil {
|
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
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -52,11 +52,11 @@ func (c *Connection) ReadMessage(
|
|||||||
) (err error) {
|
) (err error) {
|
||||||
var reader io.Reader
|
var reader io.Reader
|
||||||
if _, reader, err = c.conn.Reader(ctx); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if _, err = io.Copy(buf, reader); err != nil {
|
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
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user