Files
teleport/lib/web/app/middleware.go
T
Gabriel CoradoandRafał Cieślak bc63dddd7b Verify if proxy can handle application requests when creating session (#20418)
* feat(app): ensure there are healthy app services when creating session

* Apply suggestions from code review

Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>

* chore: code review updates

* refactor(app): preflight connection before creating session

* chore(web): change error message

* reafactor(app): change redirect to launcher rule

* refactor(app): rename preflight connection to health check app server

* refactor(apiserver): rename preflight connection

* refactor(apiserver): rename preflight connection

* refactor(apiserver): remove health check property

* test(appaccess): adjust new http status codes returned

---------

Co-authored-by: Rafał Cieślak <rafal.cieslak@goteleport.com>
2023-02-10 10:42:55 +00:00

154 lines
4.6 KiB
Go

/*
Copyright 2020 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package app
import (
"net/http"
"net/url"
"path"
"github.com/gravitational/trace"
"github.com/julienschmidt/httprouter"
"github.com/gravitational/teleport/lib/utils"
)
// withRouterAuth authenticates requests then hands the request to a
// httprouter.Handler handler.
func (h *Handler) withRouterAuth(handler routerAuthFunc) httprouter.Handle {
return makeRouterHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) error {
session, err := h.authenticate(r.Context(), r)
if err != nil {
return trace.Wrap(err)
}
if err := handler(w, r, p, session); err != nil {
return trace.Wrap(err)
}
return nil
})
}
// withAuth authenticates requests then hands the request to a http.HandlerFunc
// handler.
func (h *Handler) withAuth(handler handlerAuthFunc) http.HandlerFunc {
return makeHandler(func(w http.ResponseWriter, r *http.Request) error {
// If the caller fails to authenticate, redirect the caller to Teleport.
session, err := h.authenticate(r.Context(), r)
if err != nil {
if redirectErr := h.redirectToLauncher(w, r, launcherURLParams{}); redirectErr == nil {
return nil
}
return trace.Wrap(err)
}
if err := handler(w, r, session); err != nil {
return trace.Wrap(err)
}
return nil
})
}
// redirectToLauncher redirects to the proxy web's app launcher if the public
// address of the proxy is set.
func (h *Handler) redirectToLauncher(w http.ResponseWriter, r *http.Request, p launcherURLParams) error {
// The application launcher can only generate browser sessions (based on
// Cookies). Given this, we should only redirect to it when this format is
// already in use.
if !HasSession(r) {
return trace.BadParameter("redirecting to launcher when using client certificate is not valid")
}
if h.c.WebPublicAddr == "" {
// The error below tends to be swallowed by the Web UI, so log a warning for
// admins as well.
h.log.Error("" +
"Application Service requires public_addr to be set in the Teleport Proxy Service configuration. " +
"Please contact your Teleport cluster administrator or refer to " +
"https://goteleport.com/docs/application-access/guides/connecting-apps/#start-authproxy-service.")
return trace.BadParameter("public address of the proxy is not set")
}
addr, err := utils.ParseAddr(r.Host)
if err != nil {
return trace.Wrap(err)
}
urlPath := []string{"web", "launch", addr.Host()}
if p.clusterName != "" && p.publicAddr != "" {
urlPath = append(urlPath, p.clusterName, p.publicAddr)
}
urlQuery := url.Values{}
if p.stateToken != "" {
urlQuery.Add("state", p.stateToken)
}
if p.awsRole != "" {
urlQuery.Add("awsrole", p.awsRole)
}
if p.path != "" {
urlQuery.Add("path", p.path)
}
u := url.URL{
Scheme: "https",
Host: h.c.WebPublicAddr,
Path: path.Join(urlPath...),
RawQuery: urlQuery.Encode(),
}
http.Redirect(w, r, u.String(), http.StatusFound)
return nil
}
type launcherURLParams struct {
clusterName string
publicAddr string
stateToken string
awsRole string
path string
}
// makeRouterHandler creates a httprouter.Handle.
func makeRouterHandler(handler routerFunc) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
if err := handler(w, r, p); err != nil {
writeError(w, err)
return
}
}
}
// makeHandler creates a http.HandlerFunc.
func makeHandler(handler handlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := handler(w, r); err != nil {
writeError(w, err)
return
}
}
}
// writeError gets the HTTP status code from trace and writes the error to the
// response writer.
func writeError(w http.ResponseWriter, err error) {
code := trace.ErrorToCode(err)
http.Error(w, http.StatusText(code), code)
}
type routerFunc func(http.ResponseWriter, *http.Request, httprouter.Params) error
type routerAuthFunc func(http.ResponseWriter, *http.Request, httprouter.Params, *session) error
type handlerAuthFunc func(http.ResponseWriter, *http.Request, *session) error
type handlerFunc func(http.ResponseWriter, *http.Request) error