From ed1504c2826f6a5d406dd72e51f5a90b77ffea45 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Wed, 21 Dec 2022 22:06:29 -0800 Subject: Upgrade to latest golib --- cmd/web/server.go | 119 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 52 deletions(-) (limited to 'cmd') diff --git a/cmd/web/server.go b/cmd/web/server.go index 610cc72..9c29544 100644 --- a/cmd/web/server.go +++ b/cmd/web/server.go @@ -14,59 +14,56 @@ import ( "code.crute.us/mcrute/cloud-identity-broker/auth" "code.crute.us/mcrute/cloud-identity-broker/auth/github" - "code.crute.us/mcrute/golib/crypto/tls" - "code.crute.us/mcrute/golib/db/mongodb" + "code.crute.us/mcrute/golib/cli" + "code.crute.us/mcrute/golib/clients/autocert/v2" + "code.crute.us/mcrute/golib/clients/netbox/v3" + "code.crute.us/mcrute/golib/db/mongodb/v2" glecho "code.crute.us/mcrute/golib/echo" glmw "code.crute.us/mcrute/golib/echo/middleware" - "code.crute.us/mcrute/golib/service" - "github.com/labstack/echo/v4" + "code.crute.us/mcrute/golib/secrets" echomw "github.com/labstack/echo/v4/middleware" + + "github.com/labstack/echo/v4" "github.com/spf13/cobra" "golang.org/x/time/rate" ) -func Register(root *cobra.Command, embeddedTemplates fs.FS, version string) { +func Register(root *cobra.Command, embeddedTemplates fs.FS, appVersion string) { webCmd := &cobra.Command{ Use: "web [options]", Short: "Run web server", Run: func(c *cobra.Command, args []string) { - webMain(app.NewConfigFromCmd(c), embeddedTemplates, version) + cfg := app.Config{} + cli.MustGetConfig(c, &cfg) + webMain(cfg, embeddedTemplates, appVersion) }, } - - webCmd.Flags().StringSlice("bind", []string{":8169"}, "Addresses and ports to bind http server") - webCmd.Flags().StringSlice("bind-tls", []string{":8170"}, "Addresses and ports to bind https server") - webCmd.Flags().String("log-file", "", "Log file for combined host logs") - webCmd.Flags().String("tls-cache-dir", "ssl/", "Cache directory for TLS certificates") - webCmd.Flags().StringSlice("trusted-ip-ranges", []string{"172.19.0.0/22", "2602:803:4072::/48"}, "Comma separated list of IP ranges for trusted XFF proxies") - webCmd.Flags().StringSlice("management-ip-ranges", []string{"127.0.0.1/32", "::1/128", "172.19.0.0/22", "2602:803:4072::/48"}, "IP ranges for management systems") - webCmd.Flags().StringSlice("hostname", []string{"dev.aws-access.crute.us"}, "Hostname this server serves (can be specified multiple times)") - webCmd.Flags().Duration("rate-limit", 30*time.Second, "Number seconds between requests for credential resources") - webCmd.Flags().Duration("auth-cookie-duration", 24*time.Hour, "Expiration duration of the auth cookies") - webCmd.Flags().Int("rate-limit-burst", 30, "Number of burst requests allowed to credential endpoints") - webCmd.Flags().String("issuer-endpoint", "https://aws-access.crute.us", "Oauth issuer endpoint") - webCmd.Flags().String("jwt-audience", "aws-access", "Audience for issued JWTs") - - webCmd.Flags().String("github-oauth-vault-path", "", "Vault material name for GitHub auth credentials") - webCmd.MarkFlagRequired("github-oauth-vault-path") - + cli.AddFlags(webCmd, &app.Config{}, app.DefaultConfig, "web") root.AddCommand(webCmd) } +// webMain does the pretty standard golib/Echo setup func webMain(cfg app.Config, embeddedTemplates fs.FS, version string) { - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + s, err := glecho.NewEchoWrapper(ctx, cfg.Debug) + if err != nil { + log.Fatalf("Error building echo: %s", err) + } + + vc, err := glecho.MakeVaultSecretsClient(ctx) + if err != nil { + log.Fatalf("Error making vault client %s", err) + } - s, err := glecho.NewDefaultEchoWithConfig(glecho.EchoConfig{ - Debug: cfg.Debug, - Hostnames: cfg.Hostnames, + if err = s.Configure(glecho.EchoConfig{ + ApplicationName: "cloud-identity-broker", + ApplicationVersion: version, BindAddresses: cfg.Bind, - BindTLSAddresses: cfg.BindTLS, - TLSCacheDir: cfg.TLSCacheDir, - TrustedProxyIPRanges: cfg.TrustedIPRanges, - ManagementIPRanges: cfg.ManagementIPRanges, - DiskTemplates: os.DirFS(cfg.TemplatePath), + DiskTemplates: os.DirFS("templates/"), EmbeddedTemplates: embeddedTemplates, - TemplateGlob: &cfg.TemplateGlob, + TrustedProxyIPRanges: cfg.TrustedIPRanges, CombinedHostLogFile: cfg.LogFile, ContentSecurityPolicy: &glmw.ContentSecurityPolicyConfig{ DefaultSrc: []glmw.CSPDirective{ @@ -79,20 +76,34 @@ func webMain(cfg app.Config, embeddedTemplates fs.FS, version string) { glmw.CSPUnsafeInline, }, }, - }) - if err != nil { - log.Fatalf("Error building echo: %w", err) - } - if err = s.Init(); err != nil { - log.Fatalf("Error initializing echo: %w", err) + Autocert: autocert.MustNewAutocertWrapper(ctx, autocert.AutocertConfig{ + ApiKey: secrets.MustGetApiKey(vc, ctx, cfg.DNSApiKeyVaultPath).Key, + Hosts: cfg.Hostnames, + Email: cfg.AutocertEmail, + CertHost: cfg.AutocertHost, + }), + NetboxClient: &netbox.BasicNetboxClient{ + Endpoint: cfg.NetboxHost, + ApiKey: secrets.MustGetApiKey(vc, ctx, cfg.NetboxApiKeyVaultPath).Key, + }, + }); err != nil { + log.Fatalf("Error building echo: %s", err) } - s.Use(middleware.AddServerHeader(version)) + glecho.AttachSecretsClient(vc, cancel, s.Runner(), s.Logger) - mongo, err := mongodb.Connect(ctx, cfg.MongoDbUri, cfg.MongodbVaultPath) + mongo, err := mongodb.Connect(ctx, cfg.MongoDbUri, vc) if err != nil { - log.Fatalf("Error connecting to mongodb: %w", err) + log.Fatalf("Error connecting to mongodb: %s", err) } + setupApplication(ctx, cfg, s, mongo, vc) + + s.RunForever(!cfg.DisableBackgroundJobs) +} + +// setupApplication does the setup work that's specific to this +// application +func setupApplication(ctx context.Context, cfg app.Config, s *glecho.EchoWrapper, mongo *mongodb.Mongo, vc secrets.Client) { rateLimit := echomw.RateLimiter( echomw.NewRateLimiterMemoryStoreWithConfig( echomw.RateLimiterMemoryStoreConfig{ @@ -103,26 +114,35 @@ func webMain(cfg app.Config, embeddedTemplates fs.FS, version string) { ), ) + // These admin prefixed stores have unsafe-by-default behavior that's + // necessary for some admin workflows. They should never be used in + // non-admin workflows. adminAccountStore := &models.MongoDbAccountStore{ Db: mongo, ReturnDeleted: true, } - as := &models.MongoDbAccountStore{Db: mongo} - adminUserStore := &models.MongoDbUserStore{ Db: mongo, ReturnDeleted: true, } + + // Use these for non-admin workflows + as := &models.MongoDbAccountStore{Db: mongo} us := &models.MongoDbUserStore{Db: mongo} aws := &controllers.AWSAPI{Store: as} + ghCred := &app.GitHubOauthCreds{} + if _, err := vc.Secret(ctx, cfg.GitHubOauthCreds, &ghCred); err != nil { + log.Fatalf("Error retrieving GitHub credentials: %s", err) + } + am := &middleware.AuthenticationMiddleware{ Store: us, CookieDuration: cfg.AuthCookieDuration, GitHub: &github.GitHubAuthenticator{ - ClientId: cfg.GitHubOauthCreds.ClientId, - ClientSecret: cfg.GitHubOauthCreds.ClientSecret, + ClientId: ghCred.ClientId, + ClientSecret: ghCred.ClientSecret, }, JWTManager: &auth.JWTManager{ Store: us, @@ -171,14 +191,9 @@ func webMain(cfg app.Config, embeddedTemplates fs.FS, version string) { }).Register("/:user", "", user) } } + s.GET("/favicon.ico", echo.NotFoundHandler) s.GET("/logout", controllers.LogoutHandler) s.CachedStaticRoute("/assets", "assets") s.GET("/", controllers.IndexHandler, am.Middleware) - - runner := service.NewAppRunner(ctx, s.Logger) - runner.AddJob(s.RunCertificateManager) // Cert manager has to start before server - runner.AddJob(tls.OcspErrorLogger(s.Logger, s.OcspErrors())) - runner.AddJobs(s.MakeServerJobs()) - runner.RunForever(!cfg.DisableBackgroundJobs) } -- cgit v1.2.3