Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sticky-sessions and fips support #481

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
FROM debian
ARG GO_VER=1.22
FROM golang:${GO_VER} AS build

RUN apt update && apt install -y ca-certificates curl
RUN mkdir -p /go/src/github.com/contentsquare/chproxy
WORKDIR /go/src/github.com/contentsquare/chproxy
COPY . ./
ARG EXT_BUILD_TAG
ENV EXT_BUILD_TAG=${EXT_BUILD_TAG}
RUN make release-build
RUN ls -al /go/src/github.com/contentsquare/chproxy

COPY chproxy /
FROM alpine
RUN apk add --no-cache curl ca-certificates
COPY --from=build /go/src/github.com/contentsquare/chproxy/chproxy* /

EXPOSE 9090

ENTRYPOINT ["/chproxy"]
ENTRYPOINT [ "/chproxy" ]
CMD [ "--help" ]
44 changes: 44 additions & 0 deletions Dockerfile_boringcrypto
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ARG UBUNTU_IMAGE=ubuntu:20.04
FROM ${UBUNTU_IMAGE} AS build

ENV GOPATH=/gocode
ENV PATH=$PATH:$GOPATH/bin
ENV GOVERSION=1.22

RUN mkdir /scr
WORKDIR /src

# golang
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
apt-utils \
software-properties-common \
gcc \
libc-dev \
&& add-apt-repository -y ppa:longsleep/golang-backports \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
golang-$GOVERSION-go \
golang-golang-x-tools \
&& apt-get autoremove -y \
&& apt-get remove -y \
apt-utils \
software-properties-common

# Create symbolic link
RUN ln -s /usr/lib/go-$GOVERSION /gocode

# tools
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git \
make \
tzdata \
curl \
ca-certificates

FROM build

# Build chproxy
COPY . ./
ARG EXT_BUILD_TAG
ENV GOEXPERIMENT=boringcrypto
RUN make release-build
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ run: build

lint:
go vet $(pkgs)
golangci-lint run
go list ./... | grep -v /vendor/ | xargs -n1 golint

tidy:
go mod tidy
Expand All @@ -48,8 +48,8 @@ clean:
release-build:
@echo "Ver: $(BUILD_TAG), OPTS: $(BUILD_OPTS)"
GOOS=linux GOARCH=amd64 go build $(BUILD_OPTS)
rm chproxy-linux-amd64-*.tar.gz || true
tar czf chproxy-linux-amd64-$(BUILD_TAG).tar.gz chproxy
rm chproxy-linux-amd64-*.tar.gz

release: format lint test clean release-build
@echo "Ver: $(BUILD_TAG), OPTS: $(BUILD_OPTS)"
Expand All @@ -58,4 +58,9 @@ release: format lint test clean release-build
release-build-docker:
@echo "Ver: $(BUILD_TAG)"
@DOCKER_BUILDKIT=1 docker build --target build --build-arg EXT_BUILD_TAG=$(BUILD_TAG) --progress plain -t chproxy-build .
@docker run --rm --entrypoint "/bin/sh" -v $(CURDIR):/host chproxy-build -c "/bin/cp /go/src/github.com/contentsquare/chproxy/*.tar.gz /host"
@docker run --rm --entrypoint "/bin/sh" -v $(CURDIR):/host chproxy-build -c "/bin/cp chproxy-linux-*-*.tar.gz /host"

release-build-docker-fips:
@echo "Ver: $(BUILD_TAG)"
@DOCKER_BUILDKIT=1 docker build -f Dockerfile_boringcrypto --build-arg EXT_BUILD_TAG=$(BUILD_TAG)-fips --build-arg EXT_BUILD_OPTS="-tags fips" --progress plain -t chproxy-build .
@docker run --rm --entrypoint "/bin/sh" -v $(CURDIR):/host chproxy-build -c "/bin/cp /src/chproxy-*.tar.gz /host"
8 changes: 8 additions & 0 deletions docs/src/content/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Chproxy is an HTTP proxy and load balancer for [ClickHouse](https://ClickHouse.y
- Exposes various useful [metrics](/configuration/metrics) in [Prometheus text format](https://prometheus.io/docs/instrumenting/exposition_formats/).
- Configuration may be updated without restart - just send `SIGHUP` signal to `chproxy` process.
- Easy to manage and run - just pass config file path to a single `chproxy` binary.
- Facilitates session affinity through `session_id` mapping, guaranteeing requests from the same user session are routed to the same upstream server (It is useful if one application server performs an initial processing step and stores the results in a temporary table; other servers can efficiently access and utilize that data by reaching the same server where the data is.)
- Service can be built to use only cryptographic algorithms approved by the Federal Information Processing Standard (FIPS) 140-2, making it suitable for processing sensitive government data.
```bash
-- to build regular artifact
make release-build-docker
-- to build artifact with FIPS support relying on Borring Crypto Module
make release-build-docker-fips:
```
- Easy to [configure](https://github.com/contentsquare/chproxy/blob/master/config/examples/simple.yml):
```yml
server:
Expand Down
5 changes: 5 additions & 0 deletions fips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build fips

package main

import _ "crypto/tls/fipsonly"
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.5.0 h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ=
github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.20.0 h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8=
github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
Expand Down
20 changes: 17 additions & 3 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,21 +443,35 @@ func TestServe(t *testing.T) {
startHTTP,
},
{
"http POST request with session id",
"http POST request with session_id and session_timeout",
"testdata/http-session-id.yml",
func(t *testing.T) {
sessionName := "name"
sessionTimeout := 900
req, err := http.NewRequest("POST",
"http://127.0.0.1:9090/?query_id=45395792-a432-4b92-8cc9-536c14e1e1a9&extremes=0&session_id=default-session-id233",
"http://127.0.0.1:9090/?query_id=45395792-a432-4b92-8cc9-536c14e1e1a9&extremes=0&session_id="+sessionName+"&session_timeout="+strconv.Itoa(sessionTimeout),
bytes.NewBufferString("SELECT * FROM system.numbers LIMIT 10"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded;") // This makes it work

checkErr(t, err)
resp, err := http.DefaultClient.Do(req)
checkErr(t, err)

if resp.StatusCode != http.StatusOK || resp.StatusCode != http.StatusOK && resp.Header.Get("X-Clickhouse-Server-Session-Id") == "" {
if resp.StatusCode != http.StatusOK {
t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK)
}

// verify correctness of session_id
_sessionName := resp.Header.Get("X-Clickhouse-Server-Session-Id")
if _sessionName != sessionName {
t.Fatalf("unexpected value of X-Clickhouse-Server-Session-Id: %s; expected: %s", _sessionName, sessionName)
}

// verify correctness of session_id
_sessionTimeout, _ := strconv.Atoi(resp.Header.Get("X-Clickhouse-Server-Session-Timeout"))
if _sessionTimeout != sessionTimeout {
t.Fatalf("unexpected value of X-Clickhouse-Server-Session-Timeout: %d; expected: %d", _sessionTimeout, sessionTimeout)
}
resp.Body.Close()
},
startHTTP,
Expand Down
5 changes: 5 additions & 0 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ func (rp *reverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("X-ClickHouse-Server-Session-Id", s.sessionId)
}

// publish session_timeout if needed
if s.sessionId != "" {
rw.Header().Set("X-ClickHouse-Server-Session-Timeout", strconv.Itoa(s.sessionTimeout))
}

q, shouldReturnFromCache, err := shouldRespondFromCache(s, origParams, req)
if err != nil {
respondWith(srw, err, http.StatusBadRequest)
Expand Down
Loading