server: support new database, shim certificate management

This commit is contained in:
Cadey Ratio 2017-04-28 16:27:34 -07:00
parent e8acea0351
commit 30ebdbac7d
2 changed files with 73 additions and 158 deletions

View File

@ -1,27 +1,15 @@
package server package server
import ( import (
"crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/pem"
"errors" "errors"
"log"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/rpc"
"path/filepath"
"strings"
"time" "time"
"git.xeserv.us/xena/route/database" "git.xeserv.us/xena/route/database"
"git.xeserv.us/xena/route/lib/elfs"
"git.xeserv.us/xena/route/lib/tun2" "git.xeserv.us/xena/route/lib/tun2"
"git.xeserv.us/xena/route/routerpc"
"github.com/Xe/uuid"
"github.com/Yawning/bulb"
"github.com/brandur/simplebox"
"github.com/mtneug/pkg/ulid" "github.com/mtneug/pkg/ulid"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
) )
@ -34,114 +22,81 @@ const (
// Server is the main server type // Server is the main server type
type Server struct { type Server struct {
cfg *Config cfg *Config
db database.Storage
db *database.DB
rpcS *rpc.Server
rpcAddr string
ts *tun2.Server ts *tun2.Server
CertCache *database.CertCache autocert.Manager
} }
// Config configures Server // Config configures Server
type Config struct { type Config struct {
RethinkDBHost string BoltDBPath string `env:"BOLTDB_PATH,required"`
RethinkDBDatabase string
TorDataDir string WebAddr string `env:"WEB_ADDR,required"`
TorHashedPassword string SSLAddr string `env:"SSL_ADDR,required"`
TorPassword string BackendTCPAddr string `env:"BACKEND_TCP_ADDR,required"`
BackendKCPAddr string `env:"BACKEND_KCP_ADDR,required"`
GRPCAddr string `env:"GRPC_ADDR,required"`
WebPort string DomainSuffix string `env:"DOMAIN_SUFFIX,required"`
SSLPort string ACMEEmail string `env:"ACME_EMAIL,required"`
BackendPort string
KCPPort string
DomainSuffix string
ACMEEmail string
CertKey *[32]byte CertKey *[32]byte
} }
// New creates a new Server // New creates a new Server
func New(cfg Config) (*Server, error) { func New(cfg Config) (*Server, error) {
db, err := database.New(cfg.RethinkDBHost, cfg.RethinkDBDatabase)
if err != nil {
return nil, err
}
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, err
}
rpcs := rpc.NewServer()
s := &Server{
cfg: &cfg,
db: db,
rpcS: rpcs,
rpcAddr: l.Addr().String(),
CertCache: &database.CertCache{
DB: db,
},
}
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: s.CertCache,
HostPolicy: nil,
Email: cfg.ACMEEmail,
}
if cfg.CertKey == nil { if cfg.CertKey == nil {
return nil, errors.New("no cert decryption key, can't do anything") return nil, errors.New("no cert decryption key, can't do anything")
} }
s.CertCache.SimpleBox = simplebox.NewFromSecretKey(cfg.CertKey) 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{ tcfg := &tun2.ServerConfig{
TCPAddr: cfg.BackendPort, TCPAddr: cfg.BackendTCPAddr,
KCPAddr: cfg.KCPPort, KCPAddr: cfg.BackendKCPAddr,
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
GetCertificate: m.GetCertificate, GetCertificate: m.GetCertificate,
}, },
Storage: s.db, Storage: &storageWrapper{
Storage: db,
},
} }
ts, err := tun2.NewServer(tcfg) ts, err := tun2.NewServer(tcfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s := &Server{
cfg: &cfg,
db: db,
ts: ts,
Manager: m,
}
s.ts = ts s.ts = ts
go ts.ListenAndServe() go ts.ListenAndServe()
rpcs.RegisterName("Urls", &RPCServer{Server: s})
go rpcs.Accept(l)
log.Println("rpc at tcp://" + l.Addr().String())
err = s.restore()
if err != nil {
return nil, err
}
return s, nil return s, nil
} }
func (s *Server) onionPath(name string) string { // Director removes headers that are typically stripped off at request ingress.
return filepath.Join(s.cfg.TorDataDir, name) func (s *Server) Director(r *http.Request) {
}
func (s *Server) Director(r *http.Request) {}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.Header.Del("X-Forwarded-For") r.Header.Del("X-Forwarded-For")
r.Header.Del("X-Client-Ip") r.Header.Del("X-Client-Ip")
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Tor2web") != "" { if r.Header.Get("X-Tor2web") != "" {
http.Error(w, "tor2web proxy use is not allowed", 400) http.Error(w, "tor2web proxy use is not allowed", 400)
return return
@ -162,15 +117,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// http://www.gnuterrypratchett.com/ // http://www.gnuterrypratchett.com/
w.Header().Set("X-Clacks-Overhead", "GNU Ashlynn") w.Header().Set("X-Clacks-Overhead", "GNU Ashlynn")
if strings.HasSuffix(r.Host, ".onion") {
r.Header.Add("DNT", "1")
}
if r.RequestURI == rpc.DefaultRPCPath {
s.rpcS.ServeHTTP(w, r)
return
}
rp := &httputil.ReverseProxy{ rp := &httputil.ReverseProxy{
Director: s.Director, Director: s.Director,
Transport: s.ts, Transport: s.ts,
@ -179,68 +125,3 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rp.ServeHTTP(w, r) rp.ServeHTTP(w, r)
} }
func (s *Server) restore() error {
rts, err := s.db.GetAllRoutes()
if err != nil {
return err
}
for _, rt := range rts {
var block *pem.Block
block, _ = pem.Decode([]byte(rt.OnionKey))
_, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return err
}
log.Printf("added: %s (%s)", rt.Hostname, rt.OnionHostname)
}
return nil
}
func genPortSpec(incoming uint16, outgoing string) bulb.OnionPortSpec {
return bulb.OnionPortSpec{
VirtPort: incoming,
Target: outgoing,
}
}
// RPCServer is a Server wrapped to work inside the context of net/rpc
type RPCServer struct {
*Server
}
// AddHost adds a host to the server.
func (rs *RPCServer) AddHost(req routerpc.AddHostRequest, resp *routerpc.AddHostResponse) error {
if req.APIKey != "hunter2" {
return errors.New("invalid api key")
}
token := uuid.New()
var pKey *rsa.PrivateKey
if kk, ok := req.PrivKey.(*rsa.PrivateKey); ok {
pKey = kk
} else {
return errors.New("there must be a 1024 bit RSA private key")
}
resp.Token = token
resp.PrivKey = pKey
if req.Hostname != "" {
resp.Hostname = req.Hostname
} else {
resp.Hostname = elfs.MakeName() + rs.cfg.DomainSuffix
}
err := rs.db.SaveRoute(resp)
if err != nil {
return err
}
return nil
}

34
server/storage.go Normal file
View File

@ -0,0 +1,34 @@
package server
import (
"context"
"git.xeserv.us/xena/route/database"
"git.xeserv.us/xena/route/lib/tun2"
)
type storageWrapper struct {
database.Storage
}
var (
_ tun2.Storage = &storageWrapper{}
)
func (s *storageWrapper) HasToken(token string) (string, []string, error) {
t, err := s.Storage.GetToken(context.Background(), token)
if err != nil {
return "", nil, err
}
return t.Owner, t.Scopes, nil
}
func (s *storageWrapper) HasRoute(domain string) (string, error) {
r, err := s.Storage.GetRoute(context.Background(), domain)
if err != nil {
return "", err
}
return r.Creator, nil
}