diff options
| -rw-r--r-- | arbiter/Dockerfile | 6 | ||||
| -rw-r--r-- | arbiter/arbiter.go | 165 | ||||
| -rwxr-xr-x | arbiter/tests.sh | 3 | ||||
| -rw-r--r-- | docker-compose-test.yaml | 13 | ||||
| -rw-r--r-- | docker-compose.yaml | 13 | ||||
| -rw-r--r-- | hived/Dockerfile | 6 | ||||
| -rw-r--r-- | hived/hived.go | 10 | ||||
| -rw-r--r-- | telebot/Dockerfile | 6 | 
8 files changed, 187 insertions, 35 deletions
| diff --git a/arbiter/Dockerfile b/arbiter/Dockerfile index fe3fb31..849b345 100644 --- a/arbiter/Dockerfile +++ b/arbiter/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder  ENV GOPROXY=https://goproxy.io  RUN apk update && apk upgrade  RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /arbiter && go mod download  COPY *.go /arbiter/  RUN cd /arbiter && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 as certbuilder  RUN apk add openssl  WORKDIR /certs  RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert  # FROM gcr.io/distroless/static-debian10 -FROM alpine:3.13 +FROM alpine:3.16  COPY --from=certbuilder /certs /certs  COPY --from=builder /arbiter/arbiter /arbiter/  ENTRYPOINT ["/arbiter/arbiter"] diff --git a/arbiter/arbiter.go b/arbiter/arbiter.go index 2806842..9cb3ad0 100644 --- a/arbiter/arbiter.go +++ b/arbiter/arbiter.go @@ -3,12 +3,16 @@ package main  import (  	"context"  	"crypto/tls" +	"encoding/json"  	"errors"  	"flag"  	"fmt" +	"io/ioutil"  	"net/http" +	"net/url"  	"os"  	"os/signal" +	"sync"  	"time"  	"github.com/go-redis/redis/v8" @@ -28,8 +32,26 @@ var (  const (  	SERVER_DEPLOYMENT_TYPE = "SERVER_DEPLOYMENT_TYPE" +	coingeckoAPIURLv3      = "https://api.coingecko.com/api/v3"  ) +type HttpHandlerFunc func(http.ResponseWriter, *http.Request) + +type HttpHandler struct { +	name     string +	function HttpHandlerFunc +} + +type priceChanStruct struct { +	name  string +	price float64 +} + +type errorChanStruct struct { +	hasError bool +	err      error +} +  // OWASP: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html  func addSecureHeaders(w *http.ResponseWriter) {  	(*w).Header().Set("Cache-Control", "no-store") @@ -40,49 +62,152 @@ func addSecureHeaders(w *http.ResponseWriter) {  	(*w).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")  } +//binance +func getPriceFromBinance(name, unit string, +	wg *sync.WaitGroup, +	priceChan chan<- priceChanStruct, +	errChan chan<- errorChanStruct) { + +} + +//kucoin +func getPriceFromKu(name, uni string, +	wg *sync.WaitGroup, +	priceChan chan<- priceChanStruct, +	errChan chan<- errorChanStruct) { + +} + +func getPriceFromCoinGecko( +	name, unit string, +	wg *sync.WaitGroup, +	priceChan chan<- priceChanStruct, +	errChan chan<- errorChanStruct) { +	defer wg.Done() + +	params := "/simple/price?ids=" + url.QueryEscape(name) + "&" + +		"vs_currencies=" + url.QueryEscape(unit) +	path := coingeckoAPIURLv3 + params +	fmt.Println(path) +	resp, err := http.Get(path) +	if err != nil { +		priceChan <- priceChanStruct{name: name, price: 0.} +		errChan <- errorChanStruct{hasError: true, err: err} +		log.Error().Err(err) +	} +	defer resp.Body.Close() + +	body, err := ioutil.ReadAll(resp.Body) +	if err != nil { +		priceChan <- priceChanStruct{name: name, price: 0.} +		errChan <- errorChanStruct{hasError: true, err: err} +		log.Error().Err(err) +	} + +	jsonBody := make(map[string]interface{}) +	err = json.Unmarshal(body, &jsonBody) +	if err != nil { +		priceChan <- priceChanStruct{name: name, price: 0.} +		errChan <- errorChanStruct{hasError: true, err: err} +		log.Error().Err(err) +	} + +	price := jsonBody[name].(map[string]interface{})[unit].(float64) + +	log.Info().Msg(string(body)) + +	priceChan <- priceChanStruct{name: name, price: price} +	errChan <- errorChanStruct{hasError: false, err: nil} +} +  func arbHandler(w http.ResponseWriter, r *http.Request) {  	w.Header().Add("Content-Type", "application/json")  	if r.Method != "GET" {  		http.Error(w, "Method is not supported.", http.StatusNotFound)  	}  	addSecureHeaders(&w) -	//binance -	//kucoin + +	var name string +	var unit string +	params := r.URL.Query() +	for key, value := range params { +		switch key { +		case "name": +			name = value[0] +		case "unit": +			unit = value[0] +		default: +			log.Error().Err(errors.New("Got unexpected parameter.")) +		} +	} + +	priceChan := make(chan priceChanStruct, 1) +	errChan := make(chan errorChanStruct, 1) +	var wg sync.WaitGroup +	wg.Add(1) +	getPriceFromCoinGecko(name, unit, &wg, priceChan, errChan) +	wg.Wait() + +	select { +	case err := <-errChan: +		if err.hasError != false { +			log.Error().Err(err.err) +		} +	default: +		log.Error().Err(errors.New("We shouldnt be here")) +	} + +	var price priceChanStruct +	select { +	case priceCh := <-priceChan: +		price = priceCh +	default: +		log.Fatal().Err(errors.New("We shouldnt be here")) +	} + +	json.NewEncoder(w).Encode(map[string]interface{}{ +		"name":         price.name, +		"price":        price.price, +		"unit":         unit, +		"err":          "", +		"isSuccessful": true, +	})  } -func startServer(gracefulWait time.Duration) { +func setupLogging() { +	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix +} + +func startServer(gracefulWait time.Duration, +	handlers []HttpHandler, +	serverDeploymentType string, port string) {  	r := mux.NewRouter()  	cfg := &tls.Config{  		MinVersion: tls.VersionTLS13, -		// CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, -		// PreferServerCipherSuites: true, -		// CipherSuites: []uint16{ -		// 	tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, -		// 	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, -		// 	tls.TLS_RSA_WITH_AES_256_GCM_SHA384, -		// 	tls.TLS_RSA_WITH_AES_256_CBC_SHA, -		// },  	}  	srv := &http.Server{ -		Addr:         "0.0.0.0:" + *flagPort, +		Addr:         "0.0.0.0:" + port,  		WriteTimeout: time.Second * 15,  		ReadTimeout:  time.Second * 15,  		Handler:      r,  		TLSConfig:    cfg,  	} -	r.HandleFunc("/crypto/arb", arbHandler) + +	for i := 0; i < len(handlers); i++ { +		r.HandleFunc(handlers[i].name, handlers[i].function) +	}  	go func() {  		var certPath, keyPath string -		if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "deployment" { +		if os.Getenv(serverDeploymentType) == "deployment" {  			certPath = "/certs/fullchain1.pem"  			keyPath = "/certs/privkey1.pem" -		} else if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "test" { +		} else if os.Getenv(serverDeploymentType) == "test" {  			certPath = "/certs/server.cert"  			keyPath = "/certs/server.key"  		} else { -			log.Fatal().Err(errors.New(fmt.Sprintf("unknown deployment kind: %s", SERVER_DEPLOYMENT_TYPE))) +			log.Fatal().Err(errors.New(fmt.Sprintf("unknown deployment kind: %s", serverDeploymentType)))  		}  		if err := srv.ListenAndServeTLS(certPath, keyPath); err != nil {  			log.Fatal().Err(err) @@ -99,10 +224,6 @@ func startServer(gracefulWait time.Duration) {  	log.Info().Msg("gracefully shut down the server")  } -func setupLogging() { -	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix -} -  func main() {  	var gracefulWait time.Duration  	flag.DurationVar(&gracefulWait, "gracefulwait", time.Second*15, "the duration to wait during the graceful shutdown") @@ -115,5 +236,7 @@ func main() {  	defer rdb.Close()  	setupLogging() -	startServer(gracefulWait) +	var handlerFuncs = []HttpHandler{{name: "/arb", function: arbHandler}} + +	startServer(gracefulWait, handlerFuncs, SERVER_DEPLOYMENT_TYPE, *flagPort)  } diff --git a/arbiter/tests.sh b/arbiter/tests.sh new file mode 100755 index 0000000..1fe9792 --- /dev/null +++ b/arbiter/tests.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +curl -X GET "https://localhost:8009/arb?name=ethereum&unit=usd" diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml index afee17a..99d9a76 100644 --- a/docker-compose-test.yaml +++ b/docker-compose-test.yaml @@ -36,6 +36,19 @@ services:        - ALL      environment:        - SERVER_DEPLOYMENT_TYPE=test +  arbiter: +    image: arbiter +    build: +      context: ./arbiter +    networks: +      - mainnet +    ports: +      - "8009:8009" +    entrypoint: ["/arbiter/arbiter"] +    cap_drop: +      - ALL +    environment: +      - SERVER_DEPLOYMENT_TYPE=test    redis:      image: redis:6.2-alpine      networks: diff --git a/docker-compose.yaml b/docker-compose.yaml index 90ff90d..7ce5495 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -51,6 +51,19 @@ services:        - ALLOW_EMPTY_PASSWORD=yes      volumes:        - redis-data:/data/ +  arbiter: +    image: arbiter +    build: +      context: ./arbiter +    networks: +      - mainnet +    ports: +      - "8009:8009" +    entrypoint: ["/arbiter/abiter"] +    cap_drop: +      - ALL +    environment: +      - SERVER_DEPLOYMENT_TYPE=deployment  networks:    mainnet:      driver: bridge diff --git a/hived/Dockerfile b/hived/Dockerfile index 7eb1a8d..1eaf999 100644 --- a/hived/Dockerfile +++ b/hived/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder  ENV GOPROXY=https://goproxy.io  RUN apk update && apk upgrade  RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /hived && go mod download  COPY *.go /hived/  RUN cd /hived && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 as certbuilder  RUN apk add openssl  WORKDIR /certs  RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert  # FROM gcr.io/distroless/static-debian10 -FROM alpine:3.13 +FROM alpine:3.16  COPY --from=certbuilder /certs /certs  COPY --from=builder /hived/hived /hived/  COPY ./docker-entrypoint.sh /hived/ diff --git a/hived/hived.go b/hived/hived.go index 940c2ed..a205352 100644 --- a/hived/hived.go +++ b/hived/hived.go @@ -130,7 +130,7 @@ func getPriceFromCoinGecko(  	errChan <- errorChanStruct{hasError: false, err: nil}  } -func sendGetToCryptoCompare( +func getPriceFromCryptoCompare(  	name, unit string,  	wg *sync.WaitGroup,  	priceChan chan<- priceChanStruct, @@ -204,7 +204,7 @@ func priceHandler(w http.ResponseWriter, r *http.Request) {  	defer close(errChan)  	defer close(priceChan)  	wg.Add(1) -	go sendGetToCryptoCompare(name, unit, &wg, priceChan, errChan) +	go getPriceFromCryptoCompare(name, unit, &wg, priceChan, errChan)  	wg.Wait()  	select { @@ -271,8 +271,8 @@ func pairHandler(w http.ResponseWriter, r *http.Request) {  	defer close(errChan)  	wg.Add(2) -	go sendGetToCryptoCompare(one, "USD", &wg, priceChan, errChan) -	go sendGetToCryptoCompare(two, "USD", &wg, priceChan, errChan) +	go getPriceFromCryptoCompare(one, "USD", &wg, priceChan, errChan) +	go getPriceFromCryptoCompare(two, "USD", &wg, priceChan, errChan)  	wg.Wait()  	for i := 0; i < 2; i++ { @@ -362,7 +362,7 @@ func alertManager() {  			wg.Add(len(vars))  			for i := range vars { -				go sendGetToCryptoCompare(vars[i], "USD", &wg, priceChan, errChan) +				go getPriceFromCryptoCompare(vars[i], "USD", &wg, priceChan, errChan)  			}  			wg.Wait() diff --git a/telebot/Dockerfile b/telebot/Dockerfile index d358ed7..a3cebf9 100644 --- a/telebot/Dockerfile +++ b/telebot/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder  ENV GOPROXY=https://goproxy.io  RUN apk update && apk upgrade  RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /telebot && go mod download  COPY *.go /telebot/  RUN cd /telebot && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 as certbuilder  RUN apk add openssl  WORKDIR /certs  RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert  # FROM gcr.io/distroless/static-debian10 -FROM alpine:3.13 +FROM alpine:3.16  COPY --from=certbuilder /certs /certs  COPY --from=builder /telebot/telebot /telebot/  COPY ./docker-entrypoint.sh /telebot/ | 
