201 lines
4.3 KiB
Go
201 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"time"
|
|
|
|
"git.xeserv.us/xena/route/internal/database"
|
|
"git.xeserv.us/xena/route/internal/tun2"
|
|
proto "git.xeserv.us/xena/route/proto"
|
|
"github.com/Xe/ln"
|
|
"github.com/mtneug/pkg/ulid"
|
|
kcp "github.com/xtaci/kcp-go"
|
|
"golang.org/x/crypto/acme/autocert"
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
)
|
|
|
|
// RPC constants
|
|
const (
|
|
RPCPort uint16 = 39453
|
|
)
|
|
|
|
// Server is the main server type
|
|
type Server struct {
|
|
cfg *Config
|
|
db database.Storage
|
|
ts *tun2.Server
|
|
|
|
*autocert.Manager
|
|
}
|
|
|
|
// Config configures Server
|
|
type Config struct {
|
|
BoltDBPath string `env:"BOLTDB_PATH,required"`
|
|
|
|
WebAddr string `env:"WEB_ADDR,required"`
|
|
SSLAddr string `env:"SSL_ADDR,required"`
|
|
QuicAddr string `env:"QUIC_ADDR,required"`
|
|
BackendTCPAddr string `env:"BACKEND_TCP_ADDR,required"`
|
|
BackendKCPAddr string `env:"BACKEND_KCP_ADDR,required"`
|
|
GRPCAddr string `env:"GRPC_ADDR,required"`
|
|
|
|
DomainSuffix string `env:"DOMAIN_SUFFIX,required"`
|
|
ACMEEmail string `env:"ACME_EMAIL,required"`
|
|
CertKey *[32]byte
|
|
}
|
|
|
|
func (s *Server) listenTCP(ctx context.Context, addr string, tcfg *tls.Config) {
|
|
l, err := tls.Listen("tcp", addr, tcfg)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ln.Log(ctx, ln.Action("tcp+tls listening"), ln.F{"addr": l.Addr()})
|
|
|
|
for {
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
ln.Error(ctx, err, ln.Action("accept backend client socket"))
|
|
}
|
|
|
|
ln.Log(ctx, ln.F{
|
|
"action": "new backend client",
|
|
"addr": conn.RemoteAddr(),
|
|
"network": conn.RemoteAddr().Network(),
|
|
})
|
|
|
|
go s.ts.HandleConn(conn, false)
|
|
}
|
|
}
|
|
|
|
func (s *Server) listenKCP(ctx context.Context, addr string, tcfg *tls.Config) {
|
|
l, err := kcp.Listen(addr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ln.Log(ctx, ln.Action("kcp+tls listening"), ln.F{"addr": l.Addr()})
|
|
|
|
for {
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
ln.Error(ctx, err, ln.F{"kind": "kcp", "addr": l.Addr().String()})
|
|
}
|
|
|
|
ln.Log(ctx, ln.F{
|
|
"action": "new_client",
|
|
"network": conn.RemoteAddr().Network(),
|
|
"addr": conn.RemoteAddr(),
|
|
})
|
|
|
|
tc := tls.Server(conn, tcfg)
|
|
|
|
go s.ts.HandleConn(tc, true)
|
|
}
|
|
}
|
|
|
|
// New creates a new Server
|
|
func New(cfg Config) (*Server, error) {
|
|
if cfg.CertKey == nil {
|
|
return nil, errors.New("no cert decryption key, can't do anything")
|
|
}
|
|
|
|
db, err := database.NewBoltStorage(cfg.BoltDBPath, cfg.CertKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m := &autocert.Manager{
|
|
Prompt: autocert.AcceptTOS,
|
|
Cache: database.Cache(db),
|
|
HostPolicy: nil,
|
|
Email: cfg.ACMEEmail,
|
|
}
|
|
|
|
tcfg := &tun2.ServerConfig{
|
|
Storage: &storageWrapper{
|
|
Storage: db,
|
|
},
|
|
}
|
|
|
|
ts, err := tun2.NewServer(tcfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := &Server{
|
|
cfg: &cfg,
|
|
db: db,
|
|
ts: ts,
|
|
|
|
Manager: m,
|
|
}
|
|
|
|
tc := &tls.Config{
|
|
GetCertificate: m.GetCertificate,
|
|
}
|
|
|
|
go s.listenKCP(context.Background(), cfg.BackendKCPAddr, tc)
|
|
go s.listenTCP(context.Background(), cfg.BackendTCPAddr, tc)
|
|
|
|
// gRPC setup
|
|
gs := grpc.NewServer(grpc.Creds(credentials.NewTLS(tc)))
|
|
|
|
proto.RegisterBackendsServer(gs, &Backend{Server: s})
|
|
proto.RegisterRoutesServer(gs, &Route{Server: s})
|
|
proto.RegisterTokensServer(gs, &Token{Server: s})
|
|
|
|
l, err := net.Listen("tcp", cfg.GRPCAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go gs.Serve(l)
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// Director removes headers that are typically stripped off at request ingress.
|
|
func (s *Server) Director(r *http.Request) {
|
|
r.Header.Del("X-Forwarded-For")
|
|
r.Header.Del("X-Client-Ip")
|
|
}
|
|
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("X-Tor2web") != "" {
|
|
http.Error(w, "tor2web proxy use is not allowed", 400)
|
|
return
|
|
}
|
|
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
r.Header.Set("X-Remote-Ip", host)
|
|
r.Header.Set("X-Request-Ingress", time.Now().Format(time.RFC3339))
|
|
|
|
rid := ulid.New().String()
|
|
r.Header.Set("X-Request-Id", rid)
|
|
w.Header().Set("X-Request-Id", rid)
|
|
w.Header().Add("Alt-Svc", fmt.Sprintf(`quic="%s"; ma=2592000; v="39"`, s.cfg.QuicAddr))
|
|
|
|
// http://www.gnuterrypratchett.com/
|
|
w.Header().Set("X-Clacks-Overhead", "GNU Ashlynn")
|
|
|
|
rp := &httputil.ReverseProxy{
|
|
Director: s.Director,
|
|
Transport: s.ts,
|
|
FlushInterval: 1 * time.Second,
|
|
}
|
|
|
|
rp.ServeHTTP(w, r)
|
|
}
|