From 71965a0f3b52b4e26a52128a0c98947c2276f15b Mon Sep 17 00:00:00 2001 From: iphydf Date: Wed, 2 Feb 2022 13:03:01 +0000 Subject: [PATCH] feat: Add WASM build for toxcore and websocket bootstrap node. The websocket bootstrap node will be running on Heroku. --- .github/settings.yml | 5 +- .github/workflows/docker.yml | 51 +++++++- .hadolint.yaml | 4 + heroku.yml | 4 + other/bootstrap_daemon/docker/update-sha256 | 2 +- other/bootstrap_daemon/websocket/Dockerfile | 29 +++++ .../bootstrap_daemon/websocket/entrypoint.sh | 6 + other/bootstrap_daemon/websocket/keys | 1 + .../websocket/websockify/go.mod | 7 ++ .../websocket/websockify/websockify.go | 111 ++++++++++++++++++ other/emscripten/Dockerfile | 84 +++++++++++++ 11 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 heroku.yml create mode 100644 other/bootstrap_daemon/websocket/Dockerfile create mode 100755 other/bootstrap_daemon/websocket/entrypoint.sh create mode 100644 other/bootstrap_daemon/websocket/keys create mode 100644 other/bootstrap_daemon/websocket/websockify/go.mod create mode 100644 other/bootstrap_daemon/websocket/websockify/websockify.go create mode 100644 other/emscripten/Dockerfile diff --git a/.github/settings.yml b/.github/settings.yml index d0b8090d11..03dbc9ce54 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -18,9 +18,9 @@ branches: - "bazel-opt" - "bazel-tsan" - "bazel-valgrind" - - "build-bootstrapd-docker" - "build-compcert" - "build-macos" + - "build-msan" - "build-nacl" - "build-tcc" - "build-win32" @@ -38,6 +38,9 @@ branches: - "cimple" - "code-review/reviewable" - "continuous-integration/appveyor/pr" + - "docker-bootstrap-node" + - "docker-bootstrap-node-websocket" + - "docker-toxcore-js" - "mypy" - "sonar-scan" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2792a56083..f3acf80885 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -7,13 +7,11 @@ on: branches: [master] jobs: - build-bootstrapd-docker: + docker-bootstrap-node: runs-on: ubuntu-latest steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - name: Login to DockerHub - if: github.event_name == 'push' + if: ${{ github.event_name == 'push' }} uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} @@ -22,8 +20,49 @@ jobs: - name: Docker Build run: .github/scripts/tox-bootstrapd-docker local - name: Push latest image to DockerHub - if: github.event_name == 'push' + if: ${{ github.event_name == 'push' }} run: docker push toxchat/bootstrap-node:latest - name: Push versioned image to DockerHub - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + if: ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags/') }} run: docker push toxchat/bootstrap-node:"$(other/print-version)" + + docker-toxcore-js: + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + if: ${{ github.event_name == 'push' }} + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + file: other/emscripten/Dockerfile + push: ${{ github.event_name == 'push' }} + tags: toxchat/toxcore-js:latest + cache-from: type=registry,ref=toxchat/toxcore-js:latest + cache-to: type=inline + + docker-bootstrap-node-websocket: + runs-on: ubuntu-latest + needs: [docker-bootstrap-node] + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + if: ${{ github.event_name == 'push' }} + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: "{{defaultContext}}:other/bootstrap_daemon/websocket" + push: ${{ github.event_name == 'push' }} + tags: toxchat/bootstrap-node:latest-websocket + cache-from: type=registry,ref=toxchat/bootstrap-node:latest-websocket + cache-to: type=inline diff --git a/.hadolint.yaml b/.hadolint.yaml index 980f970e70..159a07f4a9 100644 --- a/.hadolint.yaml +++ b/.hadolint.yaml @@ -1,5 +1,9 @@ --- ignored: + # "cd" is sometimes useful when you want to run one command in one directory + # and then another command in another directory, but they should be executed + # in the same RUN instruction. + - DL3003 - DL3007 - DL3008 - DL3013 diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 0000000000..96fee69635 --- /dev/null +++ b/heroku.yml @@ -0,0 +1,4 @@ +--- +build: + docker: + web: other/bootstrap_daemon/websocket/Dockerfile diff --git a/other/bootstrap_daemon/docker/update-sha256 b/other/bootstrap_daemon/docker/update-sha256 index 8ec1d4b44e..6a547c39f9 100755 --- a/other/bootstrap_daemon/docker/update-sha256 +++ b/other/bootstrap_daemon/docker/update-sha256 @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -eux diff --git a/other/bootstrap_daemon/websocket/Dockerfile b/other/bootstrap_daemon/websocket/Dockerfile new file mode 100644 index 0000000000..5e48c7e16f --- /dev/null +++ b/other/bootstrap_daemon/websocket/Dockerfile @@ -0,0 +1,29 @@ +# Stage 1 - Compile websockify. +FROM toxchat/bootstrap-node:latest AS tox +FROM golang:1.17-alpine AS websockify + +COPY websockify /work/websockify +RUN cd /work/websockify && go mod download github.com/gorilla/websocket && go install + +# Stage 2 - Create the run-time image with bootstrap daemon and websockify. +FROM alpine:latest + +RUN addgroup -S tox && adduser -SDH -G tox tox + +COPY --from=websockify /go/bin/websockify /usr/local/bin/ +COPY --from=tox /usr/local /usr/local/ +COPY --from=tox /etc/tox-bootstrapd.conf /etc/ +COPY entrypoint.sh / + +RUN mkdir -p /var/lib/tox-bootstrapd/ /var/run/tox-bootstrapd/ \ + && chown tox:tox /var/lib/tox-bootstrapd/ /var/run/tox-bootstrapd/ +# Public Key: 122837CCDD474DD1183A83152164D51427044B3EAAA52ED683F6BA602568673B +COPY keys /var/lib/tox-bootstrapd/ +USER tox + +# Use this to generate a keys file: +#RUN /usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf --log-backend stdout \ +# && sleep 1 + +WORKDIR /web +CMD ["/entrypoint.sh"] diff --git a/other/bootstrap_daemon/websocket/entrypoint.sh b/other/bootstrap_daemon/websocket/entrypoint.sh new file mode 100755 index 0000000000..7284bdb3be --- /dev/null +++ b/other/bootstrap_daemon/websocket/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -eux + +/usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf --log-backend stdout +/usr/local/bin/websockify -l "0.0.0.0:$PORT" -t 127.0.0.1:33445 diff --git a/other/bootstrap_daemon/websocket/keys b/other/bootstrap_daemon/websocket/keys new file mode 100644 index 0000000000..c28c4d38de --- /dev/null +++ b/other/bootstrap_daemon/websocket/keys @@ -0,0 +1 @@ +(7ÌÝGMÑ:ƒ!dÕ'K>ª¥.Öƒöº`%hg;„GÄÙYª:ÊZy5˜£Sà$Í|CÚ ŒÇ¸–2Ì  t \ No newline at end of file diff --git a/other/bootstrap_daemon/websocket/websockify/go.mod b/other/bootstrap_daemon/websocket/websockify/go.mod new file mode 100644 index 0000000000..782137e8d1 --- /dev/null +++ b/other/bootstrap_daemon/websocket/websockify/go.mod @@ -0,0 +1,7 @@ +module github.com/TokTok/c-toxcore/other/bootstrap_daemon/websocket/websockify + +go 1.17 + +require ( + github.com/gorilla/websocket master +) diff --git a/other/bootstrap_daemon/websocket/websockify/websockify.go b/other/bootstrap_daemon/websocket/websockify/websockify.go new file mode 100644 index 0000000000..0fcad22b0c --- /dev/null +++ b/other/bootstrap_daemon/websocket/websockify/websockify.go @@ -0,0 +1,111 @@ +// A Go version WebSocket to TCP socket proxy +// +// This is a heavily modified version of this file: +// https://github.com/novnc/websockify-other/blob/master/golang/websockify.go +// +// Changes include: +// - Fix infinite loop on error. +// - Proper logging. +// - Proper error handling in general. +// - Support both websocket and regular GET requests on /. +// +// Copyright 2022 The TokTok team. +// Copyright 2021 Michael.liu. +// See LICENSE for licensing conditions. + +package main + +import ( + "encoding/hex" + "flag" + "log" + "net" + "net/http" + + "github.com/gorilla/websocket" +) + +var ( + sourceAddr = flag.String("l", "127.0.0.1:8080", "http service address") + targetAddr = flag.String("t", "127.0.0.1:5900", "tcp service address") +) + +var upgrader = websocket.Upgrader{ + // Should be enough to fit any Tox TCP packets. + ReadBufferSize: 2048, + WriteBufferSize: 2048, + Subprotocols: []string{"binary"}, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +func forwardTCP(wsconn *websocket.Conn, conn net.Conn) { + var tcpbuffer [2048]byte + defer wsconn.Close() + defer conn.Close() + for { + n, err := conn.Read(tcpbuffer[0:]) + if err != nil { + log.Println("TCP READ :", err) + break + } + log.Println("TCP READ :", n, hex.EncodeToString(tcpbuffer[0:n])) + + if err := wsconn.WriteMessage(websocket.BinaryMessage, tcpbuffer[0:n]); err != nil { + log.Println("WS WRITE :", err) + break + } + log.Println("WS WRITE :", n) + } +} + +func forwardWeb(wsconn *websocket.Conn, conn net.Conn) { + defer wsconn.Close() + defer conn.Close() + for { + _, buffer, err := wsconn.ReadMessage() + if err != nil { + log.Println("WS READ :", err) + break + } + log.Println("WS READ :", len(buffer), hex.EncodeToString(buffer)) + + m, err := conn.Write(buffer) + if err != nil { + log.Println("TCP WRITE:", err) + break + } + log.Println("TCP WRITE:", m) + } +} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade:", err) + return + } + vnc, err := net.Dial("tcp", *targetAddr) + if err != nil { + log.Println("dial:", err) + return + } + go forwardTCP(ws, vnc) + go forwardWeb(ws, vnc) + +} + +func main() { + flag.Parse() + log.Println("Starting up websockify endpoint") + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Upgrade") == "websocket" { + serveWs(w, r) + } else { + http.ServeFile(w, r, r.URL.Path[1:]) + } + }) + log.Fatal(http.ListenAndServe(*sourceAddr, nil)) +} diff --git a/other/emscripten/Dockerfile b/other/emscripten/Dockerfile new file mode 100644 index 0000000000..d7e435d6f3 --- /dev/null +++ b/other/emscripten/Dockerfile @@ -0,0 +1,84 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND="noninteractive" + +# Install dependencies. +RUN apt-get update && apt-get install --no-install-recommends -y \ + autoconf \ + automake \ + ca-certificates \ + cmake \ + curl \ + git \ + libtool \ + make \ + ninja-build \ + pkg-config \ + python3 \ + unzip \ + wget \ + xz-utils \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /work/emsdk +RUN git clone --depth=1 https://github.com/emscripten-core/emsdk /work/emsdk \ + && ./emsdk install latest \ + && ./emsdk activate latest + +# Build libsodium. +RUN . "/work/emsdk/emsdk_env.sh" \ + && git clone --depth=1 --branch=1.0.18 https://github.com/jedisct1/libsodium /work/libsodium \ + && cd /work/libsodium \ + && autoreconf -fi \ + && emconfigure ./configure --disable-shared \ + --without-pthreads \ + --disable-ssp --disable-asm --disable-pie \ + && emmake make install -j8 + +# Build an unused libsodium binding first so emcc caches all the system +# libraries. This makes rebuilds of toxcore below much faster. +RUN . "/work/emsdk/emsdk_env.sh" \ + && mkdir -p /work/wasm \ + && emcc -O3 -flto \ + --closure=1 \ + -s ALLOW_UNIMPLEMENTED_SYSCALLS=1 \ + -s EXPORT_NAME=libtoxcore \ + -s IGNORE_MISSING_MAIN=1 \ + -s MAIN_MODULE=1 \ + -s MALLOC=emmalloc \ + -s MODULARIZE=1 \ + -s STRICT=1 \ + -s WEBSOCKET_URL=wss:// \ + /usr/local/lib/libsodium.a \ + -o /work/wasm/libsodium.js + +# Build c-toxcore. +COPY . /work/c-toxcore +RUN . "/work/emsdk/emsdk_env.sh" \ + && cd /work/c-toxcore \ + && emcmake cmake -B_build -H. -GNinja \ + -DCMAKE_C_FLAGS="-O3 -flto -fPIC" \ + -DBUILD_TOXAV=OFF \ + -DENABLE_SHARED=OFF \ + -DBOOTSTRAP_DAEMON=OFF \ + -DCMAKE_INSTALL_PREFIX:PATH="/usr/local" \ + -DMIN_LOGGER_LEVEL=DEBUG \ + && emmake cmake --build _build --parallel 8 --target install + +# Build wasm bindings. +RUN . "/work/emsdk/emsdk_env.sh" \ + && mkdir -p /work/wasm \ + && emcc -O3 -flto \ + --closure=1 \ + -s ALLOW_UNIMPLEMENTED_SYSCALLS=1 \ + -s EXPORT_NAME=libtoxcore \ + -s IGNORE_MISSING_MAIN=1 \ + -s MAIN_MODULE=1 \ + -s MALLOC=emmalloc \ + -s MODULARIZE=1 \ + -s STRICT=1 \ + -s WEBSOCKET_URL=wss:// \ + /usr/local/lib/libsodium.a \ + /usr/local/lib/libtoxcore.a \ + -o /work/wasm/libtoxcore.js