-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
82 changed files
with
5,733 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,68 @@ | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
/bin | ||
/dist | ||
/target | ||
/.cr-release-packages | ||
/vendor | ||
/reverse-http | ||
/*.pem | ||
/*.b64 | ||
|
||
/*.tar | ||
/*.tgz | ||
|
||
# Intellij | ||
.idea/ | ||
out/ | ||
*.iml | ||
|
||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
# Test binary, build with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
# Compiled Object files, Static and Dynamic libs (Shared Objects) | ||
*.o | ||
|
||
# Folders | ||
_obj | ||
_test | ||
|
||
# Architecture specific extensions/prefixes | ||
*.[568vq] | ||
[568vq].out | ||
|
||
*.cgo1.go | ||
*.cgo2.c | ||
_cgo_defun.c | ||
_cgo_gotypes.go | ||
_cgo_export.* | ||
|
||
_testmain.go | ||
|
||
*.prof | ||
|
||
# coverage | ||
.coverprofile | ||
gover.coverprofile | ||
|
||
# Swap | ||
[._]*.s[a-v][a-z] | ||
[._]*.sw[a-p] | ||
[._]s[a-v][a-z] | ||
[._]sw[a-p] | ||
|
||
# Session | ||
Session.vim | ||
|
||
# Go workspace file | ||
go.work | ||
# Temporary | ||
.netrwhist | ||
*~ | ||
# Auto-generated tag files | ||
tags |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# options for analysis running | ||
run: | ||
# exit code when at least one issue was found, default is 1 | ||
issues-exit-code: 1 | ||
|
||
# which dirs to skip: they won't be analyzed; | ||
# can use regexp here: generated.*, regexp is applied on full path; | ||
# default value is empty list, but next dirs are always skipped independently | ||
# from this option's value: | ||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ | ||
skip-dirs: | ||
- vendor | ||
|
||
linters: | ||
enable: | ||
- errcheck | ||
- goconst | ||
- godot | ||
- gofmt | ||
- goimports | ||
- gosimple | ||
- govet | ||
- ineffassign | ||
- staticcheck | ||
- typecheck | ||
- unparam | ||
- unused | ||
- exportloopref | ||
|
||
issues: | ||
exclude-rules: | ||
- path: _test\.go | ||
linters: | ||
- unparam |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
FROM golang:1.21-alpine3.19 AS builder | ||
# hadolint ignore=DL3018 | ||
RUN apk add --no-cache alpine-sdk ca-certificates curl | ||
|
||
WORKDIR /app | ||
COPY go.* ./ | ||
RUN go mod download | ||
COPY . . | ||
RUN make vendor build | ||
|
||
FROM alpine:3.19 | ||
|
||
COPY --from=builder /app/reverse-http /reverse-http | ||
|
||
USER 65532:65532 | ||
ENTRYPOINT ["/reverse-http"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
.DEFAULT_GOAL := help | ||
|
||
.PHONY: clean build fmt test | ||
|
||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) | ||
|
||
BUILD_FLAGS ?= | ||
VERSION = "0.0.1" | ||
BRANCH = $(shell git rev-parse --abbrev-ref HEAD) | ||
REVISION = $(shell git describe --tags --always --dirty) | ||
BUILD_DATE = $(shell date +'%Y.%m.%d-%H:%M:%S') | ||
LDFLAGS ?= -w -s | ||
BINARY = reverse-http | ||
|
||
TEST_AGENT_ID = 4711 | ||
TEST_AUTH = ha-tls | ||
TEST_STORE_TYPE = none | ||
|
||
default: help | ||
|
||
.PHONY: help | ||
help: | ||
@grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' | ||
|
||
build: ## Build executable | ||
@CGO_ENABLED=0 GO111MODULE=on go build -mod=vendor -o $(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . | ||
|
||
test: ## Test | ||
@GO111MODULE=on go test -count=1 -mod=vendor -v ./... | ||
|
||
fmt: ## Go format | ||
go fmt ./... | ||
|
||
vet: ## Go vet | ||
go vet ./... | ||
|
||
clean: ## Clean | ||
@rm -rf $(BINARY) | ||
|
||
lint: ## Lint | ||
@golangci-lint run | ||
|
||
.PHONY: deps | ||
deps: ## Get dependencies | ||
GO111MODULE=on go get ./... | ||
|
||
.PHONY: vendor | ||
vendor: ## Go vendor | ||
GO111MODULE=on go mod vendor | ||
|
||
.PHONY: tidy | ||
tidy: ## Go tidy | ||
GO111MODULE=on go mod tidy | ||
|
||
##### Testing | ||
|
||
docker-compose.build: | ||
docker-compose -f $(ROOT_DIR)/docker-compose.${TEST_AUTH}.yml build | ||
|
||
docker-compose.up: | ||
docker-compose -f $(ROOT_DIR)/docker-compose.${TEST_AUTH}.yml up --remove-orphans | ||
|
||
docker-compose.down: | ||
docker-compose -f $(ROOT_DIR)/docker-compose.${TEST_AUTH}.yml down --remove-orphans | ||
|
||
docker-compose.run: docker-compose.build docker-compose.up | ||
|
||
start-proxy: build | ||
@export QUIC_GO_LOG_LEVEL_=debug && ${ROOT_DIR}/reverse-http proxy --store.type="${TEST_STORE_TYPE}" --agent-server.listen-address=":4242" \ | ||
--http-proxy.listen-address=":3128" --agent-server.tls.file.key=tests/cfssl/certs/proxy-key.pem --agent-server.tls.file.cert=tests/cfssl/certs/proxy.pem | ||
|
||
start-proxy-tls: build | ||
@export QUIC_GO_LOG_LEVEL_=debug && ${ROOT_DIR}/reverse-http proxy --store.type="${TEST_STORE_TYPE}" --agent-server.listen-address=":4242" \ | ||
--http-proxy.listen-address=":3128" --agent-server.tls.file.key=tests/cfssl/certs/proxy-key.pem --agent-server.tls.file.cert=tests/cfssl/certs/proxy.pem \ | ||
--http-proxy.tls.enable --http-proxy.tls.file.key=tests/cfssl/certs/proxy-key.pem --http-proxy.tls.file.cert=tests/cfssl/certs/proxy.pem | ||
|
||
start-proxy2: build | ||
@${ROOT_DIR}/reverse-http proxy --store.type="memcached" --agent-server.listen-address=":4243" --http-proxy.listen-address=":3127" \ | ||
--store.http-proxy-address="localhost:3127" --agent-server.tls.file.key=tests/cfssl/certs/proxy-key.pem --agent-server.tls.file.cert=tests/cfssl/certs/proxy.pem | ||
|
||
start-agent: build | ||
@export QUIC_GO_LOG_LEVEL_=debug && ${ROOT_DIR}/reverse-http agent --auth.noauth.agent-id="4711" --agent-client.server-address="localhost:4242" --agent-client.tls.file.root-ca=tests/cfssl/certs/ca.pem | ||
|
||
start-agent2: build | ||
@${ROOT_DIR}/reverse-http agent --auth.noauth.agent-id="4712" --agent-client.server-address="localhost:4243" --agent-client.tls.insecure-skip-verify | ||
|
||
start-lb: build | ||
@${ROOT_DIR}/reverse-http lb --http-proxy.listen-address=":3129" --store.type="${TEST_STORE_TYPE}" | ||
|
||
curl-proxy: | ||
curl -x "http://${TEST_AGENT_ID}:noauth@localhost:3128" https://httpbin.org/ip | ||
|
||
curl-proxy-tls: | ||
curl -x "https://${TEST_AGENT_ID}:noauth@localhost:3128" https://httpbin.org/ip --proxy-cacert tests/cfssl/certs/ca.pem | ||
|
||
curl-lb: | ||
curl -x "http://${TEST_AGENT_ID}:noauth@localhost:3129" https://httpbin.org/ip | ||
|
||
jwt-keys: build | ||
@${ROOT_DIR}/reverse-http auth key private --out=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
@${ROOT_DIR}/reverse-http auth key public --out=${ROOT_DIR}/tests/jwt/auth-key-public.pem --in=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
@${ROOT_DIR}/reverse-http auth jwt token --duration=87600h --agent-id="4711" --role "client" --out ${ROOT_DIR}/tests/jwt/auth-client-jwt-4711.b64 --in=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
@${ROOT_DIR}/reverse-http auth jwt token --duration=87600h --agent-id="4711" --role "agent" --out ${ROOT_DIR}/tests/jwt/auth-agent-jwt-4711.b64 --in=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
@${ROOT_DIR}/reverse-http auth jwt token --duration=87600h --agent-id="4712" --role "client" --out ${ROOT_DIR}/tests/jwt/auth-client-jwt-4712.b64 --in=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
@${ROOT_DIR}/reverse-http auth jwt token --duration=87600h --agent-id="4712" --role "agent" --out ${ROOT_DIR}/tests/jwt/auth-agent-jwt-4712.b64 --in=${ROOT_DIR}/tests/jwt/auth-key-private.pem | ||
|
||
start-proxy-jwt: build | ||
@${ROOT_DIR}/reverse-http proxy --auth.type="jwt" --auth.jwt.public-key=tests/jwt/auth-key-public.pem \ | ||
--agent-server.listen-address=":4242" --http-proxy.listen-address=":3128" --agent-server.tls.file.key=tests/cfssl/certs/proxy-key.pem --agent-server.tls.file.cert=tests/cfssl/certs/proxy.pem | ||
|
||
start-agent-jwt: build | ||
@$(eval JWT_TOKEN=$(shell cat tests/jwt/auth-agent-jwt-${TEST_AGENT_ID}.b64)) | ||
@${ROOT_DIR}/reverse-http agent --auth.type="jwt" --agent-client.server-address="localhost:4242" --agent-client.tls.file.root-ca=tests/cfssl/certs/ca.pem --auth.jwt.token="file:tests/jwt/auth-agent-jwt-${TEST_AGENT_ID}.b64" | ||
|
||
curl-proxy-jwt: | ||
@$(eval JWT_TOKEN=$(shell cat tests/jwt/auth-client-jwt-${TEST_AGENT_ID}.b64)) | ||
curl -x "http://${TEST_AGENT_ID}:${JWT_TOKEN}@localhost:3128" https://httpbin.org/ip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,107 @@ | ||
# reverse-http | ||
Reverse HTTP proxy over QUIC protocol | ||
|
||
Reverse HTTP proxy over QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000)). | ||
|
||
## Architecture | ||
|
||
### Standalone | ||
|
||
<p style="text-align: center;"><img src="docs/reverse-http-arch.svg" alt="Architecture"></p> | ||
|
||
* Agent connection process | ||
* An agent initiates a connection to the proxy server utilizing the `QUIC` protocol. | ||
* The connection between the agent and the proxy is persistent | ||
* Upon connection, the proxy server performs an agent authentication | ||
* The proxy keeps track of agents' connections | ||
* Each agent is uniquely identified by an `agentID` | ||
* Multiple agents can simultaneously connect to the proxy. | ||
* Only one connection per `agentID` is allowed. | ||
|
||
* Client connection process | ||
* Clients establish a connection with the HTTP proxy by issuing an `HTTP CONNECT` request. This standard method allows the client to specify the desired destination. | ||
* During the connection process, the proxy authenticates the connecting client using basic `Proxy-Authorization`, where the `username` is utilized to specify the `agentID` that the client wishes to connect to. | ||
* Once authenticated, the proxy server locates the corresponding agent's `QUIC` connection that is already being tracked. | ||
* Proxy opens a new `QUIC` stream to the agent and sends all subsequent data through it | ||
* The agent proceeds with the `CONNECT` procedure by establishing a new TCP connection to the requested destination. | ||
|
||
### HA setup | ||
|
||
<p style="text-align: center;"><img src="docs/reverse-http-ha.svg" alt="HA"></p> | ||
|
||
* Agent connection process | ||
* An agent initiates a connection to the UDP load balancer, which in turn establishes a connection with one of the proxy servers | ||
* Upon establishing a connection, the proxy server records an entry in `memcached` for an agentID along with its own HTTP proxy address. | ||
* Client connection process | ||
* Clients connect to the TCP load balancer, which then establishes a connection with one of the LB servers. | ||
* Upon connection, the LB server retrieves the HTTP proxy address and an agentID from Memcached. | ||
* The LB server then sends an `HTTP CONNECT` request to the proxy. | ||
|
||
## Build | ||
### build binary | ||
|
||
make clean build | ||
|
||
## Quick requirements | ||
|
||
https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes | ||
|
||
```bash | ||
sudo bash -c 'echo net.core.rmem_max=2500000 >> /etc/sysctl.conf' | ||
sudo bash -c 'echo net.core.wmem_max=2500000 >> /etc/sysctl.conf' | ||
sudo sysctl -p | ||
``` | ||
|
||
|
||
|
||
## Local test standalone | ||
|
||
### no auth | ||
|
||
```bash | ||
make start-proxy | ||
make start-agent | ||
curl -x "http://4711:noauth@localhost:3128" https://httpbin.org/ip | ||
``` | ||
|
||
### jwt auth | ||
|
||
```bash | ||
make start-proxy-jwt | ||
make start-agent-jwt | ||
make curl-proxy-jwt | ||
``` | ||
|
||
## Local test docker-compose | ||
|
||
```bash | ||
make TEST_AUTH=noauth docker-compose.run | ||
make TEST_AGENT_ID=4711 curl-proxy | ||
make TEST_AGENT_ID=4712 curl-proxy | ||
``` | ||
|
||
## Whitelisting patterns | ||
|
||
``` | ||
localhost | ||
localhost:80 | ||
localhost:1000-2000 | ||
*.zone | ||
*.zone:80 | ||
*.zone:1000-2000 | ||
127.0.0.1 | ||
127.0.0.1:80 | ||
127.0.0.1:1000-2000 | ||
10.0.0.1/8 | ||
10.0.0.1/8:80 | ||
10.0.0.1/8:1000-2000 | ||
1000::/16 | ||
1000::/16:80 | ||
1000::/16:1000-2000 | ||
[2001:db8::1]/64 | ||
[2001:db8::1]/64:80 | ||
[2001:db8::1]/64:1000-2000 | ||
2001:db8::1 | ||
[2001:db8::1] | ||
[2001:db8::1]:80 | ||
[2001:db8::1]:1000-2000 | ||
``` |
Oops, something went wrong.