diff options
author | Mike Crute <mike@crute.us> | 2021-11-16 14:46:24 -0800 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2021-11-17 07:56:10 -0800 |
commit | cc58a3da7d647de8520e33dc4356672d2ed1a366 (patch) | |
tree | 1b232a0d51446eb6370cfb13932190d31ce053df /cmd | |
parent | a42d794a286154a3106551e6e483861af2a9ef16 (diff) | |
download | cloud-identity-broker-cc58a3da7d647de8520e33dc4356672d2ed1a366.tar.bz2 cloud-identity-broker-cc58a3da7d647de8520e33dc4356672d2ed1a366.tar.xz cloud-identity-broker-cc58a3da7d647de8520e33dc4356672d2ed1a366.zip |
Import of source code
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/web/server.go | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/cmd/web/server.go b/cmd/web/server.go new file mode 100644 index 0000000..e76b6b2 --- /dev/null +++ b/cmd/web/server.go | |||
@@ -0,0 +1,155 @@ | |||
1 | package web | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "io/fs" | ||
6 | "log" | ||
7 | "os" | ||
8 | "time" | ||
9 | |||
10 | "code.crute.us/mcrute/cloud-identity-broker/app" | ||
11 | "code.crute.us/mcrute/cloud-identity-broker/app/controllers" | ||
12 | "code.crute.us/mcrute/cloud-identity-broker/app/middleware" | ||
13 | "code.crute.us/mcrute/cloud-identity-broker/app/models" | ||
14 | "code.crute.us/mcrute/cloud-identity-broker/auth" | ||
15 | "code.crute.us/mcrute/cloud-identity-broker/auth/github" | ||
16 | |||
17 | "code.crute.us/mcrute/golib/crypto/tls" | ||
18 | "code.crute.us/mcrute/golib/db/mongodb" | ||
19 | glecho "code.crute.us/mcrute/golib/echo" | ||
20 | glmw "code.crute.us/mcrute/golib/echo/middleware" | ||
21 | "code.crute.us/mcrute/golib/service" | ||
22 | "github.com/labstack/echo/v4" | ||
23 | echomw "github.com/labstack/echo/v4/middleware" | ||
24 | "github.com/spf13/cobra" | ||
25 | "golang.org/x/time/rate" | ||
26 | ) | ||
27 | |||
28 | func Register(root *cobra.Command, embeddedTemplates fs.FS) { | ||
29 | webCmd := &cobra.Command{ | ||
30 | Use: "web [options]", | ||
31 | Short: "Run web server", | ||
32 | Run: func(c *cobra.Command, args []string) { | ||
33 | webMain(app.NewConfigFromCmd(c), embeddedTemplates) | ||
34 | }, | ||
35 | } | ||
36 | |||
37 | webCmd.Flags().StringSlice("bind", []string{":8169"}, "Addresses and ports to bind http server") | ||
38 | webCmd.Flags().StringSlice("bind-tls", []string{":8170"}, "Addresses and ports to bind https server") | ||
39 | webCmd.Flags().String("log-file", "", "Log file for combined host logs") | ||
40 | webCmd.Flags().String("tls-cache-dir", "ssl/", "Cache directory for TLS certificates") | ||
41 | 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") | ||
42 | 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") | ||
43 | webCmd.Flags().StringSlice("hostname", []string{"dev.aws-access.crute.us"}, "Hostname this server serves (can be specified multiple times)") | ||
44 | webCmd.Flags().Duration("rate-limit", 30*time.Second, "Number seconds between requests for credential resources") | ||
45 | webCmd.Flags().Duration("auth-cookie-duration", 24*time.Hour, "Expiration duration of the auth cookies") | ||
46 | webCmd.Flags().Int("rate-limit-burst", 30, "Number of burst requests allowed to credential endpoints") | ||
47 | webCmd.Flags().String("issuer-endpoint", "https://aws-access.crute.us", "Oauth issuer endpoint") | ||
48 | webCmd.Flags().String("jwt-audience", "aws-access", "Audience for issued JWTs") | ||
49 | |||
50 | webCmd.Flags().String("github-oauth-vault-path", "", "Vault material name for GitHub auth credentials") | ||
51 | webCmd.MarkFlagRequired("github-oauth-vault-path") | ||
52 | |||
53 | root.AddCommand(webCmd) | ||
54 | } | ||
55 | |||
56 | func webMain(cfg app.Config, embeddedTemplates fs.FS) { | ||
57 | ctx := context.Background() | ||
58 | |||
59 | s, err := glecho.NewDefaultEchoWithConfig(glecho.EchoConfig{ | ||
60 | Debug: cfg.Debug, | ||
61 | Hostnames: cfg.Hostnames, | ||
62 | BindAddresses: cfg.Bind, | ||
63 | BindTLSAddresses: cfg.BindTLS, | ||
64 | TLSCacheDir: cfg.TLSCacheDir, | ||
65 | TrustedProxyIPRanges: cfg.TrustedIPRanges, | ||
66 | ManagementIPRanges: cfg.ManagementIPRanges, | ||
67 | DiskTemplates: os.DirFS(cfg.TemplatePath), | ||
68 | EmbeddedTemplates: embeddedTemplates, | ||
69 | TemplateGlob: &cfg.TemplateGlob, | ||
70 | CombinedHostLogFile: cfg.LogFile, | ||
71 | ContentSecurityPolicy: &glmw.ContentSecurityPolicyConfig{ | ||
72 | DefaultSrc: []glmw.CSPDirective{ | ||
73 | glmw.CSPSelf, | ||
74 | glmw.CSPUnsafeInline, | ||
75 | }, | ||
76 | StyleSrc: []glmw.CSPDirective{ | ||
77 | glmw.CSPSelf, | ||
78 | glmw.CSPData, | ||
79 | glmw.CSPUnsafeInline, | ||
80 | }, | ||
81 | }, | ||
82 | }) | ||
83 | if err != nil { | ||
84 | log.Fatalf("Error building echo: %w", err) | ||
85 | } | ||
86 | if err = s.Init(); err != nil { | ||
87 | log.Fatalf("Error initializing echo: %w", err) | ||
88 | } | ||
89 | |||
90 | mongo, err := mongodb.Connect(ctx, cfg.MongoDbUri, cfg.MongodbVaultPath) | ||
91 | if err != nil { | ||
92 | log.Fatalf("Error connecting to mongodb: %w", err) | ||
93 | } | ||
94 | |||
95 | rateLimit := echomw.RateLimiter( | ||
96 | echomw.NewRateLimiterMemoryStoreWithConfig( | ||
97 | echomw.RateLimiterMemoryStoreConfig{ | ||
98 | Rate: rate.Every(cfg.RateLimit), | ||
99 | Burst: cfg.RateLimitBurst, | ||
100 | ExpiresIn: time.Hour, | ||
101 | }, | ||
102 | ), | ||
103 | ) | ||
104 | |||
105 | as := &models.MongoDbAccountStore{Db: mongo} | ||
106 | us := &models.MongoDbUserStore{Db: mongo} | ||
107 | |||
108 | aws := &controllers.AWSAPI{Store: as} | ||
109 | |||
110 | am := &middleware.AuthenticationMiddleware{ | ||
111 | Store: us, | ||
112 | CookieDuration: cfg.AuthCookieDuration, | ||
113 | GitHub: &github.GitHubAuthenticator{ | ||
114 | ClientId: cfg.GitHubOauthCreds.ClientId, | ||
115 | ClientSecret: cfg.GitHubOauthCreds.ClientSecret, | ||
116 | }, | ||
117 | JWTManager: &auth.JWTManager{ | ||
118 | Store: us, | ||
119 | Audience: cfg.JWTAudience, | ||
120 | TokenExpires: cfg.AuthCookieDuration, | ||
121 | }, | ||
122 | } | ||
123 | am.RegisterUrls(s) | ||
124 | |||
125 | api := s.Group("/api/account") | ||
126 | api.Use(glmw.VaryCookie()) | ||
127 | api.Use(glmw.CacheNeverMiddleware) | ||
128 | api.Use(am.Middleware) | ||
129 | { | ||
130 | api.GET("", controllers.NewAPIAccountListHandler(as)) | ||
131 | api.GET( | ||
132 | "/:account/credentials", | ||
133 | controllers.NewAPIRegionListHandler(aws), | ||
134 | ) | ||
135 | api.GET( | ||
136 | "/:account/console", | ||
137 | controllers.NewAPIConsoleRedirectHandler(aws, cfg.IssuerEndpoint), | ||
138 | rateLimit, | ||
139 | ) | ||
140 | api.GET( | ||
141 | "/:account/credentials/:region", | ||
142 | controllers.NewAPICredentialsHandler(aws), | ||
143 | rateLimit, | ||
144 | ) | ||
145 | } | ||
146 | s.GET("/favicon.ico", echo.NotFoundHandler) | ||
147 | s.GET("/logout", controllers.LogoutHandler) | ||
148 | s.GET("/", controllers.IndexHandler, am.Middleware) | ||
149 | |||
150 | runner := service.NewAppRunner(ctx, s.Logger) | ||
151 | runner.AddJob(s.RunCertificateManager) // Cert manager has to start before server | ||
152 | runner.AddJob(tls.OcspErrorLogger(s.Logger, s.OcspErrors())) | ||
153 | runner.AddJobs(s.MakeServerJobs()) | ||
154 | runner.RunForever(!cfg.DisableBackgroundJobs) | ||
155 | } | ||