package server import ( "crypto/tls" "errors" "net" "net/http" "net/http/httputil" "time" "git.xeserv.us/xena/route/internal/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) }