Skip to content

Commit

Permalink
tests: metrics and request log (#24)
Browse files Browse the repository at this point in the history
* refactor: use handlerfunc for Serverable

feature: allow exposing metrics.

* refactor: make nonDeterministicLimitedRandReadSeeker re-usable across packages

* test: add test coverage for metrics gathering
bugfix: fix metrics such that they can pass test

* refactor: make log fixture re-usable across packages

* feature: allow enabling metrics endpoint for CLI app

* refactor: undo change of refactor move where we changed logBufferToLines to be public as there is no need to have it as a public method

* tests: add coverage for request log

* lint: add errro checking to satisfy linter

---------

Co-authored-by: Peter Van Bouwel <[email protected]>
  • Loading branch information
pvbouwel and Peter Van Bouwel authored Feb 8, 2025
1 parent 21bfb75 commit 0a8ccb6
Show file tree
Hide file tree
Showing 26 changed files with 806 additions and 267 deletions.
53 changes: 0 additions & 53 deletions aws/service/s3/loghelpers_test.go

This file was deleted.

4 changes: 1 addition & 3 deletions aws/service/s3/policy-iam-action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,8 @@ func runListObjectsV2AndReturnError(t *testing.T, s server.Serverable) error {
func runListObjectsV2AndReturnErrorAlternateEndpoint(t *testing.T, s server.Serverable) error {
_, certFile, certKey := s.GetTls()
sWithAlternateEndpoint := &S3Server{
BasicServer: *server.NewBasicServer(s.GetPort(), "localhost2", certFile, certKey, nil),
fqdns: []string{"localhost2"},
port: s.GetPort(),
tlsCertFilePath: certFile,
tlsKeyFilePath: certKey,
}
return runListObjectsV2AndReturnErrorForEndpoint(t, sWithAlternateEndpoint)
}
Expand Down
4 changes: 2 additions & 2 deletions aws/service/s3/proxys3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func setupSuiteProxyS3(
t testing.TB, proxyHB interfaces.HandlerBuilderI, pm *iam.PolicyManager, bm interfaces.BackendManager, mws []middleware.Middleware, tlsEnabled bool,
) (func(t testing.TB), *S3Server) {
s := NewTestS3Server(t, proxyHB, pm, bm, mws, tlsEnabled)
stsProxyDone, stsProxySrv, err := server.CreateAndStart(s)
stsProxyDone, stsProxySrv, err := server.CreateAndStart(s, server.ServerOpts{})
if err != nil {
t.Errorf("Could not spawn fake STS server %s", err)
}
Expand Down Expand Up @@ -458,7 +458,7 @@ func TestAllowEnablingTracingAtClientSide(t *testing.T) {
os.Setenv(logging.ENV_FORCE_LOGGING_FOR_REQUEST_ID_PREFIX, "00AABBCC")

//Given a way to capture logs
stopLogCapture, getLogLines := captureLogFixture(t, slog.LevelError, nil)
stopLogCapture, getLogLines := testutils.CaptureLogFixture(t, slog.LevelError, nil)
defer stopLogCapture()

//Given a uuid4 that starts with the prefix
Expand Down
7 changes: 0 additions & 7 deletions aws/service/s3/s3-errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ package s3
import (
"context"
"encoding/xml"
"errors"
"log/slog"
"net/http"

Expand Down Expand Up @@ -85,12 +84,6 @@ func writeS3ErrorResponse(ctx context.Context, w http.ResponseWriter, errCode S3
service.WriteResponse(ctx, w, s3Err.HTTPStatusCode, encodedErrorResponse, service.MimeXML)
}

func writeS3ErrorAccessDeniedResponse(ctx context.Context, w http.ResponseWriter) {
// nolint:staticcheck
writeS3ErrorResponse(ctx, w, ErrS3AccessDenied, errors.New("access Denied"))
}


type S3ErrorResponse struct {
XMLName xml.Name `xml:"Error" json:"-"`
Code string `xml:"Code"`
Expand Down
2 changes: 1 addition & 1 deletion aws/service/s3/s3-errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestS3Error(t *testing.T) {
r.Header.Set(requestctx.XRequestID, testReqID)
rr := httptest.NewRecorder()
ctx := requestctx.NewContextFromHttpRequest(r)
writeS3ErrorAccessDeniedResponse(ctx, rr)
writeS3ErrorResponse(ctx, rr, ErrS3AccessDenied, nil)
bodyBytes, err := io.ReadAll(rr.Body)
if err != nil {
t.Errorf("Could not read response body %s", err)
Expand Down
45 changes: 11 additions & 34 deletions aws/service/s3/server.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package s3

import (
"log/slog"
"errors"
"net/http"
"strings"
"time"
Expand All @@ -11,24 +11,17 @@ import (
"github.com/VITObelgium/fakes3pp/middleware"
"github.com/VITObelgium/fakes3pp/server"
"github.com/VITObelgium/fakes3pp/utils"
"github.com/minio/mux"
)


type S3Server struct{
server.BasicServer

//The Key material that is used for signing JWT tokens. Needed for verification.
jwtKeyMaterial utils.KeyPairKeeper

fqdns []string

port int

//The TLS certificate used to encrypt traffic with if omitted HTTP server will be spawned
tlsCertFilePath string

//The TLS key used to encrypt traffic with if omitted HTTP server will be spawned
tlsKeyFilePath string

pm *iam.PolicyManager

signedUrlGracePeriod time.Duration
Expand Down Expand Up @@ -95,13 +88,15 @@ func newS3Server(
if err != nil {
return nil, err
}
if len(fqdns) == 0 {
return nil, errors.New("must at least pass in 1 fqdn to create a server")
}
basicServer := server.NewBasicServer(serverPort, fqdns[0], tlsCertFilePath, tlsKeyFilePath, nil)

s = &S3Server{
BasicServer: *basicServer,
jwtKeyMaterial: key,
fqdns: fqdns,
port: serverPort,
tlsCertFilePath: tlsCertFilePath,
tlsKeyFilePath: tlsKeyFilePath,
pm: pm,
signedUrlGracePeriod: time.Duration(signedUrlGraceTimeSeconds) * time.Second,
proxyHB: proxyHB,
Expand All @@ -117,6 +112,7 @@ func newS3Server(
}
}
s.mws = mws
s.SetHandlerFunc(s.BuildHandlerfunc())
return s, nil
}

Expand All @@ -141,29 +137,10 @@ func (s *S3Server)IsVirtualHostingRequest(req *http.Request) bool {
return true
}


func (s *S3Server) GetPort() (int) {
return s.port
}

func (s *S3Server) GetTls() (enabled bool, certFile string, keyFile string) {
enabled = true
if certFile == "" {
slog.Debug("Disabling TLS", "reason", "no certFile provided")
enabled = false
} else if keyFile == "" {
slog.Debug("Disabling TLS", "reason", "no keyFile provided")
enabled = false
}
return enabled, s.tlsCertFilePath, s.tlsKeyFilePath
}

//Register routes to S3 router
//For real cases the proxyHB HandlerBuilder should build a handler function
//that sends the request upstream and passes back the response.
func (s *S3Server) RegisterRoutes(router *mux.Router) error {
func (s *S3Server) BuildHandlerfunc() http.HandlerFunc{
h := s.proxyHB.Build(s.s3BackendManager)

router.NewRoute().HandlerFunc(middleware.Chain(h, s.mws...))
return nil
return middleware.Chain(h, s.mws...)
}
9 changes: 9 additions & 0 deletions aws/service/sts/api/apiactions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package api

type STSOperation int

//go:generate stringer -type=STSOperation $GOFILE
const (
UnknownOperation STSOperation = iota
AssumeRoleWithWebIdentity
)
24 changes: 24 additions & 0 deletions aws/service/sts/api/stsoperation_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 16 additions & 30 deletions aws/service/sts/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/VITObelgium/fakes3pp/aws/credentials"
"github.com/VITObelgium/fakes3pp/aws/service"
"github.com/VITObelgium/fakes3pp/aws/service/iam"
"github.com/VITObelgium/fakes3pp/aws/service/sts/api"
"github.com/VITObelgium/fakes3pp/aws/service/sts/oidc"
"github.com/VITObelgium/fakes3pp/aws/service/sts/session"
"github.com/VITObelgium/fakes3pp/requestctx"
Expand All @@ -23,19 +24,13 @@ import (


type STSServer struct{
server.BasicServer

//The Key material that is used for signing JWT tokens.
jwtKeyMaterial utils.KeyPairKeeper

fqdns []string

port int

//The TLS certificate used to encrypt traffic with if omitted HTTP server will be spawned
tlsCertFilePath string

//The TLS key used to encrypt traffic with if omitted HTTP server will be spawned
tlsKeyFilePath string

//The verifier for OIDC IDP tokens
oidcVerifier oidc.OIDCVerifier

Expand Down Expand Up @@ -92,42 +87,32 @@ func newSTSServer(
if err != nil {
return nil, err
}
if len(fqdns) == 0 {
return nil, errors.New("must pass at least 1 FQDN")
}
s = &STSServer{
BasicServer: *server.NewBasicServer(serverPort, fqdns[0], tlsCertFilePath, tlsKeyFilePath, nil),
jwtKeyMaterial: key,
fqdns: fqdns,
port: serverPort,
tlsCertFilePath: tlsCertFilePath,
tlsKeyFilePath: tlsKeyFilePath,
oidcVerifier: oidcVerifier,
pm: pm,
maxAllowedDuration: time.Duration(maxDurationSeconds) * time.Second,
}
s.SetHandlerFunc(s.CreateHandler())
return s, nil
}

func (s *STSServer) GetPort() (int) {
return s.port
}

func (s *STSServer) GetTls() (bool, string, string) {
enabled := true
if s.tlsCertFilePath == "" {
slog.Debug("Disabling TLS", "reason", "no certFile provided")
enabled = false
} else if s.tlsKeyFilePath == "" {
slog.Debug("Disabling TLS", "reason", "no keyFile provided")
enabled = false
}
return enabled, s.tlsCertFilePath, s.tlsKeyFilePath
}

func (s *STSServer) RegisterRoutes(router *mux.Router) error {
func (s *STSServer) CreateHandler() http.HandlerFunc {
router := mux.NewRouter()
stsRouter := router.NewRoute().PathPrefix(server.SlashSeparator).Subrouter()

stsRouter.Methods(http.MethodPost).HandlerFunc(s.processSTSPost)

stsRouter.PathPrefix("/").HandlerFunc(justLog)
return nil

return func(w http.ResponseWriter, r *http.Request) {
stsRouter.ServeHTTP(w, r)
}
}

func justLog(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -186,9 +171,10 @@ type stsClaims map[string]interface{}
// - RoleSessionName
// - WebIdentityToken following the structure
func (s *STSServer)assumeRoleWithWebIdentity(ctx context.Context, w http.ResponseWriter, r *http.Request) {

requestctx.SetOperation(r, api.AssumeRoleWithWebIdentity)
claims := stsClaims{}
defer slog.InfoContext(ctx, "Auditlog", "claims", claims)
requestctx.AddAccessLogInfo(r, "sts", slog.Any("claims", claims))

token := r.Form.Get(stsWebIdentityToken)

Expand Down
2 changes: 1 addition & 1 deletion aws/service/sts/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestProxyStsAssumeRoleWithWebIdentitySessionTagsToken(t *testing.T) {
// This works like a fixture see https://medium.com/nerd-for-tech/setup-and-teardown-unit-test-in-go-bd6fa1b785cd
func setupSuiteProxySTS(t testing.TB, pm *iam.PolicyManager, oidcConfig string, tlsEnabled bool) (func(t testing.TB), *STSServer) {
s := NewTestSTSServer(t, pm, 3600, oidcConfig, tlsEnabled)
stsProxyDone, stsProxySrv, err := server.CreateAndStart(s)
stsProxyDone, stsProxySrv, err := server.CreateAndStart(s, server.ServerOpts{})
if err != nil {
t.Errorf("Could not spawn fake STS server %s", err)
}
Expand Down
Loading

0 comments on commit 0a8ccb6

Please sign in to comment.