diff --git a/.github/workflows/test-deploy-publish.yml b/.github/workflows/test-deploy-publish.yml index 5593f2a..a7fd547 100644 --- a/.github/workflows/test-deploy-publish.yml +++ b/.github/workflows/test-deploy-publish.yml @@ -94,6 +94,7 @@ jobs: WEBAUTHN_TABLE: ${{ vars.WEBAUTHN_TABLE }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} run: cd cdk && cdk deploy --require-approval never build-and-publish: diff --git a/cdk/cdk.go b/cdk/cdk.go index c5d510c..86b8609 100644 --- a/cdk/cdk.go +++ b/cdk/cdk.go @@ -48,6 +48,7 @@ func NewCdkStack(scope constructs.Construct, id string, props *CdkStackProps) aw "TOTP_TABLE": jsii.String(totpTable), "WEBAUTHN_TABLE": jsii.String(webauthnTable), "AWS_ENDPOINT": jsii.String(""), + "SENTRY_DSN": jsii.String(os.Getenv("SENTRY_DSN")), }, FunctionName: &functionName, Handler: jsii.String("bootstrap"), diff --git a/config.go b/config.go index 2c0c654..2711c2b 100644 --- a/config.go +++ b/config.go @@ -22,6 +22,8 @@ type EnvConfig struct { AwsDefaultRegion string `default:"" split_words:"true"` AWSConfig aws.Config `json:"-"` + + SentryDSN string `split_words:"true"` } func (e *EnvConfig) InitAWS() { diff --git a/go.mod b/go.mod index 8a914bd..3f01833 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.2 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.43.3 github.com/fxamacker/cbor/v2 v2.8.0 + github.com/getsentry/sentry-go v0.35.3 github.com/go-webauthn/webauthn v0.11.2 github.com/google/uuid v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 @@ -42,6 +43,6 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/sys v0.33.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/text v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index df9ba2f..6f209cf 100644 --- a/go.sum +++ b/go.sum @@ -42,28 +42,35 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/getsentry/sentry-go v0.35.3 h1:u5IJaEqZyPdWqe/hKlBKBBnMTSxB/HenCqF3QLabeds= +github.com/getsentry/sentry-go v0.35.3/go.mod h1:mdL49ixwT2yi57k5eh7mpnDyPybixPzlzEJFu0Z76QA= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc= github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0= github.com/go-webauthn/x v0.1.21 h1:nFbckQxudvHEJn2uy1VEi713MeSpApoAv9eRqsb9AdQ= github.com/go-webauthn/x v0.1.21/go.mod h1:sEYohtg1zL4An1TXIUIQ5csdmoO+WO0R4R2pGKaHYKA= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc= github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= @@ -77,10 +84,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/lambda/main.go b/lambda/main.go index cad88a4..2d999c7 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -8,9 +8,11 @@ import ( "net/url" "os" "strings" + "time" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" + "github.com/getsentry/sentry-go" "github.com/kelseyhightower/envconfig" mfa "github.com/silinternational/serverless-mfa-api-go" @@ -39,6 +41,8 @@ func main() { envConfig.InitAWS() mfa.SetConfig(envConfig) + sentryInit() + lambda.Start(handler) } @@ -56,6 +60,12 @@ func handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.API headers[k] = v[0] } + if w.Status == http.StatusInternalServerError && envConfig.SentryDSN != "" { + logger := sentry.NewLogger(ctx) + logger.Error().Emit(string(w.Body)) + defer sentry.Flush(2 * time.Second) + } + return events.APIGatewayProxyResponse{ StatusCode: w.Status, Headers: headers, @@ -82,3 +92,16 @@ func httpRequestFromProxyRequest(ctx context.Context, req events.APIGatewayProxy return r.WithContext(ctx) } + +func sentryInit() { + if envConfig.SentryDSN == "" { + return + } + + if err := sentry.Init(sentry.ClientOptions{ + Dsn: envConfig.SentryDSN, + EnableLogs: true, + }); err != nil { + log.Printf("Sentry initialization failed: %v\n", err) + } +} diff --git a/lambda/main_test.go b/lambda/main_test.go new file mode 100644 index 0000000..2707100 --- /dev/null +++ b/lambda/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test_sentryInit(t *testing.T) { + sentryInit() +} diff --git a/router/router.go b/router/router.go index b0dcd42..5cdf42e 100644 --- a/router/router.go +++ b/router/router.go @@ -19,17 +19,17 @@ func NewMux(app *mfa.App) *http.ServeMux { // getRoutes returns a list of routes for the server func getRoutes(app *mfa.App) map[string]http.HandlerFunc { return map[string]http.HandlerFunc{ - "POST /api-key/activate": app.ActivateApiKey, - "POST /api-key/rotate": app.RotateApiKey, - "POST /api-key": app.CreateApiKey, - "POST /totp": app.CreateTOTP, - "DELETE /totp/{" + mfa.UUIDParam + "}": app.DeleteTOTP, - "POST /totp/{" + mfa.UUIDParam + "}/validate": app.ValidateTOTP, - "POST /webauthn/register": app.BeginRegistration, - "PUT /webauthn/register": app.FinishRegistration, - "POST /webauthn/login": app.BeginLogin, - "PUT /webauthn/login": app.FinishLogin, - "DELETE /webauthn/user": app.DeleteUser, + "POST /api-key/activate": app.ActivateApiKey, + "POST /api-key/rotate": app.RotateApiKey, + "POST /api-key": app.CreateApiKey, + "POST /totp": app.CreateTOTP, + "DELETE /totp/{" + mfa.UUIDParam + "}": app.DeleteTOTP, + "POST /totp/{" + mfa.UUIDParam + "}/validate": app.ValidateTOTP, + "POST /webauthn/register": app.BeginRegistration, + "PUT /webauthn/register": app.FinishRegistration, + "POST /webauthn/login": app.BeginLogin, + "PUT /webauthn/login": app.FinishLogin, + "DELETE /webauthn/user": app.DeleteUser, "DELETE /webauthn/credential/{" + mfa.IDParam + "}": app.DeleteCredential, } }