-
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
9 changed files
with
1,626 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.build-image | ||
.dummy-auth-image | ||
.http-logger-image | ||
.image | ||
bin | ||
*~ |
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,18 @@ | ||
(auth) { | ||
extauth { | ||
endpoint http://auth:8000 | ||
copy-request-header Authorization | ||
copy-response-header X-Token | ||
set-header X-Original-Uri {uri} | ||
set-header X-Original-Method {method} | ||
} | ||
} | ||
|
||
http://localhost:2015 { | ||
route { | ||
import auth | ||
reverse_proxy logger:8001 | ||
} | ||
} | ||
|
||
|
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,84 @@ | ||
IMAGE=container.trusch.io/caddy-extauth/caddy:latest | ||
DUMMY_AUTH_IMAGE=container.trusch.io/caddy-extauth/dummy-auth:latest | ||
HTTP_LOGGER_IMAGE=container.trusch.io/caddy-extauth/http-logger:latest | ||
BASE_IMAGE=gcr.io/distroless/base-debian10:latest | ||
BUILD_IMAGE=container.trusch.io/caddy-extauth/builder | ||
BUILD_BASE_IMAGE=golang:1.15 | ||
|
||
all: image dummy-auth-image http-logger-image | ||
|
||
bin/caddy: extauth.go .build-image | ||
mkdir -p bin | ||
podman run \ | ||
--rm \ | ||
-v ./:/app \ | ||
-w /app \ | ||
-e GOOS=${GOOS} \ | ||
-e GOARCH=${GOARCH} \ | ||
-e GOARM=${GOARM} \ | ||
-v go-build-cache:/root/.cache/go-build \ | ||
-v go-mod-cache:/go/pkg/mod $(BUILD_IMAGE) bash -c \ | ||
"xcaddy build master --with github.com/trusch/caddy-extauth/pkg/extauth=/app && mv caddy bin/caddy" | ||
|
||
bin/http-logger: cmd/http-logger/main.go | ||
mkdir -p bin | ||
podman run \ | ||
--rm \ | ||
-v ./:/app \ | ||
-w /app \ | ||
-e GOOS=${GOOS} \ | ||
-e GOARCH=${GOARCH} \ | ||
-e GOARM=${GOARM} \ | ||
-v go-build-cache:/root/.cache/go-build \ | ||
-v go-mod-cache:/go/pkg/mod $(BUILD_IMAGE) \ | ||
go build -o $@ ./cmd/http-logger | ||
|
||
bin/dummy-auth: cmd/dummy-auth/main.go | ||
mkdir -p bin | ||
podman run \ | ||
--rm \ | ||
-v ./:/app \ | ||
-w /app \ | ||
-e GOOS=${GOOS} \ | ||
-e GOARCH=${GOARCH} \ | ||
-e GOARM=${GOARM} \ | ||
-v go-build-cache:/root/.cache/go-build \ | ||
-v go-mod-cache:/go/pkg/mod $(BUILD_IMAGE) \ | ||
go build -o $@ ./cmd/dummy-auth | ||
|
||
build-image: .build-image | ||
.build-image: | ||
$(eval ID=$(shell buildah from $(BUILD_BASE_IMAGE))) | ||
buildah run $(ID) go get -u github.com/caddyserver/xcaddy/cmd/xcaddy | ||
buildah commit $(ID) $(BUILD_IMAGE) | ||
buildah rm $(ID) | ||
touch .build-image | ||
|
||
image: .image | ||
.image: bin/caddy | ||
$(eval ID=$(shell buildah from $(BASE_IMAGE))) | ||
buildah copy $(ID) bin/caddy /bin/ | ||
buildah commit $(ID) $(IMAGE) | ||
buildah rm $(ID) | ||
touch .image | ||
|
||
dummy-auth-image: .dummy-auth-image | ||
.dummy-auth-image: bin/dummy-auth | ||
$(eval ID=$(shell buildah from $(BASE_IMAGE))) | ||
buildah copy $(ID) bin/dummy-auth /bin/ | ||
buildah config --cmd dummy-auth $(ID) | ||
buildah commit $(ID) $(DUMMY_AUTH_IMAGE) | ||
buildah rm $(ID) | ||
touch .dummy-auth-image | ||
|
||
http-logger-image: .http-logger-image | ||
.http-logger-image: bin/http-logger | ||
$(eval ID=$(shell buildah from $(BASE_IMAGE))) | ||
buildah copy $(ID) bin/http-logger /bin/ | ||
buildah config --cmd http-logger $(ID) | ||
buildah commit $(ID) $(HTTP_LOGGER_IMAGE) | ||
buildah rm $(ID) | ||
touch .http-logger-image | ||
|
||
clean: | ||
-rm -r .build-image .image .http-logger-image .dummy-auth-image bin |
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,22 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
func main() { | ||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
val := r.Header.Get("Authorization") | ||
if val == "secret" { | ||
log.Info().Str("ip", r.RemoteAddr).Msg("success") | ||
w.Header().Add("X-Token", "token") | ||
w.WriteHeader(http.StatusOK) | ||
return | ||
} | ||
log.Info().Str("ip", r.RemoteAddr).Msg("fail") | ||
w.WriteHeader(http.StatusUnauthorized) | ||
}) | ||
http.ListenAndServe(":8000", nil) | ||
} |
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 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"os" | ||
) | ||
|
||
func main() { | ||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
fmt.Println("---") | ||
r.Write(os.Stdout) | ||
w.Write([]byte("You are authenticated!")) | ||
}) | ||
http.ListenAndServe(":8001", nil) | ||
} |
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,27 @@ | ||
--- | ||
version: "3.8" | ||
configs: | ||
caddyfile: | ||
file: ./Caddyfile | ||
services: | ||
caddy: | ||
image: container.trusch.io/caddy-extauth/caddy:latest | ||
command: | ||
- caddy | ||
- run | ||
- -config=/Caddyfile | ||
configs: | ||
- source: caddyfile | ||
target: /Caddyfile | ||
ports: | ||
- "0.0.0.0:2015:2015" | ||
links: | ||
- auth | ||
- logger | ||
|
||
auth: | ||
image: container.trusch.io/caddy-extauth/dummy-auth:latest | ||
|
||
logger: | ||
image: container.trusch.io/caddy-extauth/http-logger:latest | ||
|
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,158 @@ | ||
package extauth | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/caddyserver/caddy/v2" | ||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" | ||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" | ||
"github.com/caddyserver/caddy/v2/modules/caddyhttp" | ||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
func init() { | ||
caddy.RegisterModule(Middleware{}) | ||
httpcaddyfile.RegisterHandlerDirective("extauth", parseCaddyfile) | ||
} | ||
|
||
// Middleware implements an HTTP handler that calls an external service for authentication | ||
type Middleware struct { | ||
Endpoint string // http endpoint endpoint for the auth request | ||
Timeout time.Duration // timeout for the auth request | ||
CopyRequestHeaders []string // headers to copy from the incoming request to the auth request | ||
CopyResponseHeaders []string // headers to copy from the auth response to the incoming request | ||
SetHeaders map[string]string // headers to set in the auth request | ||
httpClient *http.Client | ||
} | ||
|
||
// CaddyModule returns the Caddy module information. | ||
func (Middleware) CaddyModule() caddy.ModuleInfo { | ||
return caddy.ModuleInfo{ | ||
ID: "http.handlers.extauth", | ||
New: func() caddy.Module { return new(Middleware) }, | ||
} | ||
} | ||
|
||
// Provision implements caddy.Provisioner. | ||
func (m *Middleware) Provision(ctx caddy.Context) error { | ||
m.httpClient = &http.Client{ | ||
Timeout: m.Timeout, | ||
} | ||
return nil | ||
} | ||
|
||
// Validate implements caddy.Validator. | ||
func (m *Middleware) Validate() error { | ||
if m.Endpoint == "" { | ||
return errors.New("'endpoint' is required") | ||
} | ||
return nil | ||
} | ||
|
||
// ServeHTTP implements caddyhttp.MiddlewareHandler. | ||
func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { | ||
// create auth request | ||
authReq, err := http.NewRequest(http.MethodGet, m.Endpoint, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// copy headers from the incoming request | ||
for _, name := range m.CopyRequestHeaders { | ||
authReq.Header.Set(name, r.Header.Get(name)) | ||
} | ||
|
||
// set additional headers | ||
for key, val := range m.SetHeaders { | ||
if val == "{http.request.uri}" { | ||
val = r.URL.RequestURI() | ||
} | ||
if val == "{http.request.method}" { | ||
val = r.Method | ||
} | ||
authReq.Header.Set(key, val) | ||
} | ||
|
||
// perform the request | ||
resp, err := m.httpClient.Do(authReq) | ||
if err != nil || resp.StatusCode != http.StatusOK { | ||
// something went wrong or the server responded with something != 200 -> reject request with 401 | ||
log.Error().Str("url", r.URL.RequestURI()).Msg("failed to authenticate") | ||
w.WriteHeader(http.StatusUnauthorized) | ||
return nil | ||
} | ||
log.Info().Str("url", r.URL.RequestURI()).Msg("successfully authenticated") | ||
|
||
// copy the user defined response headers to the incoming request before going on to the next handler in the chain | ||
for _, name := range m.CopyResponseHeaders { | ||
val := resp.Header.Get(name) | ||
if val != "" { | ||
r.Header.Set(name, resp.Header.Get(name)) | ||
} | ||
} | ||
|
||
// call next handler | ||
return next.ServeHTTP(w, r) | ||
} | ||
|
||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. | ||
func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) (err error) { | ||
m.SetHeaders = make(map[string]string) | ||
d.NextArg() | ||
for d.NextBlock(0) { | ||
switch d.Val() { | ||
case "endpoint": | ||
if !d.AllArgs(&m.Endpoint) { | ||
return d.ArgErr() | ||
} | ||
case "timeout": | ||
timeoutStr := "" | ||
if !d.AllArgs(&timeoutStr) { | ||
return d.ArgErr() | ||
} | ||
m.Timeout, err = time.ParseDuration(timeoutStr) | ||
if err != nil { | ||
return fmt.Errorf("can't parse timeout: %w", err) | ||
} | ||
case "copy-request-header": | ||
name := "" | ||
for d.Args(&name) { | ||
m.CopyRequestHeaders = append(m.CopyRequestHeaders, name) | ||
} | ||
case "copy-response-header": | ||
name := "" | ||
for d.Args(&name) { | ||
m.CopyResponseHeaders = append(m.CopyResponseHeaders, name) | ||
} | ||
case "set-header": | ||
var key, val string | ||
for d.AllArgs(&key, &val) { | ||
m.SetHeaders[key] = val | ||
} | ||
default: | ||
return d.Errf("%s not a valid extauth option", d.Val()) | ||
} | ||
} | ||
if m.Timeout == 0 { | ||
m.Timeout = 1 * time.Second | ||
} | ||
return nil | ||
} | ||
|
||
// parseCaddyfile unmarshals tokens from h into a new Middleware. | ||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { | ||
var m Middleware | ||
err := m.UnmarshalCaddyfile(h.Dispenser) | ||
return m, err | ||
} | ||
|
||
// Interface guards | ||
var ( | ||
_ caddy.Provisioner = (*Middleware)(nil) | ||
_ caddy.Validator = (*Middleware)(nil) | ||
_ caddyhttp.MiddlewareHandler = (*Middleware)(nil) | ||
_ caddyfile.Unmarshaler = (*Middleware)(nil) | ||
) |
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,37 @@ | ||
module github.com/trusch/caddy-extauth | ||
|
||
go 1.15 | ||
|
||
require ( | ||
cloud.google.com/go v0.54.0 // indirect | ||
github.com/antlr/antlr4 v0.0.0-20201010232522-9e64dfc6e99f // indirect | ||
github.com/caddyserver/caddy/v2 v2.2.1 | ||
github.com/dgraph-io/badger v1.6.2 // indirect | ||
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect | ||
github.com/golang/protobuf v1.4.3 // indirect | ||
github.com/golang/snappy v0.0.2 // indirect | ||
github.com/google/cel-go v0.6.0 // indirect | ||
github.com/huandu/xstrings v1.3.2 // indirect | ||
github.com/imdario/mergo v0.3.11 // indirect | ||
github.com/klauspost/cpuid v1.3.1 // indirect | ||
github.com/lucas-clemente/quic-go v0.18.1 // indirect | ||
github.com/manifoldco/promptui v0.8.0 // indirect | ||
github.com/marten-seemann/qtls-go1-15 v0.1.1 // indirect | ||
github.com/mattn/go-colorable v0.1.8 // indirect | ||
github.com/miekg/dns v1.1.33 // indirect | ||
github.com/mitchellh/reflectwalk v1.0.1 // indirect | ||
github.com/prometheus/common v0.14.0 // indirect | ||
github.com/prometheus/procfs v0.2.0 // indirect | ||
github.com/rs/zerolog v1.20.0 | ||
github.com/smallstep/nosql v0.3.2 // indirect | ||
github.com/urfave/cli v1.22.4 // indirect | ||
go.step.sm/crypto v0.6.1 // indirect | ||
go.uber.org/multierr v1.6.0 // indirect | ||
go.uber.org/zap v1.16.0 // indirect | ||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee // indirect | ||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb // indirect | ||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 // indirect | ||
google.golang.org/genproto v0.0.0-20201014134559-03b6142f0dc9 // indirect | ||
google.golang.org/grpc v1.33.0 // indirect | ||
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 // indirect | ||
) |
Oops, something went wrong.