mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
* 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>
154 lines
4.6 KiB
Go
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
|