From 1ce5cf30c9609c443f7d6a894a60b53486aa6b04 Mon Sep 17 00:00:00 2001 From: Jop Zitman Date: Fri, 16 May 2025 16:36:34 +0800 Subject: [PATCH] Update documentation --- .github/workflows/release.yaml | 50 +++++++++++++----- Dockerfile | 17 ++++-- README.md | 81 +++++++++++++++++++---------- docs/file_transfer_times_dark.png | Bin 0 -> 13399 bytes docs/file_transfer_times_light.png | Bin 0 -> 14810 bytes 5 files changed, 102 insertions(+), 46 deletions(-) create mode 100644 docs/file_transfer_times_dark.png create mode 100644 docs/file_transfer_times_light.png diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 939710a..228a20d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -84,27 +84,51 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v3 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push Docker image - id: push + - name: Extract metadata (tags, labels) for Docker (server) + id: meta-server + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-server + + - name: Extract metadata (tags, labels) for Docker (client) + id: meta-client + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-client + + - name: Build and push Docker image (server) + id: push-server uses: docker/build-push-action@v6 with: context: . push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta-server.outputs.tags }} + labels: ${{ steps.meta-server.outputs.labels }} + target: server - - name: Generate artifact attestation + - name: Build and push Docker image (client) + id: push-client + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta-client.outputs.tags }} + labels: ${{ steps.meta-client.outputs.labels }} + target: client + + - name: Generate artifact attestation (server) uses: actions/attest-build-provenance@v2 with: - subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - subject-digest: ${{ steps.push.outputs.digest }} + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-server + subject-digest: ${{ steps.push-server.outputs.digest }} + push-to-registry: true + + - name: Generate artifact attestation (client) + uses: actions/attest-build-provenance@v2 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-client + subject-digest: ${{ steps.push-client.outputs.digest }} push-to-registry: true diff --git a/Dockerfile b/Dockerfile index d770c5f..9020628 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/usr/src/app/cmake-build-release \ cp cmake-build-release/slipstream-client . && \ cp cmake-build-release/slipstream-server . -FROM gcr.io/distroless/base-debian12 +FROM gcr.io/distroless/base-debian12 AS runtime WORKDIR /usr/src/app @@ -38,9 +38,16 @@ COPY ./certs/ ./certs/ ENV PATH=/usr/src/app/:$PATH -COPY --from=builder --chmod=755 /usr/src/app/slipstream-client ./client -COPY --from=builder --chmod=755 /usr/src/app/slipstream-server ./server +LABEL org.opencontainers.image.source=https://github.com/EndPositive/slipstream -LABEL org.opencontainers.image.source https://github.com/EndPositive/slipstream +FROM runtime AS client -ENTRYPOINT [] +COPY --from=builder --chmod=755 /usr/src/app/slipstream-client . + +ENTRYPOINT ["/usr/src/app/slipstream-client"] + +FROM runtime AS server + +COPY --from=builder --chmod=755 /usr/src/app/slipstream-server . + +ENTRYPOINT ["/usr/src/app/slipstream-server"] diff --git a/README.md b/README.md index 66a019d..906942d 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,61 @@ A high-performance covert channel over DNS, powered by QUIC multipath. +

+ + + + Shows a bar chart with benchmark results. + +

+ +

+ Exfiltrating a 10 MB file over a single DNS resolver. +

+ ## Highlights * Adaptive congestion control for rate-limited resolvers * Parallel routing over multiple multiple rate-limited resolvers * 60% lower header overhead than DNSTT +## Installation + +Get the latest binaries [GitHub releases](https://github.com/EndPositive/slipstream/releases/latest) or pull the latest version from the [GitHub Container Registry](https://github.com/users/EndPositive/packages?repo_name=slipstream). + +## Usage + +``` +Usage: slipstream-server [OPTION...] +slipstream-server - A high-performance covert channel over DNS (server) + + -a, --target-address=ADDRESS Target server address (default: + 127.0.0.1:5201) + -c, --cert=CERT Certificate file path (default: certs/cert.pem) + -d, --domain=DOMAIN Domain name this server is authoritative for + (Required) + -k, --key=KEY Private key file path (default: certs/key.pem) + -l, --dns-listen-port=PORT DNS listen port (default: 53) +``` +``` +Usage: slipstream-client [OPTION...] +slipstream-client - A high-performance covert channel over DNS (client) + + -c, --congestion-control=ALGO Congestion control algorithm (bbr, dcubic) + (default: dcubic) + -d, --domain=DOMAIN Domain name used for the covert channel (Required) + + -g, --gso[=BOOL] GSO enabled (true/false) (default: false). Use + --gso or --gso=true to enable. + -l, --tcp-listen-port=PORT Listen port (default: 5201) + -r, --resolver=RESOLVER Slipstream server resolver address (e.g., 1.1.1.1 + or 8.8.8.8:53). Can be specified multiple times. + (Required) +``` + ## Quickstart -Download a release binary from GitHub. -The binary contains both the client and the server. +### Server setup The server listens for DNS messages and attempts to decode QUIC message from them. Any new QUIC streams opened will be forwarded to a specified TCP service. @@ -25,17 +70,18 @@ $ slipstream-server \ --dns-listen-port=8853 \ --cert=certs/cert.pem \ --key=certs/key.pem \ - --target=127.0.0.1:5201 \ + --target-address=127.0.0.1:5201 \ --domain=test.com ``` +### Client setup + The client listens on a TCP port for incoming connections. It opens a QUIC connection through the resolver specified. For every TCP connection it accepts, a new QUIC stream will be opened. In this example, we connect to the slipstream server running on port 8853. ```shell -$ echo "127.0.0.1 8853" > resolvers.txt $ slipstream-client \ --tcp-listen-port=7000 \ --resolver=127.0.0.1:8853 \ @@ -48,6 +94,8 @@ Connection completed, almost ready. Connection confirmed. ``` +### Usage + You can then connect to the slipstream client on port 7000 as if you were connecting to the nc client on port 5201. ```shell @@ -88,7 +136,7 @@ Then run the slipstream server on port 53 (requires elevated privileges) and ins # Benchmarks -Comparison of slipstream and other existing DNS tunneling tools can be found in the [EndPositive/dns-tunneling-benchmark]([https://github.com/EndPositive/dns-tunneling-benchmark]) repository. +Comparison of slipstream and other existing DNS tunneling tools can be found in the [EndPositive/dns-tunneling-benchmark](https://github.com/EndPositive/dns-tunneling-benchmark) repository. Main findings: @@ -96,29 +144,6 @@ Main findings: * 23/19 Mbps upload/download speed for direction connections * automatically maximizes query rate according to resolver rate-limit -# Building from source - -```shell -# build deps on debian: cmake, pkg-config, libssl-dev, ninja-build, clang -$ git clone --recurse-submodules https://github.com/EndPositive/slipstream.git -$ cd slipstream/ -$ cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_MAKE_PROGRAM=ninja \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -G Ninja \ - -S . \ - -B ./build -$ cmake \ - --build ./build \ - --target slipstream \ - -j 18 -# mark as executable and install to your system -$ chmod +x ./build/slipstream -$ mv ./build/slipstream ~/.local/bin -``` - # Acknowledgements David Fifield's DNSTT and Turbo Tunnel concept has been a massive source of inspiration. diff --git a/docs/file_transfer_times_dark.png b/docs/file_transfer_times_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b0a456a5002a6e1eb45d4b5a92e56185cdfd69bf GIT binary patch literal 13399 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 I$^HI+0U2DuVgLXD literal 0 HcmV?d00001 diff --git a/docs/file_transfer_times_light.png b/docs/file_transfer_times_light.png new file mode 100644 index 0000000000000000000000000000000000000000..d784e70b6392d8483dc701831c31862573447daf GIT binary patch literal 14810 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 literal 0 HcmV?d00001