Skip to content

Commit

Permalink
Make the framework available for public
Browse files Browse the repository at this point in the history
  • Loading branch information
pmateusz committed Dec 23, 2023
0 parents commit 0a3ffa8
Show file tree
Hide file tree
Showing 74 changed files with 7,377 additions and 0 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/build-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Build and publish a docker image
on: [ workflow_dispatch ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-image:
timeout-minutes: 5
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Export environment variables
run: |
echo BUILD_TIME=$(date --utc --iso-8601=seconds) >> $GITHUB_ENV
echo VCS_TAG=$(git describe --tags --abbrev 2>/dev/null || echo -n v0.0.0) >> $GITHUB_ENV
- name: Log in to the Container registry
uses: docker/login-action
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
file: ./build/Dockerfile
push: true
provenance: mode=max
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VCS_COMMIT=${{ GITHUB_SHA }}
VCS_BRANCH=${{ GITHUB_HEAD_REF }}
VCS_TAG=${{ env.VCS_TAG }}
BUILD_TIME=${{ env.BUILD_TIME }}
BUILD_ENVIRONMENT=ci
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
11 changes: 11 additions & 0 deletions .github/workflows/build-master-branch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Build the master branch
on:
push:
branches:
- master
concurrency:
group: build-master-branch
jobs:
check-branch:
uses: ./.github/workflows/check-branch.yaml
secrets: inherit
26 changes: 26 additions & 0 deletions .github/workflows/check-branch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Run linter, shellcheck, and unit tests on a branch
on: [ workflow_call, workflow_dispatch, pull_request ]
jobs:
lint-and-test:
timeout-minutes: 5
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ "1.21" ]
steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- name: Run linter
run: "go vet ./..."
- name: Run tests
run: "go test ./..."
- name: Run shellcheck
uses: dominikh/staticcheck-action@v1.3.0
with:
version: "2023.1.6"
install-go: false
cache-key: ${{ matrix.go-version }}
27 changes: 27 additions & 0 deletions .github/workflows/release-git-tag.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Release a git tag
on:
workflow_dispatch:
inputs:
git_tag:
description: 'the git tag to publish'
type: string
required: true
jobs:
check-branch:
uses: ./.github/workflows/check-branch.yaml
secrets: inherit
publish-tag:
needs: [ check-branch ]
timeout-minutes: 5
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Create the git tag
run: |
git config --global user.name "Glove CI"
git config --global user.email "glove-ci@users.noreply.github.com"
git tag --annotate "${{ github.event.inputs.git_tag }}" --message "Version ${{ github.event.inputs.git_tag }}"
git push origin "${{ github.event.inputs.git_tag }}"
20 changes: 20 additions & 0 deletions .github/workflows/upload-code-cover-task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Generate and upload a code coverage report
on: [workflow_dispatch]
jobs:
lint-and-test:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.21"
- run: "go test ./... -coverpkg=glove/internal/...,glove/pkg/... -coverprofile=coverage.out"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
files: ./coverage.out
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bin
.idea
glove.iml
coverage.out
26 changes: 26 additions & 0 deletions License.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Copyright (c) 2023 The Glove Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67 changes: 67 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2023 The Glove Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

ifndef VCS_COMMIT
VCS_COMMIT := $(shell git rev-parse HEAD)
endif

ifndef VCS_BRANCH
VCS_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
endif

ifndef VCS_TAG
VCS_TAG := $(shell git describe --tags --abbrev 2>/dev/null || echo -n "v0.0.0")
endif

ifndef BUILD_TIME
BUILD_TIME := $(shell date --utc --iso-8601=seconds)
endif

ifndef BUILD_ENVIRONMENT
BUILD_ENVIRONMENT := local
endif

LDFLAGS = "-X glove/internal/version.commitHash=${VCS_COMMIT} \
-X glove/internal/version.branch=${VCS_BRANCH} \
-X glove/internal/version.version=${VCS_TAG} \
-X glove/internal/version.buildTime=${BUILD_TIME} \
-X glove/internal/version.environment=${BUILD_ENVIRONMENT}"

.PHONY: build clean cli deps-install deps-update format image lint test test-cover update

cli: deps-install build

format:
gofmt -l -s -w .

lint:
go vet ./...
staticcheck ./...

test:
go test ./...

test-cover:
go test ./... -coverpkg=glove/internal/...,glove/pkg/... -coverprofile=coverage.out

deps-install:
go get ./...

deps-update:
go get -u ./...

build:
echo $(LDFLAGS)
go generate && go build -ldflags=$(LDFLAGS) -o ./bin/glove ./cmd/glove/main.go

clean:
rm ./bin/glove

image:
docker build \
--build-arg="VCS_COMMIT=${VCS_COMMIT}" \
--build-arg="VCS_BRANCH=${VCS_BRANCH}" \
--build-arg="VCS_TAG=${VCS_TAG}" \
--build-arg="BUILD_TIME=${BUILD_TIME}" \
--build-arg="BUILD_ENVIRONMENT=${BUILD_ENVIRONMENT}" \
-t glove -f ./build/Dockerfile .
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Glove: Build forward proxies in Golang

<p align="center">
<img src="./assets/gopher.png" width="128" height="128" align="center">
</p>

<p align="center">
<img src="https://codecov.io/gh/pmateusz/glove/graph/badge.svg?token=VKTGCERVTC" alt="Codecov" />
<img src="https://img.shields.io/github/license/pmateusz/glove" alt="License: BSD-3-Clause" />
</p>

Glove is a framework for developing HTTP/HTTPS forward proxies in Golang that makes it easy to implement a custom logic
for processing HTTP/HTTPS traffic that is passing through the proxy.

## Why you may actually want to build a forward proxy?

The ability to introspect and alter HTTP/HTTPS traffic passing by a forward proxy could be used to address many
technical problems that arise during integration with external APIs:

- Logging and auditing of requests and responses,
- Enforcing access control measures to restrict communication with the origin server, i.e., blocking access to certain
hosts or a subset of endpoints,
- Signing requests on behalf of the client, secret rotation, and secret access policy management are simplified because
API keys don't need to be disclosed to the client,
- Queuing requests from multiple clients to mitigate the risk of throttling, and to prevent escalation of throttling
events,
- Providing consistent retry policy in response to timeouts and throttling events.

There are no universal solutions for the scenarios mentioned above. For that reason, instead of attempting to provide
opinionated solutions, Glove focuses on helping to plug in arbitrary code for processing HTTP/HTTPS requests and
responses implemented in Golang.

## Features

Glove supports processing HTTP and HTTPS traffic. The introspection of HTTPS traffic is conditional on the successful
TLS handshake between the clients and the proxy. Subsequently, the proxy acts as the man-in-the-middle (**MITM**)
passing a request received from the client to the origin server. The server might not be aware the request received from
the proxy was sent on behalf of some other client. Once the origin server replies, the proxy is responsible for passing
the response back to the client.

Besides MITM, Glove supports **TCP tunneling** (a.k.a. transparent proxy). In contrast to MITM, which requires the
client to accept a TLS handshake with the proxy, in TCP tunneling the handshake is established between the client and
the origin server. The proxy responsibility is restricted to accepting the TCP connection requested by the client,
establishing a TCP
connection with the origin server, and copying a stream of bytes between both connections. Traffic passing through the
proxy is encrypted. Consequently, introspection of HTTPS communication is not available, and custom handlers are not
executed. TCP tunneling doesn't require any additional configuration of the client and the proxy. The primary motivation
for setting up a proxy for TCP tunneling is to prevent the client's IP address/location from being discovered. As the
origin server
receives traffic for the IP address of the proxy, it is not aware of the client's whereabouts.

Glove supports **websockets** in the MITM mode. Specifically, Glove responds to an HTTP/HTTPS request to upgrade the
protocol to a websocket by switching the mode of handling the connection from MITM to TCP tunneling.

Finally, besides core forward proxy abilities, Glove provides some auxiliary features:

- Ability to **customize TLS configuration** used by the proxy based on the client host or the origin server,
- **IP and CIDR whitelisting** of hosts allowed to connect to the proxy.

## Mission Statement

As authors, our mission was to develop a useful framework that will help build high-quality solutions for technical
problems arising in a broad range of applications in a time-efficient manner. The project is open source and licensed
using a permissive BSD-type license, which makes it effectively free for commercial applications.

## Sponsorship

Future project maintenance relies on financial support. Please consider sponsorship or a donation if the framework
benefits your project or the company. We thank private and corporate sponsors for their generous financial support.

## License

This project is licensed under the terms of the [3-Clause BSD](./License.md) License.

## References

- [**Developer Guide**](./docs/developer-guide.md)
Binary file added assets/gopher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2023 The Glove Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21

FROM golang:${GO_VERSION} as build-stage
ARG VCS_COMMIT
ARG VCS_BRANCH
ARG VCS_TAG
ARG BUILD_TIME
ARG BUILD_ENVIRONMENT
WORKDIR /build
COPY cmd /build/cmd
COPY internal /build/internal
COPY pkg /build/pkg
COPY go.mod /build
RUN go mod tidy
RUN go generate && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-X glove/internal/version.commitHash=${VCS_COMMIT} \
-X glove/internal/version.branch=${VCS_BRANCH} \
-X glove/internal/version.version=${VCS_TAG} \
-X glove/internal/version.buildTime=${BUILD_TIME} \
-X glove/internal/version.environment=${BUILD_ENVIRONMENT}" \
-o glove ./cmd/glove/main.go

FROM gcr.io/distroless/base-debian11 AS release-stage
WORKDIR /
COPY --from=build-stage /build/glove /glove
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/glove"]
14 changes: 14 additions & 0 deletions cmd/glove/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2023 The Glove Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
*/

package main

import (
"glove/pkg/cmd"
)

func main() {
cmd.Execute()
}
Loading

0 comments on commit 0a3ffa8

Please sign in to comment.