Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
pbochynski committed Mar 20, 2024
1 parent f329889 commit 22be6d1
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 0 deletions.
84 changes: 84 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Test and release

on:
push:
branches: [ "main" ]
tags: [ '*.*.*' ]
pull_request:
branches: [ "main" ]

env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}

jobs:

build:
runs-on: ubuntu-latest
permissions: write-all

steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.22
cache: true

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=semver,pattern={{version}},event=tag
- name: Build Docker image
id: build-and-push
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
load: ${{ github.event_name == 'pull_request' }}
cache-from: type=gha
cache-to: type=gha,mode=max


- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
cluster-ip-operator.yaml
config/samples/cluster-ip-nodes.yaml
config/samples/cluster-ip-zones.yaml
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Build the manager binary
FROM golang:1.22 as builder
ARG TARGETOS
ARG TARGETARCH

WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
# COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download

# Copy the go source
COPY main.go main.go

# Build
# the GOARCH has not a default value to allow the binary be built according to the host where the command
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o ip-auth main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/ip-auth .
USER 65532:65532

ENTRYPOINT ["/ip-auth"]
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/kyma-project/ip-auth

go 1.22.0
128 changes: 128 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
)

const (
checkHeader = "x-ext-authz"
allowedValue = "allow"
resultHeader = "x-ext-authz-check-result"
receivedHeader = "x-ext-authz-check-received"
overrideHeader = "x-ext-authz-additional-header-override"
resultAllowed = "allowed"
resultDenied = "denied"
)

var (
httpPort = flag.String("http", "8000", "HTTP server port")
denyBody = fmt.Sprintf("denied by ext_authz for not found header `%s: %s` in the request", checkHeader, allowedValue)
)

// ExtAuthzServer implements the ext_authz v2/v3 gRPC and HTTP check request API.
type ExtAuthzServer struct {
httpServer *http.Server
// For test only
httpPort chan int
}

// ServeHTTP implements the HTTP check request.
func (s *ExtAuthzServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
body, err := io.ReadAll(request.Body)
if err != nil {
log.Printf("[HTTP] read body failed: %v", err)
}
l := fmt.Sprintf("%s %s%s, headers: %v, body: [%s]\n", request.Method, request.Host, request.URL, request.Header, returnIfNotTooLong(string(body)))
if allowedValue == request.Header.Get(checkHeader) {
log.Printf("[HTTP][allowed]: %s", l)
response.Header().Set(resultHeader, resultAllowed)
response.Header().Set(overrideHeader, request.Header.Get(overrideHeader))
response.Header().Set(receivedHeader, l)
response.WriteHeader(http.StatusOK)
} else {
log.Printf("[HTTP][denied]: %s", l)
response.Header().Set(resultHeader, resultDenied)
response.Header().Set(overrideHeader, request.Header.Get(overrideHeader))
response.Header().Set(receivedHeader, l)
response.WriteHeader(http.StatusForbidden)
_, _ = response.Write([]byte(denyBody))
}
}

func (s *ExtAuthzServer) startHTTP(address string, wg *sync.WaitGroup) {
defer func() {
wg.Done()
log.Printf("Stopped HTTP server")
}()

listener, err := net.Listen("tcp", address)
if err != nil {
log.Fatalf("Failed to create HTTP server: %v", err)
}
// Store the port for test only.
s.httpPort <- listener.Addr().(*net.TCPAddr).Port
s.httpServer = &http.Server{Handler: s}

log.Printf("Starting HTTP server at %s", listener.Addr())
if err := s.httpServer.Serve(listener); err != nil {
log.Fatalf("Failed to start HTTP server: %v", err)
}
}

func (s *ExtAuthzServer) run(httpAddr string) {
var wg sync.WaitGroup
wg.Add(2)
go s.startHTTP(httpAddr, &wg)
wg.Wait()
}

func (s *ExtAuthzServer) stop() {
log.Printf("HTTP server stopped: %v", s.httpServer.Close())
}

func NewExtAuthzServer() *ExtAuthzServer {
return &ExtAuthzServer{
httpPort: make(chan int, 1),
}
}

// type GBaaSClient struct {
// }

// func (c *GBaaSClient) check(ip string) bool {
// if ip == "127.0.0.1" {
// return true
// } else {
// return false
// }
// }

func main() {
flag.Parse()
s := NewExtAuthzServer()
go s.run(fmt.Sprintf(":%s", *httpPort))
defer s.stop()

// Wait for the process to be shutdown.
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
}

func returnIfNotTooLong(body string) string {
// Maximum size of a header accepted by Envoy is 60KiB, so when the request body is bigger than 60KB,
// we don't return it in a response header to avoid rejecting it by Envoy and returning 431 to the client
if len(body) > 60000 {
return "<too-long>"
}
return body
}
53 changes: 53 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"fmt"
"net/http"
"testing"
)

func TestExtAuthz(t *testing.T) {
server := NewExtAuthzServer()
// Start the test server on random port.
go server.run("localhost:0")

// Prepare the HTTP request.
httpClient := &http.Client{}
httpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/check", <-server.httpPort), nil)
if err != nil {
t.Fatalf(err.Error())
}

cases := []struct {
name string
header string
want int
}{
{
name: "HTTP-allow",
header: "allow",
want: http.StatusOK,
},
{
name: "HTTP-deny",
header: "deny",
want: http.StatusForbidden,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var got int
httpReq.Header.Set(checkHeader, tc.header)
resp, err := httpClient.Do(httpReq)
if err != nil {
t.Errorf(err.Error())
} else {
got = resp.StatusCode
resp.Body.Close()
}
if got != tc.want {
t.Errorf("want %d but got %d", tc.want, got)
}
})
}
}

0 comments on commit 22be6d1

Please sign in to comment.