//AddSSHBundles hands a default bundle to the supplied director func AddSSHBundles(x ProxyDirector, sv SSHStreamer, serv *SSHStreamServer, check bool) error { if serv == nil { return ErrBadBundle } if sv == nil { return ErrBadBundle } x.Register("ssh", func(req *ProxyRequest, d *Director, a Action) error { var err error var cert *tls.Config if req.CertFile != "" && req.KeyFile != "" { cert, err = LoadTLS(req.CertFile, req.KeyFile) if err != nil { return err } } return x.ServeCustomTCP(req.From, cert, func(con net.Conn, d Directors) error { addr := net.JoinHostPort(req.Addr, fmt.Sprintf("%d", req.Port)) // addr := net.JoinHostPort(req.Addr, string(req.Port)) sl, cl, err := serv.StreamConnection(con, addr, nil) flux.Report(err, fmt.Sprintf("Initiated ssh authentication Scheme for %s and %s", con.RemoteAddr().String(), addr)) if err != nil { return err } cn, err := sv.Stream(sl, cl) flux.Report(err, fmt.Sprintf("Initiated ssh proxy Scheme for %s -> %s", con.RemoteAddr().String(), addr)) if err != nil { return err } a(cn) return nil }, check) }) return nil }
//DialClient returns two channels where one returns a ssh.Client and the other and error func DialClient(dial, expire time.Duration, ip string, conf *ssh.ClientConfig, retry <-chan struct{}) (*ssh.Client, error) { flux.Report(nil, fmt.Sprintf("MakeDial for %s for dailing at %+s and expiring in %+s", conf.User, dial, expire)) cons := make(chan *ssh.Client) errs := make(chan error) var con net.Conn var sc ssh.Conn var chans <-chan ssh.NewChannel var req <-chan *ssh.Request var err error flux.GoDefer("MakeDial", func() { con, err = net.DialTimeout("tcp", ip, dial) if err != nil { flux.Report(err, fmt.Sprintf("MakeDial:Before for %s net.DailTimeout", ip)) errs <- err return } sc, chans, req, err = ssh.NewClientConn(con, ip, conf) if err != nil { flux.Report(err, fmt.Sprintf("MakeDial:After for %s ssh.NewClientConn", ip)) errs <- err return } flux.Report(nil, fmt.Sprintf("MakeDial initiating NewClient for %s", ip)) cons <- ssh.NewClient(sc, chans, req) return }) expiration := threshold(expire) go func() { for _ = range retry { expiration = threshold(expire) } }() select { case err := <-errs: flux.Report(err, fmt.Sprintf("NewClient Ending!")) return nil, err case som := <-cons: flux.Report(nil, fmt.Sprintf("NewClient Created!")) expiration = nil return som, nil case <-expiration: flux.Report(nil, fmt.Sprintf("MakeDial Expired for %s!", ip)) defer con.Close() if sc != nil { sc.Close() } return nil, ErrTimeout } }
//StreamWithConn handles streaming two http conns func (h *TCP) StreamWithConn(src net.Conn, addr string, conf *tls.Config) (*ConnInsight, error) { rsrc := NewStreamConn(src) var rdest *StreamConn var err error if conf == nil { rdest, err = NewNetConn(addr) } else { rdest, err = NewTLSConn(addr, conf) } if err != nil { flux.Report(err, fmt.Sprintf("StreamWithConn Unable to Create Request for %s!", src.RemoteAddr())) return nil, err } return h.Stream(rsrc, rdest) }
//handleOperations manages the operations and behaviours of the connserver func (c *ConnServe) handleOperations() { var killsig int64 flux.GoDefer(fmt.Sprintf("%s:ConnectionCycleManager", c.ID), func() { flux.Report(nil, fmt.Sprintf("ConnServe:Listener Connection Cycle Management Started for %s", c.ID)) defer flux.Report(nil, fmt.Sprintf("ConnServe:Listener Closed for %s!", c.ID)) func() { log.Info("ConnServer checking HealthCheck Status: %t For: %s", c.checkable, c.ID) connloop: defer func() { defer flux.Report(nil, fmt.Sprintf("ConnServe:Listener Setting Kill Singal for %s!", c.ID)) atomic.StoreInt64(&killsig, 1) }() for { select { case <-c.director.HealthNotify(): log.Info("ConnServer checking HealthCheck Status: %t For: %s", c.checkable, c.ID) if c.checkable { age := c.director.MaxAge() idle := time.Duration(c.idle.Unix()) if idle > age { break connloop } } case <-c.closer: flux.Report(nil, fmt.Sprintf("ConnServe:Listener User Closing Operation for %s", c.ID)) break connloop case <-c.director.CloseNotify(): flux.Report(nil, fmt.Sprintf("ConnServe:Listener Director Closing Operation for %s", c.ID)) break connloop } } }() }) flux.GoDefer(fmt.Sprintf("%s:AcceptCycleHandler", c.ID), func() { flux.Report(nil, fmt.Sprintf("ConnServe:Listener Accept Cycle Started for %s", c.ID)) for { if killsig > 0 { break } defer flux.Report(nil, fmt.Sprintf("ConnServe:Listener Finished Handling Accept for %s", c.ID)) con, err := c.listener.Accept() flux.Report(err, fmt.Sprintf("ConnServe:Listener Operation Processing for %s", c.ID)) if err != nil { flux.GoDefer("ReportError", func() { errs, ok := c.director.Errors() if !ok { errs <- err } }) return } c.idle = time.Now() //send the process in to a goroutine,lets not block flux.GoDefer("Handling net.Conn from ConnServe", func() { c.director.Requests().Emit(con) c.director.Wait() err := c.target(con, c.director) flux.Report(err, fmt.Sprintf("Received On Request for %s by %s", con.RemoteAddr().String(), c.ID)) if err != nil { fmt.Fprint(con, EOFV) flux.Report(con.Close(), fmt.Sprintf("Closing ConnServe Request for %s by %s", con.RemoteAddr().String(), c.ID)) flux.GoDefer("ReportError", func() { errs, ok := c.director.Errors() if !ok { errs <- err } }) } }) } }) }
//AddHTTPSpecialBundles hands a default bundle to the supplied director func AddHTTPSpecialBundles(x ProxyDirector, stream HTTPStreamer, controls *xnet.Control, check bool, each xnet.EachControl, report *flux.StackReport) error { if stream == nil { return ErrBadBundle } if controls == nil { return ErrBadBundle } x.Register("chttp", func(req *ProxyRequest, d *Director, a Action) error { var err error var cert *tls.Config if req.CertFile != "" && req.KeyFile != "" { cert, err = LoadTLS(req.CertFile, req.KeyFile) if err != nil { return err } } ls, err := xnet.ListenTCP("tcp", req.From, cert, controls, each, report) if err != nil { return err } return x.ServeCustom(ls, func(con net.Conn, d Directors) error { xcon, ok := con.(*xnet.XConn) flux.Report(nil, fmt.Sprintf("Retrieving HTTP XConn from net.Conn for IP %s -> %t", con.RemoteAddr().String(), ok)) if !ok { return xnet.ErrBadXConn } ip, err := xcon.Container().IP() flux.Report(err, fmt.Sprintf("Retrieving Container IP from HTTP XConn for %s -> %s", con.RemoteAddr().String(), ip)) if err != nil { return err } req.Addr = ip addr := net.JoinHostPort(ip, fmt.Sprintf("%d", req.Port)) // addr := net.JoinHostPort(ip, string(req.Port)) cn, err := stream.StreamWithConn(xcon, addr, cert) flux.Report(err, fmt.Sprintf("Initiated HTTP tcp container proxy Scheme for %s -> %s", con.RemoteAddr().String(), addr)) if err != nil { return err } rip, _, _ := net.SplitHostPort(ip) cn.Meta["container_ip"] = ip cn.Meta["remote_ip"] = rip cn.Meta["local_ip"] = con.LocalAddr().String() cn.Meta["name"] = xcon.Container().Name() cn.Meta["port"] = fmt.Sprintf("%d", req.Port) a(cn) return nil }, check) }) return nil }
//AddSSHSpecialBundles hands a default bundle to the supplied director func AddSSHSpecialBundles(x ProxyDirector, sv SSHStreamer, serv *SSHStreamServer, controls *xnet.Control, check bool, each xnet.EachControl) error { if serv == nil { return ErrBadBundle } if sv == nil { return ErrBadBundle } if controls == nil { return ErrBadBundle } x.Register("cssh", func(req *ProxyRequest, d *Director, a Action) error { var err error var cert *tls.Config if req.CertFile != "" && req.KeyFile != "" { cert, err = LoadTLS(req.CertFile, req.KeyFile) if err != nil { return err } } rq, _ := serv.Reports() ls, err := xnet.ListenTCP("tcp", req.From, cert, controls, each, rq) if err != nil { return err } return x.ServeCustom(ls, func(con net.Conn, d Directors) error { xcon, ok := con.(*xnet.XConn) flux.Report(nil, fmt.Sprintf("Retrieving XConn from net.Conn for IP %s -> %t", con.RemoteAddr().String(), ok)) if !ok { return xnet.ErrBadXConn } ip, err := xcon.Container().IP() flux.Report(err, fmt.Sprintf("Retrieving Container IP from XConn for %s -> %s", con.RemoteAddr().String(), ip)) if err != nil { return err } req.Addr = ip addr := net.JoinHostPort(ip, fmt.Sprintf("%d", req.Port)) // addr := net.JoinHostPort(ip, string(req.Port)) sl, cl, err := serv.StreamConnection(xcon, addr, nil) flux.Report(err, fmt.Sprintf("Initiated ssh authentication Scheme for %s and %s", con.RemoteAddr().String(), addr)) if err != nil { return err } cn, err := sv.Stream(sl, cl) flux.Report(err, fmt.Sprintf("Initiated ssh proxy Scheme for %s -> %s", con.RemoteAddr().String(), addr)) if err != nil { return err } rip, _, _ := net.SplitHostPort(cl.Client.Meta.RemoteAddr().String()) cn.Meta["cip"] = ip cn.Meta["rip"] = rip cn.Meta["name"] = xcon.Container().Name() cn.Meta["port"] = fmt.Sprintf("%d", req.Port) cn.Meta["pass"] = string(cl.Client.Pass) cn.Meta["user"] = cl.Client.Meta.User() a(cn) cn.open.Emit(true) return nil }, check) }) return nil }
//AddTCPBundles hands a default bundle to the supplied director func AddTCPBundles(x ProxyDirector, stream TCPStreamer, check bool) { x.Register("tcp", func(req *ProxyRequest, d *Director, a Action) error { var err error var cert *tls.Config if req.CertFile != "" && req.KeyFile != "" { cert, err = LoadTLS(req.CertFile, req.KeyFile) if err != nil { return err } } to := net.JoinHostPort(req.Addr, fmt.Sprintf("%d", req.Port)) return TCPProvider(x, stream, a, req.From, to, cert, check) }) x.Register("xtcp", func(req *ProxyRequest, d *Director, a Action) error { var err error var cert *tls.Config if req.CertFile != "" && req.KeyFile != "" { cert, err = LoadTLS(req.CertFile, req.KeyFile) if err != nil { return err } } log.Info("XTcp Will set up custom Addr to %s", req.From) return x.ServeCustomTCP(req.From, cert, func(con net.Conn, dir Directors) error { var c *ConnInsight var err error ip, _, _ := net.SplitHostPort(con.RemoteAddr().String()) log.Info("XTCP using IP:%s to Port: %d", ip, req.Port) addr := net.JoinHostPort(ip, fmt.Sprintf("%d", req.Port)) log.Info("XTCP using new endpoint Addr: %d", addr) req.Addr = ip log.Info("XTCP Done setting IP Addr: %d", addr) // flux.GoDefer("StreamWithConn.XTcp", func() { log.Info("XTCP Begin streaming using tcp.StreamWithConn IP Addr: %d", addr) c, err = stream.StreamWithConn(con, addr, cert) log.Info("XTCP Done streaming using tcp.StreamWithConn IP Addr: %d", addr) if err != nil { flux.Report(err, fmt.Sprintf("XTcp XConn Closing Connection for %s", ip)) flux.Report(con.Close(), fmt.Sprintf("XTcp XConn Closing Connection for %s", ip)) return err } // }) a(c) return nil }, check) }) }
//ServeHTTPWith provides a different approach,instead of using the base net.Conn ,itself uses the http.Request and http.Response as means of proxying using the director func ServeHTTPWith(t TargetReqResOp, addr string, d Directors, conf *tls.Config, check bool) (*HTTPServe, error) { var hs *http.Server var ls net.Listener var err error handler := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { defer flux.Report(err, fmt.Sprintf("Request Processor Completed!")) flux.GoDefer("HttpServer", func() { d.Requests().Emit(true) flux.Report(err, fmt.Sprintf("Request Process Begin!")) // d.Wait() err := t(res, req, d) flux.Report(err, fmt.Sprintf("Request Processed Finished!")) if err != nil { res.WriteHeader(404) res.Write([]byte(err.Error())) flux.GoDefer("ReportError", func() { eos, ok := d.Errors() if ok { eos <- err } }) } }) }) if conf != nil { hs, ls, err = CreateTLS(addr, conf, handler) } else { hs, ls, err = CreateHTTP(addr, handler) } flux.Report(err, fmt.Sprintf("HttpServe Listener Processor!")) if err != nil { return nil, err } hps := &HTTPServe{ listener: ls, server: hs, director: d, idle: time.Now(), closer: make(Notifier), } go func() { defer ls.Close() flux.Report(nil, fmt.Sprintf("HttpServer HealthCheck Status %t", check)) nloop: for { select { case <-d.HealthNotify(): flux.Report(nil, fmt.Sprintf("HttpServer checking HealthCheck status %t", check)) if check { age := hps.director.MaxAge() idle := time.Duration(hps.idle.Unix()) if idle > age { break nloop } } case <-hps.closer: break nloop } } }() return hps, nil }