From 62fa5c31ba39ebf968f98a7ee46525f14c0ea97c Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Wed, 23 Mar 2016 16:05:38 +0100 Subject: Rewrite into config-les setup --- .gitignore | 2 +- Dockerfile | 2 +- certs/goxy.yml | 11 ----- data/.gitkeep | 0 gob.go | 24 +++++++++++ goxyctl/main.go | 68 ++++++++++++++++++++++++++++++ main.go | 126 ++++++++++++++++++++++++++------------------------------ rpc.go | 36 ++++++++++++++++ 8 files changed, 189 insertions(+), 80 deletions(-) delete mode 100644 certs/goxy.yml create mode 100644 data/.gitkeep create mode 100644 gob.go create mode 100644 goxyctl/main.go create mode 100644 rpc.go diff --git a/.gitignore b/.gitignore index 9127b54..a1fb9c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -goxy *.swp +*.gob diff --git a/Dockerfile b/Dockerfile index caef533..5b1a2b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,4 +3,4 @@ RUN go get dim13.org/goxy VOLUME /go/src/dim13.org/goxy/certs WORKDIR /go/src/dim13.org/goxy ENTRYPOINT /go/bin/goxy -EXPOSE 80 443 +EXPOSE 80 443 8080 diff --git a/certs/goxy.yml b/certs/goxy.yml deleted file mode 100644 index a24e58d..0000000 --- a/certs/goxy.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- - -wahlplan.goxy.moccu: - upstream: http://wahlplan:8080 - certfile: certs/wahlplan/cert.pem - keyfile: certs/wahlplan/key.pem - -gowiki.goxy.moccu: - upstream: http://gowiki - certfile: certs/gowiki/cert.pem - keyfile: certs/gowiki/key.pem diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/gob.go b/gob.go new file mode 100644 index 0000000..3932a65 --- /dev/null +++ b/gob.go @@ -0,0 +1,24 @@ +package main + +import ( + "encoding/gob" + "os" +) + +func (r Route) Save(fname string) error { + fd, err := os.Create(fname) + if err != nil { + return err + } + defer fd.Close() + return gob.NewEncoder(fd).Encode(r) +} + +func (r *Route) Load(fname string) error { + fd, err := os.Open(fname) + if err != nil { + return err + } + defer fd.Close() + return gob.NewDecoder(fd).Decode(r) +} diff --git a/goxyctl/main.go b/goxyctl/main.go new file mode 100644 index 0000000..824f892 --- /dev/null +++ b/goxyctl/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "crypto/rsa" + "crypto/tls" + "encoding/gob" + "flag" + "log" + "net/rpc" +) + +var ( + rpcserver = flag.String("server", ":http-alt", "RPC Server port") + servername = flag.String("servername", "", "from") + upstream = flag.String("upstream", "", "to") + keyfile = flag.String("keyfile", "", "TLS Key file") + crtfile = flag.String("crtfile", "", "TLC Crt file") + remove = flag.Bool("remove", false, "remove entry") +) + +type Entry struct { + ServerName string + Upstream string + Cert *tls.Certificate +} + +func init() { + gob.Register(rsa.PrivateKey{}) +} + +func main() { + flag.Parse() + + e := Entry{ + ServerName: *servername, + Upstream: *upstream, + } + + if *keyfile != "" && *crtfile != "" { + crt, err := tls.LoadX509KeyPair(*crtfile, *keyfile) + if err != nil { + log.Fatal(err) + } + e.Cert = &crt + } + + client, err := rpc.DialHTTP("tcp", *rpcserver) + if err != nil { + log.Fatal(err) + } + defer client.Close() + + switch { + case e.ServerName != "" && e.Upstream != "": + err = client.Call("GoXY.Add", e, nil) + case e.ServerName != "" && *remove: + err = client.Call("GoXY.Del", e, nil) + default: + var r map[string]Entry + err = client.Call("GoXY.List", struct{}{}, &r) + for k, v := range r { + log.Println(k, v) + } + } + if err != nil { + log.Fatal(err) + } +} diff --git a/main.go b/main.go index 946603f..e54cd96 100644 --- a/main.go +++ b/main.go @@ -4,107 +4,99 @@ import ( "crypto/tls" "errors" "flag" - "io/ioutil" "log" "net/http" "net/http/httputil" + "net/rpc" "net/url" - "sync" - - "gopkg.in/yaml.v2" ) var ( config = flag.String("conf", "certs/goxy.yml", "configuration file") - listen = flag.String("listen", ":http", "HTTP") - listenTLS = flag.String("listentls", ":https", "TLS") + listen = flag.String("listen", ":http", "HTTP port") + listenTLS = flag.String("listentls", ":https", "TLS port") + listenRPC = flag.String("listenrpc", ":http-alt", "RPC port") + data = flag.String("data", "data/goxy.gob", "persistent storage file") + route = make(Route) + mux = http.NewServeMux() ) -type Config map[string]Route +type Route map[string]Entry -type Route struct { - CertFile string - KeyFile string - Upstream string - cert *tls.Certificate +type Entry struct { + ServerName string + Upstream string + Cert *tls.Certificate } -func (c Config) SNI(h *tls.ClientHelloInfo) (*tls.Certificate, error) { - if r, ok := c[h.ServerName]; ok && r.cert != nil { - return r.cert, nil +func (r Route) SNI(h *tls.ClientHelloInfo) (*tls.Certificate, error) { + if e, ok := r[h.ServerName]; ok && e.Cert != nil { + return e.Cert, nil } return nil, errors.New("no cert for " + h.ServerName) } -func (r *Route) LoadCert() error { - if r.CertFile == "" && r.KeyFile == "" { - return nil +func (r Route) Restore() { + *mux = *http.NewServeMux() + for _, e := range route { + e.NewHandle() } - cert, err := tls.LoadX509KeyPair(r.CertFile, r.KeyFile) +} + +func (e Entry) NewHandle() error { + log.Println("New handle", e) + up, err := url.Parse(e.Upstream) if err != nil { return err } - r.cert = &cert + mux.Handle(e.ServerName+"/", httputil.NewSingleHostReverseProxy(up)) return nil } -func (r Route) String() string { - if r.cert != nil { - return r.Upstream + " with TLS" +func (e Entry) String() string { + if e.Cert != nil { + return e.ServerName + " -> " + e.Upstream + " with TLS" } else { - return r.Upstream + return e.ServerName + " -> " + e.Upstream } } -func LoadConfig(fname string) (Config, error) { - conf, err := ioutil.ReadFile(fname) - if err != nil { - return Config{}, err +func StartHTTP(listen string) { + log.Println("listen", listen, "(HTTP)") + s := http.Server{ + Addr: listen, + Handler: mux, + } + log.Fatal(s.ListenAndServe()) +} + +func StartTLS(listen string) { + log.Println("listen", listen, "(TLS)") + s := http.Server{ + Addr: listen, + Handler: mux, + TLSConfig: &tls.Config{GetCertificate: route.SNI}, } - var c Config - return c, yaml.Unmarshal(conf, &c) + log.Fatal(s.ListenAndServeTLS("", "")) +} + +func StartRPC(listen string) { + log.Println("listen", listen, "(RPC)") + rpc.HandleHTTP() + log.Fatal(http.ListenAndServe(listen, nil)) } func main() { flag.Parse() - c, err := LoadConfig(*config) - if err != nil { - log.Fatal(err) - } - for k, v := range c { - if err := v.LoadCert(); err != nil { - log.Println("load", err) - continue - } - c[k] = v - up, err := url.Parse(v.Upstream) - if err != nil { - log.Println("upstream", err) - continue - } - log.Println("map", k, "to", v) - http.Handle(k+"/", httputil.NewSingleHostReverseProxy(up)) + if err := route.Load(*data); err != nil { + log.Println(err) } + route.Restore() + + go StartHTTP(*listen) + go StartTLS(*listenTLS) + go StartRPC(*listenRPC) - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - log.Println("listen", *listenTLS) - s := http.Server{ - Addr: *listenTLS, - TLSConfig: &tls.Config{GetCertificate: c.SNI}, - } - log.Fatal(s.ListenAndServeTLS("", "")) - }() - go func() { - defer wg.Done() - log.Println("listen", *listen) - s := http.Server{ - Addr: *listen, - } - log.Fatal(s.ListenAndServe()) - }() - wg.Wait() + select {} } diff --git a/rpc.go b/rpc.go new file mode 100644 index 0000000..589ca92 --- /dev/null +++ b/rpc.go @@ -0,0 +1,36 @@ +package main + +import ( + "crypto/rsa" + "encoding/gob" + "log" + "net/rpc" +) + +type GoXY struct{} + +func init() { + gob.Register(rsa.PrivateKey{}) + rpc.Register(GoXY{}) +} + +func (GoXY) Add(e Entry, _ *struct{}) error { + log.Println("Add route", e) + defer route.Save(*data) + route[e.ServerName] = e + route.Restore() + return nil +} + +func (GoXY) Del(e Entry, _ *struct{}) error { + log.Println("Del route", e) + defer route.Save(*data) + delete(route, e.ServerName) + route.Restore() + return nil +} + +func (GoXY) List(_ struct{}, r *Route) error { + *r = route + return nil +} -- cgit v1.2.3