|
| 1 | +// Package main contains the definition of all routes, proxying and authentication |
| 2 | +// performed by the reverse proxy that is part of the Renku gateway. |
| 3 | +package main |
| 4 | + |
| 5 | +import ( |
| 6 | + "context" |
| 7 | + "fmt" |
| 8 | + "net/http" |
| 9 | + "os" |
| 10 | + "os/signal" |
| 11 | + "time" |
| 12 | + |
| 13 | + "github.com/labstack/echo/v4" |
| 14 | + "github.com/labstack/echo/v4/middleware" |
| 15 | +) |
| 16 | + |
| 17 | +func setupServer(config revProxyConfig) *echo.Echo { |
| 18 | + // Intialize common reverse proxy middlewares |
| 19 | + fallbackProxy := proxyFromURL(config.RenkuBaseURL) |
| 20 | + renkuBaseProxyHost := setHost(config.RenkuBaseURL.Host) |
| 21 | + var gitlabProxy, gitlabProxyHost echo.MiddlewareFunc |
| 22 | + if config.ExternalGitlabURL != nil { |
| 23 | + gitlabProxy = proxyFromURL(config.ExternalGitlabURL) |
| 24 | + gitlabProxyHost = setHost(config.ExternalGitlabURL.Host) |
| 25 | + } else { |
| 26 | + gitlabProxy = fallbackProxy |
| 27 | + gitlabProxyHost = setHost(config.RenkuBaseURL.Host) |
| 28 | + } |
| 29 | + notebooksProxy := proxyFromURL(config.RenkuServices.Notebooks) |
| 30 | + authSvcProxy := proxyFromURL(config.RenkuServices.Auth) |
| 31 | + coreProxy := proxyFromURL(config.RenkuServices.Core) |
| 32 | + kgProxy := proxyFromURL(config.RenkuServices.KG) |
| 33 | + webhookProxy := proxyFromURL(config.RenkuServices.Webhook) |
| 34 | + logger := middleware.Logger() |
| 35 | + |
| 36 | + // Initialize common authentication middleware |
| 37 | + notebooksAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "notebook"}), "Renku-Auth-Access-Token", "Renku-Auth-Id-Token", "Renku-Auth-Git-Credentials", "Renku-Auth-Anon-Id", "Renku-Auth-Refresh-Token") |
| 38 | + renkuAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "renku"}), "Authorization", "Renku-user-id", "Renku-user-fullname", "Renku-user-email") |
| 39 | + gitlabAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "gitlab"}), "Authorization") |
| 40 | + cliGitlabAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "cli-gitlab"}), "Authorization") |
| 41 | + |
| 42 | + // Server instance |
| 43 | + e := echo.New() |
| 44 | + e.Pre(middleware.RemoveTrailingSlash()) |
| 45 | + e.Use(middleware.Recover()) |
| 46 | + |
| 47 | + // Routing for Renku services |
| 48 | + e.Group("/api/auth", logger, authSvcProxy) |
| 49 | + e.Group("/api/notebooks", logger, notebooksAuth, noCookies, stripPrefix("/api"), notebooksProxy) |
| 50 | + e.Group("/api/projects/:projectID/graph", logger, gitlabAuth, noCookies, kgProjectsGraphRewrites, webhookProxy) |
| 51 | + e.Group("/api/datasets", logger, noCookies, regexRewrite("^/api(.*)", "/knowledge-graph$1"), kgProxy) |
| 52 | + e.Group("/api/kg", logger, gitlabAuth, noCookies, regexRewrite("^/api/kg(.*)", "/knowledge-graph$1"), kgProxy) |
| 53 | + e.Group("/api/renku", logger, renkuAuth, noCookies, stripPrefix("/api"), coreProxy) |
| 54 | + |
| 55 | + // Routes that end up proxied to Gitlab |
| 56 | + if config.ExternalGitlabURL != nil { |
| 57 | + // Redirect "old" style bundled /gitlab pathing if an external Gitlab is used |
| 58 | + e.Group("/gitlab", logger, stripPrefix("/gitlab"), gitlabProxyHost, gitlabProxy) |
| 59 | + e.Group("/api/graphql", logger, gitlabAuth, gitlabProxyHost, gitlabProxy) |
| 60 | + e.Group("/api/direct", logger, stripPrefix("/api/direct"), gitlabProxyHost, gitlabProxy) |
| 61 | + e.Group("/api/repos", logger, cliGitlabAuth, noCookies, stripPrefix("/api/repos"), gitlabProxyHost, gitlabProxy) |
| 62 | + // If nothing is matched in any other more specific /api route then fall back to Gitlab |
| 63 | + e.Group("/api", logger, gitlabAuth, noCookies, regexRewrite("^/api(.*)", "/api/v4$1"), gitlabProxyHost, gitlabProxy) |
| 64 | + } else { |
| 65 | + e.Group("/api/graphql", logger, gitlabAuth, regexRewrite("^(.*)", "/gitlab$1"), gitlabProxyHost, gitlabProxy) |
| 66 | + e.Group("/api/direct", logger, regexRewrite("^/api/direct(.*)", "/gitlab$1"), gitlabProxyHost, gitlabProxy) |
| 67 | + e.Group("/api/repos", logger, cliGitlabAuth, noCookies, regexRewrite("^/api/repos(.*)", "/gitlab$1"), gitlabProxyHost, gitlabProxy) |
| 68 | + // If nothing is matched in any other more specific /api route then fall back to Gitlab |
| 69 | + e.Group("/api", logger, gitlabAuth, noCookies, regexRewrite("^/api(.*)", "/gitlab/api/v4$1"), gitlabProxyHost, gitlabProxy) |
| 70 | + } |
| 71 | + |
| 72 | + // If nothing is matched from any of the routes above then fall back to the UI |
| 73 | + e.Group("/", logger, renkuBaseProxyHost, fallbackProxy) |
| 74 | + |
| 75 | + // Reverse proxy specific endpoints |
| 76 | + rp := e.Group("/revproxy") |
| 77 | + rp.GET("/health", func(c echo.Context) error { |
| 78 | + return c.JSON(http.StatusOK, map[string]string{"status": "ok"}) |
| 79 | + }) |
| 80 | + |
| 81 | + return e |
| 82 | +} |
| 83 | + |
| 84 | +func main() { |
| 85 | + config := getConfig() |
| 86 | + e := setupServer(config) |
| 87 | + // Start API server |
| 88 | + e.Logger.Printf("Starting server with config: %+v", config) |
| 89 | + go func() { |
| 90 | + if err := e.Start(fmt.Sprintf(":%d", config.Port)); err != nil && err != http.ErrServerClosed { |
| 91 | + e.Logger.Fatal(err) |
| 92 | + } |
| 93 | + }() |
| 94 | + // Start metrics server if enabled |
| 95 | + var metricsServer *echo.Echo |
| 96 | + if (config.Metrics.Enabled) { |
| 97 | + metricsServer = getMetricsServer(e, config.Metrics.Port) |
| 98 | + go func() { |
| 99 | + if err := metricsServer.Start(fmt.Sprintf(":%d", config.Metrics.Port)); err != nil && err != http.ErrServerClosed { |
| 100 | + metricsServer.Logger.Fatal(err) |
| 101 | + } |
| 102 | + }() |
| 103 | + } |
| 104 | + quit := make(chan os.Signal, 1) |
| 105 | + signal.Notify(quit, os.Interrupt) |
| 106 | + <-quit // Wait for interrupt signal from OS |
| 107 | + // Start shutting down servers |
| 108 | + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) |
| 109 | + defer cancel() |
| 110 | + if err := e.Shutdown(ctx); err != nil { |
| 111 | + e.Logger.Fatal(err) |
| 112 | + } |
| 113 | + if config.Metrics.Enabled { |
| 114 | + if err := metricsServer.Shutdown(ctx); err != nil { |
| 115 | + metricsServer.Logger.Fatal(err) |
| 116 | + } |
| 117 | + } |
| 118 | +} |
0 commit comments