diff --git a/Dockerfile b/Dockerfile index 86f4517..53fd34b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,12 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN go install ./... + +RUN go build -o /go/bin/webauthn-oidc-idp -ldflags "\ + -X 'github.com/prometheus/common/version.Branch=$(git describe --contains --all HEAD)' \ + -X 'github.com/prometheus/common/version.BuildUser=$(whoami)' \ + -X 'github.com/prometheus/common/version.BuildDate=$(date --iso-8601=seconds)'" \ + ./... FROM debian:bookworm diff --git a/e2e_test.go b/e2e_test.go index 77bee8a..6346036 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -101,7 +101,7 @@ func TestE2E(t *testing.T) { serveErr := make(chan error, 1) go func() { - if err := serve(serveCtx, db, issConfig, net.JoinHostPort("localhost", port)); err != nil { + if err := serve(serveCtx, db, issConfig, net.JoinHostPort("localhost", port), ""); err != nil { serveErr <- err } }() diff --git a/go.mod b/go.mod index 83db2fc..2773154 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,16 @@ require ( github.com/lstoll/oidc v1.0.0-alpha.1 github.com/mattn/go-sqlite3 v1.14.13 github.com/oklog/run v1.1.0 + github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/common v0.50.0 github.com/tink-crypto/tink-go/v2 v2.1.0 - golang.org/x/oauth2 v0.17.0 - golang.org/x/sys v0.17.0 + golang.org/x/oauth2 v0.18.0 + golang.org/x/sys v0.18.0 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chromedp/sysutil v1.0.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/go-webauthn/x v0.1.8 // indirect @@ -32,10 +36,12 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/crypto v0.20.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect ) replace crawshaw.dev/jsonfile => github.com/sr/jsonfile v0.0.0-20240301210704-69e8a5b5b148 diff --git a/go.sum b/go.sum index b537004..3ddf6f2 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 h1:XYUCaZrW8ckGWlCRJKCSoh/iFwlpX316a8yY9IFEzv8= github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg= @@ -53,6 +57,14 @@ github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhA github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= 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/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ= +github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/sr/jsonfile v0.0.0-20240301210704-69e8a5b5b148 h1:gQ7t2Q9iRRtBYAvcszOA9vFGdtW3R/4pT+itPH8b22c= github.com/sr/jsonfile v0.0.0-20240301210704-69e8a5b5b148/go.mod h1:rWYpwcta+HEXjFjbDiSFGqzhZ5KPlNhit+irVCFLQAI= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -70,8 +82,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -81,8 +93,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -100,7 +112,7 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 8d94191..07328be 100644 --- a/main.go +++ b/main.go @@ -24,17 +24,34 @@ import ( "github.com/lstoll/oidc/core/staticclients" "github.com/lstoll/oidc/discovery" "github.com/oklog/run" + "github.com/prometheus/client_golang/prometheus" + versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/version" "github.com/tink-crypto/tink-go/v2/keyset" "golang.org/x/sys/unix" ) +const progname = "webauthn-oidc-idp" + //go:embed web/public/* var staticFiles embed.FS +func init() { + if version.Version == "" { + version.Version = "devel" + } + if version.Branch == "" { + version.Branch = "unknown" + } + prometheus.MustRegister(versioncollector.NewCollector(strings.ReplaceAll(progname, "-", "_"))) +} + func main() { + ver := flag.Bool("version", false, "Print the version and exit.") debug := flag.Bool("debug", false, "Enable debug logging") - addr := flag.String("http", "127.0.0.1:8085", "Run the IDP server on the given host:port.") + metrics := flag.String("metrics", "", "Expose Prometheus metrics on the given host:port.") configFile := flag.String("config", "config.json", "Path to the config file.") enroll := flag.Bool("enroll", false, "Enroll a user into the system.") email := flag.String("email", "", "Email address for the user.") @@ -45,6 +62,11 @@ func main() { flag.Parse() + if *ver { + fmt.Fprintln(os.Stdout, version.Print(progname)) + os.Exit(0) + } + b, err := os.ReadFile(*configFile) if err != nil { fatalf("read config file: %v", err) @@ -134,12 +156,12 @@ func main() { issuer := cfg.Issuer[0] - if err := serve(ctx, db, issuer, *addr); err != nil { + if err := serve(ctx, db, issuer, *addr, *metrics); err != nil { fatalf("start server: %v", err) } } -func serve(ctx context.Context, db *DB, issuer issuerConfig, addr string) error { +func serve(ctx context.Context, db *DB, issuer issuerConfig, addr, metrics string) error { ksm, err := NewKeysetManager(db) if err != nil { return fmt.Errorf("creating OIDC keyset manager: %w", err) @@ -296,6 +318,24 @@ func serve(ctx context.Context, db *DB, issuer issuerConfig, addr string) error _ = hs.Shutdown(ctx) }) + { + if metrics != "" { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + promsrv := &http.Server{Addr: metrics, Handler: mux} + + g.Add(func() error { + slog.Info("metrics server listing", slog.String("addr", "http://"+metrics)) + if err := promsrv.ListenAndServe(); err != nil { + return fmt.Errorf("serving metrics: %v", err) + } + return nil + }, func(error) { + promsrv.Close() + }) + } + } + return g.Run() } @@ -329,12 +369,12 @@ func reloadDB(addr string) { } func fatal(s string) { - fmt.Fprintf(os.Stderr, "webauthn-oidc-idp: %s\n", s) + fmt.Fprintf(os.Stderr, "%s: %s\n", progname, s) os.Exit(1) } func fatalf(s string, args ...any) { - fmt.Fprintf(os.Stderr, fmt.Sprintf("webauthn-oidc-idp: %s\n", s), args...) + fmt.Fprintf(os.Stderr, fmt.Sprintf("%s: %s\n", progname, s), args...) os.Exit(1) }