route/cmd/routed/server.go

238 lines
5.9 KiB
Go
Raw Normal View History

2017-12-15 18:18:13 +00:00
package main
import (
2017-03-26 19:51:37 +00:00
"crypto/tls"
"errors"
"net"
"net/http"
2017-03-26 19:51:37 +00:00
"net/http/httputil"
"time"
2017-09-30 13:41:35 +00:00
"git.xeserv.us/xena/route/internal/database"
2018-01-21 16:22:10 +00:00
"git.xeserv.us/xena/route/internal/middleware"
2017-10-01 15:23:08 +00:00
"git.xeserv.us/xena/route/internal/tun2"
2017-04-29 03:08:11 +00:00
proto "git.xeserv.us/xena/route/proto"
2017-10-04 07:00:16 +00:00
"github.com/Xe/ln"
2018-01-03 19:12:21 +00:00
"github.com/lucas-clemente/quic-go/h2quic"
2017-03-26 19:51:37 +00:00
"github.com/mtneug/pkg/ulid"
2017-10-04 07:00:16 +00:00
kcp "github.com/xtaci/kcp-go"
2017-03-26 19:51:37 +00:00
"golang.org/x/crypto/acme/autocert"
2017-10-04 07:00:16 +00:00
"golang.org/x/net/context"
)
// RPC constants
const (
RPCPort uint16 = 39453
)
// Server is the main server type.
type Server struct {
2017-01-20 00:31:22 +00:00
cfg *Config
db database.Storage
ts *tun2.Server
2018-01-03 19:12:21 +00:00
QuicServer *h2quic.Server
2017-04-29 03:08:11 +00:00
*autocert.Manager
}
2017-01-20 00:31:22 +00:00
// Config configures Server
type Config struct {
BoltDBPath string `env:"BOLTDB_PATH,required"`
2017-03-27 04:39:19 +00:00
WebAddr string `env:"WEB_ADDR,required"`
SSLAddr string `env:"SSL_ADDR,required"`
2017-12-12 02:51:45 +00:00
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"`
2017-03-27 04:39:19 +00:00
DomainSuffix string `env:"DOMAIN_SUFFIX,required"`
ACMEEmail string `env:"ACME_EMAIL,required"`
2017-03-27 04:39:19 +00:00
CertKey *[32]byte
}
// listenTCP configures a listener for TCP+TLS agent connections.
2017-10-04 07:00:16 +00:00
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)
}
}
// listenKCP configures a listener for KCP+TLS agent connections.
2017-10-04 07:00:16 +00:00
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()})
2017-10-04 07:00:16 +00:00
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
2017-01-20 00:31:22 +00:00
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
}
2017-04-29 03:08:11 +00:00
m := &autocert.Manager{
2017-03-26 19:51:37 +00:00
Prompt: autocert.AcceptTOS,
Cache: database.Cache(db),
2017-03-26 19:51:37 +00:00
HostPolicy: nil,
2017-03-27 04:39:19 +00:00
Email: cfg.ACMEEmail,
2017-03-26 19:51:37 +00:00
}
tcfg := &tun2.ServerConfig{
Storage: &storageWrapper{
Storage: db,
},
2017-03-26 19:51:37 +00:00
}
2017-03-26 19:51:37 +00:00
ts, err := tun2.NewServer(tcfg)
if err != nil {
return nil, err
}
2017-10-04 07:00:16 +00:00
s := &Server{
cfg: &cfg,
db: db,
ts: ts,
2017-03-26 19:51:37 +00:00
Manager: m,
2017-01-28 00:14:05 +00:00
}
2017-10-06 17:54:09 +00:00
tc := &tls.Config{
2017-10-04 07:00:16 +00:00
GetCertificate: m.GetCertificate,
2017-10-06 17:54:09 +00:00
}
2017-10-04 07:00:16 +00:00
2017-10-06 17:54:09 +00:00
go s.listenKCP(context.Background(), cfg.BackendKCPAddr, tc)
go s.listenTCP(context.Background(), cfg.BackendTCPAddr, tc)
2018-01-21 17:24:15 +00:00
withHandler := func(disc string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(ln.WithF(r.Context(), ln.F{"disc": disc})))
})
}
2018-01-21 16:22:10 +00:00
bhdr := proto.NewBackendsServer(&Backend{Server: s}, s.makeTwirpHooks())
rhdr := proto.NewRoutesServer(&Route{Server: s}, s.makeTwirpHooks())
thdr := proto.NewTokensServer(&Token{Server: s}, s.makeTwirpHooks())
mux := http.NewServeMux()
2017-04-29 03:08:11 +00:00
2018-01-21 17:24:15 +00:00
mux.Handle(proto.BackendsPathPrefix, withHandler("backends", bhdr))
mux.Handle(proto.RoutesPathPrefix, withHandler("routes", rhdr))
mux.Handle(proto.TokensPathPrefix, withHandler("tokens", thdr))
2017-04-29 03:08:11 +00:00
2018-01-21 16:22:10 +00:00
hs := &http.Server{
TLSConfig: tc,
Addr: cfg.GRPCAddr,
2018-01-21 17:07:21 +00:00
Handler: middleware.SaveHeaders(middleware.Trace("twirp-https")(mux)),
2017-04-29 03:08:11 +00:00
}
2018-01-21 17:13:30 +00:00
ln.Log(context.Background(), ln.F{
"kind": "api",
"backends": proto.BackendsPathPrefix,
"routes": proto.RoutesPathPrefix,
"tokens": proto.TokensPathPrefix,
"addr": cfg.GRPCAddr,
})
2018-01-21 16:22:10 +00:00
go hs.ListenAndServeTLS("", "")
2017-04-29 03:08:11 +00:00
return s, nil
}
// Director removes headers that are typically stripped off at request ingress.
func (s *Server) Director(r *http.Request) {
2017-01-22 17:49:14 +00:00
r.Header.Del("X-Forwarded-For")
2017-03-26 20:47:42 +00:00
r.Header.Del("X-Client-Ip")
}
2017-01-22 17:49:14 +00:00
// ServeHTTP proxies traffic to a remote backend based on the request meta-information.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2017-01-22 17:49:14 +00:00
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
}
2017-03-26 20:47:42 +00:00
r.Header.Set("X-Remote-Ip", host)
r.Header.Set("X-Request-Ingress", time.Now().Format(time.RFC3339))
2017-01-20 01:17:18 +00:00
2017-03-26 19:51:37 +00:00
rid := ulid.New().String()
2017-01-23 16:03:14 +00:00
r.Header.Set("X-Request-Id", rid)
w.Header().Set("X-Request-Id", rid)
2018-01-03 19:12:21 +00:00
s.QuicServer.SetQuicHeaders(w.Header())
2017-01-23 16:03:14 +00:00
2017-02-08 02:25:14 +00:00
// http://www.gnuterrypratchett.com/
2017-03-26 19:51:37 +00:00
w.Header().Set("X-Clacks-Overhead", "GNU Ashlynn")
2017-02-08 02:25:14 +00:00
2017-03-26 19:51:37 +00:00
rp := &httputil.ReverseProxy{
2017-03-26 20:02:22 +00:00
Director: s.Director,
2017-03-26 19:51:37 +00:00
Transport: s.ts,
FlushInterval: 1 * time.Second,
}
rp.ServeHTTP(w, r)
}
2018-01-17 04:49:45 +00:00
// insecureRedirect redirects a client to https if they connect over plain HTTP.
func insecureRedirect(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPatch, http.MethodPut, http.MethodPost:
http.Error(w, "use https", http.StatusNotAcceptable)
ln.Log(r.Context(), ln.Action("cannot redirect (wrong method)"), ln.F{"remote": r.RemoteAddr, "host": r.Host, "path": r.URL.Path})
return
}
r.URL.Host = r.Host
r.URL.Scheme = "https"
ln.Log(r.Context(), ln.Action("redirecting insecure HTTP to HTTPS"), ln.F{"remote": r.RemoteAddr, "host": r.Host, "path": r.URL.Path})
http.Redirect(w, r, r.URL.String(), http.StatusPermanentRedirect)
}