Skip to content

Commit

Permalink
Added support for aliases
Browse files Browse the repository at this point in the history
Improved logging
Added docker compose for local logs
  • Loading branch information
fredjeck committed Apr 11, 2024
1 parent e9e57e7 commit 5314d71
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 21 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@

# Go workspace file
go.work

#local elastic
assets/elastic/data/*
!assets/elastic/data/.gitkeep
35 changes: 19 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,33 @@ COPY . .

# build
RUN go build -o /go/src/github.com/fredjeck/jarl/bin/jarl ./cmd/jarl
# create a default directory for the configuration in order to copy it to the runtime image as mkdir is not available on distroless
RUN mkdir -p /var/run/jarl/configuration


# use a distroless base image with glibc
FROM gcr.io/distroless/base-debian12:nonroot
FROM alpine:3.19

LABEL org.opencontainers.image.source="https://github.com/fredjeck/jarl"

LABEL org.opencontainers.image.source="hhttps://github.com/fredjeck/jarl"
RUN apk update && \
apk add --no-cache shadow && \
groupadd muggles && \
useradd -ms /bin/sh -G muggles jarl

# copy our compiled binary
COPY --from=builder --chown=nonroot /go/src/github.com/fredjeck/jarl/bin/jarl /usr/local/bin/
COPY --from=builder --chown=nonroot /var/run/jarl/configuration /var/run/jarl/configuration
COPY --from=builder --chown=jarl /go/src/github.com/fredjeck/jarl/bin/jarl /usr/local/bin/

RUN mkdir -p /var/run/jarl/configuration
RUN chown jarl /var/run/jarl/configuration

# run as non-privileged user
USER nonroot
USER jarl

ARG PORT_GRPC=9000
ARG PORT_HTTP=8000
ARG AUTHZ_HEADER=x-forwarded-sub
ARG CONFIGURATION=/var/run/jarl/configuration
ENV CONFIGURATION=${CONFIGURATION}
ENV PORT_GRPC=9000
ENV PORT_HTTP=8000
ENV AUTHZ_HEADER=x-forwarded-sub

# command / entrypoint of container
ENTRYPOINT ["jarl"]
#,"-h","$PORT_HTTP","-g","$PORT_HTTP","-a","$AUTHZ_HEADER","-c",$CONFIGURATION]
ENTRYPOINT jarl -h ${PORT_HTTP} -g ${PORT_GRPC} -a ${AUTHZ_HEADER}

EXPOSE $PORT_GRPC
EXPOSE $PORT_HTTP
EXPOSE ${PORT_HTTP}
EXPOSE ${PORT_GRPC}
Empty file added assets/elastic/data/.gitkeep
Empty file.
23 changes: 23 additions & 0 deletions assets/fluent/fluent-bit.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[SERVICE]
log_level debug
Parsers_File parsers.conf

[INPUT]
Name forward
Listen 0.0.0.0
port 24224

[FILTER]
Name parser
Match *
Key_Name log
Preserve_Key False
Reserve_Data On
Parser cri-json

[OUTPUT]
Name es
Match *
Host elasticsearch
Port 9200
Suppress_Type_Name On
3 changes: 3 additions & 0 deletions assets/fluent/parsers.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[PARSER]
Name cri-json
Format json
17 changes: 17 additions & 0 deletions authz/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const (
// Authorization is the internal representation of a client configuration
type Authorization struct {
ClientID string
Aliases []string
Allow bool
Endpoints map[HTTPMethod][]*regexp.Regexp
}
Expand All @@ -73,6 +74,7 @@ type Authorization struct {
func NewAuthorization() *Authorization {
return &Authorization{
Endpoints: make(map[HTTPMethod][]*regexp.Regexp),
Aliases: make([]string, 0),
}
}

Expand All @@ -84,6 +86,14 @@ var (
)

// NewAuthorizationFromYaml Geneates a new authorization configration from the provided yaml content
//
// Expected yaml format
// cliendID
// mode: allow # or deny
// paths:
// - /single.*?/path # Single pat regex
// - path: /other path
// methods: GET, PUT, ALL # Http methods to look for
func NewAuthorizationFromYaml(contents []byte) (*Authorization, error) {
auth := NewAuthorization()

Expand All @@ -104,6 +114,13 @@ func NewAuthorizationFromYaml(contents []byte) (*Authorization, error) {
return nil, ErrInvalidMode
}

aliases, ok := yamlMap["aliases"]
if ok {
for _, a := range aliases.([]interface{}) {
auth.Aliases = append(auth.Aliases, a.(string))
}
}

mode := strings.ToLower(m.(string))
if len(mode) == 0 || (mode != modeAllow && mode != modeDeny) {
return nil, ErrInvalidMode
Expand Down
16 changes: 16 additions & 0 deletions authz/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,19 @@ paths:
assert.NoError(t, err)
assert.Len(t, auth.Endpoints, 1)
}

func TestAliases(t *testing.T) {
yml := `
clientID: client
mode: allow
aliases:
- client.dev
- client.int
paths:
- /api/encounter
`

auth, err := NewAuthorizationFromYaml([]byte(yml))
assert.NoError(t, err)
assert.Len(t, auth.Aliases, 2)
}
4 changes: 4 additions & 0 deletions authz/authorizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ func LoadAll(dir string) (*Authorizations, error) {
if err != nil {
slog.Error(fmt.Sprintf("unable to load '%s' see details for errors", path), slog.Any("error", err))
}
slog.Info(fmt.Sprintf("%s (aliases: %s) - loaded authorizations from '%s'", conf.ClientID, conf.Aliases, path))
authz.authorizations[conf.ClientID] = conf
for _, alias := range conf.Aliases {
authz.authorizations[alias] = conf
}
}

return nil
Expand Down
47 changes: 47 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
services:
elasticsearch:
image: elasticsearch:8.13.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- 9200:9200
volumes:
- ./assets/elastic/data/:/usr/share/elasticsearch/data
mem_limit: "1g"

fluentd:
image: fluent/fluent-bit
volumes:
- ./assets/fluent:/fluent-bit/etc/
ports:
- 24224:24224
- 24224:24224/udp
depends_on:
- elasticsearch

kibana:
image: kibana:8.13.0
ports:
- 5601:5601
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch

jarl:
container_name: jarl
build: .
ports:
- 8000:8000
- 9000:9000
environment:
- AUTHZ_HEADER=x-forwarded-sub
logging:
driver: fluentd
options:
tag: jarl
fluentd-async: 'true'
restart: always
depends_on:
- fluentd
4 changes: 2 additions & 2 deletions server/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ func (srv *GRPCAuthzServer) Start(wg *sync.WaitGroup) {

select {
case srv.ready <- true:
fmt.Println("Notified test cases")
slog.Info(fmt.Sprintf("advertised status to test case listeners"))
default:
fmt.Println("No test cases running")
slog.Info(fmt.Sprintf("no GRPC test cases listener found...skipping"))
}
srv.state = Serving

Expand Down
4 changes: 2 additions & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ func (srv *HTTPAuthzServer) Start(wg *sync.WaitGroup, healthFunc func() (bool, s
srv.httpServer = &http.Server{Handler: mux}
select {
case srv.ready <- true:
fmt.Println("Notified test cases")
slog.Info(fmt.Sprintf("advertised status to test case listeners"))
default:
fmt.Println("No test cases running")
slog.Info(fmt.Sprintf("no HTTP test cases listener found...skipping"))
}
srv.state = Serving
slog.Info(fmt.Sprintf("starting jarl http authz server at '%s", listener.Addr()))
Expand Down
2 changes: 2 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server

import (
"fmt"
"log/slog"
"sync"
)

Expand Down Expand Up @@ -56,6 +57,7 @@ func (s *JarlAuthzServer) Stop() {

// NewJarlAuthzServer instantiates a new Authz server based on the provided configuration
func NewJarlAuthzServer(conf *Configuration) *JarlAuthzServer {
slog.Info(fmt.Sprintf("configuring jarl using headers['%s'] as authz content attribute", conf.HTTPAuthZHeader))
return &JarlAuthzServer{
grpcServer: NewGRPCAuthzServer(conf),
httpServer: NewHTTPAuthzServer(conf),
Expand Down
2 changes: 1 addition & 1 deletion server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestExtAuthz(t *testing.T) {

// Prepare the gRPC request.
_ = <-server.grpcServer.ready
conn, err := grpc.NewClient(fmt.Sprintf("localhost:%d", 9000), grpc.WithTransportCredentials(insecure.NewCredentials()))
conn, err := grpc.NewClient(fmt.Sprintf("localhost:%d", server.grpcServer.port), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatalf(err.Error())
}
Expand Down

0 comments on commit 5314d71

Please sign in to comment.