route/server/server.go

148 lines
3.1 KiB
Go

package server
import (
"crypto/tls"
"errors"
"net"
"net/http"
"net/http/httputil"
"time"
"git.xeserv.us/xena/route/database"
"git.xeserv.us/xena/route/lib/tun2"
proto "git.xeserv.us/xena/route/proto"
"github.com/mtneug/pkg/ulid"
"github.com/oxtoacart/bpool"
"golang.org/x/crypto/acme/autocert"
"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"`
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
}
// 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{
TCPAddr: cfg.BackendTCPAddr,
KCPAddr: cfg.BackendKCPAddr,
TLSConfig: &tls.Config{
GetCertificate: m.GetCertificate,
},
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,
}
s.ts = ts
go ts.ListenAndServe()
gs := grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
GetCertificate: m.GetCertificate,
InsecureSkipVerify: true,
})))
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)
// 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,
BufferPool: bpool.NewBytePool(256, 4096),
}
rp.ServeHTTP(w, r)
}