github.com/status-im/status-go@v1.1.0/server/pairing/preflight/preflight.go (about)

     1  package preflight
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/ethereum/go-ethereum/log"
    15  
    16  	"github.com/status-im/status-go/server/pairing"
    17  	"github.com/status-im/status-go/timesource"
    18  
    19  	"go.uber.org/zap"
    20  
    21  	"github.com/status-im/status-go/logutils"
    22  	"github.com/status-im/status-go/protocol/common"
    23  	"github.com/status-im/status-go/server"
    24  )
    25  
    26  const (
    27  	outboundCheck = "/outbound_check"
    28  	headerPing    = "ping"
    29  	headerPong    = "pong"
    30  )
    31  
    32  func preflightHandler(w http.ResponseWriter, r *http.Request) {
    33  	ping := r.Header.Get(headerPing)
    34  	if ping == "" {
    35  		http.Error(w, "no value in 'ping' header", http.StatusBadRequest)
    36  	}
    37  
    38  	w.Header().Set(headerPong, ping)
    39  }
    40  
    41  func makeCert(address net.IP) (*tls.Certificate, []byte, error) {
    42  	now := timesource.GetCurrentTime()
    43  	log.Debug("makeCert", "system time", time.Now().String(), "timesource time", now.String())
    44  	notBefore := now.Add(-pairing.CertificateMaxClockDrift)
    45  	notAfter := now.Add(pairing.CertificateMaxClockDrift)
    46  	return server.GenerateTLSCert(notBefore, notAfter, []net.IP{address}, []string{})
    47  }
    48  
    49  func makeAndStartServer(cert *tls.Certificate, address net.IP) (string, func() error, error) {
    50  	wg := sync.WaitGroup{}
    51  	wg.Add(1)
    52  	waitForPortSet := func(int) {
    53  		wg.Done()
    54  	}
    55  
    56  	s := server.NewServer(
    57  		cert,
    58  		address.String(),
    59  		waitForPortSet,
    60  		logutils.ZapLogger().Named("Preflight Server"),
    61  	)
    62  
    63  	s.SetHandlers(server.HandlerPatternMap{outboundCheck: preflightHandler})
    64  	err := s.Start()
    65  	if err != nil {
    66  		return "", nil, err
    67  	}
    68  
    69  	wg.Wait()
    70  	return s.GetHostname() + ":" + strconv.Itoa(s.GetPort()), s.Stop, nil
    71  }
    72  
    73  func makeClient(certPem []byte) (*http.Client, error) {
    74  	rootCAs, err := x509.SystemCertPool()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	if ok := rootCAs.AppendCertsFromPEM(certPem); !ok {
    79  		return nil, fmt.Errorf("failed to append certPem to rootCAs")
    80  	}
    81  
    82  	tr := &http.Transport{
    83  		TLSClientConfig: &tls.Config{
    84  			MinVersion:         tls.VersionTLS12,
    85  			InsecureSkipVerify: false, // MUST BE FALSE
    86  			RootCAs:            rootCAs,
    87  			Time:               timesource.GetCurrentTime,
    88  		},
    89  	}
    90  
    91  	return &http.Client{Transport: tr}, nil
    92  }
    93  
    94  func makeOutboundCheck(c *http.Client, host string) error {
    95  	u := url.URL{
    96  		Scheme: "https",
    97  		Host:   host,
    98  		Path:   outboundCheck,
    99  	}
   100  
   101  	req, err := http.NewRequest("GET", u.String(), nil)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	ping, err := common.RandomAlphanumericString(64)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	req.Header.Set(headerPing, ping)
   112  
   113  	resp, err := c.Do(req)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	if resp.StatusCode != http.StatusOK {
   119  		return fmt.Errorf("response status not ok, received '%d' : '%s'", resp.StatusCode, resp.Status)
   120  	}
   121  
   122  	pong := resp.Header.Get(headerPong)
   123  	if pong != ping {
   124  		return fmt.Errorf("ping should match pong: ping '%s', pong '%s'", ping, pong)
   125  	}
   126  	return nil
   127  }
   128  
   129  func CheckOutbound() error {
   130  	// cert stuff
   131  	outboundIP, err := server.GetOutboundIP()
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	cert, certPem, err := makeCert(outboundIP)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// server stuff
   142  	host, stop, err := makeAndStartServer(cert, outboundIP)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	defer func() {
   147  		err := stop()
   148  		if err != nil {
   149  			logutils.ZapLogger().Error("error while stopping preflight serve", zap.Error(err))
   150  		}
   151  	}()
   152  
   153  	// Client stuff
   154  	c, err := makeClient(certPem)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	return makeOutboundCheck(c, host)
   160  }