Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
493 changes: 354 additions & 139 deletions api/gen/proto/go/teleport/plugins/v1/plugin_service.pb.go

Large diffs are not rendered by default.

53 changes: 47 additions & 6 deletions api/gen/proto/go/teleport/plugins/v1/plugin_service_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions api/proto/teleport/plugins/v1/plugin_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ import "teleport/legacy/types/types.proto";

option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/plugins/v1;pluginsv1";

// PluginType represents a single type of hosted plugin
// that can be onboarded.
message PluginType {
// Type is a string corresponding to api.PluginTypeXXX constants
string type = 1;

// OAuthClientID contains the client ID of the OAuth application
// that is used with this plugin's API provider.
// For plugins that are not authenticated via OAuth,
// this will be empty.
string oauth_client_id = 2;
}

// CreatePluginRequest creates a new plugin from the given spec and initial credentials.
message CreatePluginRequest {
// Plugin is the plugin object without live credentials.
Expand Down Expand Up @@ -81,6 +94,16 @@ message SetPluginStatusRequest {
types.PluginStatusV1 status = 2;
}

// GetAvailablePluginTypesRequest is the request type for GetAvailablePluginTypes
message GetAvailablePluginTypesRequest {}

// GetAvailablePluginTypesResponse is a response to for GetAvailablePluginTypes
message GetAvailablePluginTypesResponse {
// PluginTypes is a list of hosted plugins
// that the auth service supports.
repeated PluginType plugin_types = 1;
}

// PluginService provides CRUD operations for Plugin resources.
service PluginService {
// CreatePlugin creates a new plugin instance.
Expand All @@ -100,4 +123,8 @@ service PluginService {

// SetPluginCredentials sets the status for the given plugin.
rpc SetPluginStatus(SetPluginStatusRequest) returns (google.protobuf.Empty);

// GetAvailablePluginTypes returns the types of plugins
// that the auth server supports onboarding.
rpc GetAvailablePluginTypes(GetAvailablePluginTypesRequest) returns (GetAvailablePluginTypesResponse);
}
21 changes: 21 additions & 0 deletions api/types/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ import (
"github.com/gravitational/trace"
)

// PluginType represents the type of the plugin
type PluginType string

const (
// PluginTypeUnknown is returned when no plugin type matches.
PluginTypeUnknown PluginType = ""
// PluginTypeSlack is the Slack access request plugin
PluginTypeSlack = "slack"
)

// Plugin represents a plugin instance
type Plugin interface {
// ResourceWithSecrets provides common resource methods.
ResourceWithSecrets
Clone() Plugin
GetCredentials() PluginCredentials
GetStatus() PluginStatus
GetType() PluginType
SetCredentials(PluginCredentials) error
SetStatus(PluginStatus) error
}
Expand Down Expand Up @@ -211,6 +222,16 @@ func (p *PluginV1) SetStatus(status PluginStatus) error {
return nil
}

// GetType implements Plugin
func (p *PluginV1) GetType() PluginType {
switch p.Spec.Settings.(type) {
case *PluginSpecV1_SlackAccessPlugin:
return PluginTypeSlack
default:
return PluginTypeUnknown
}
}

// CheckAndSetDefaults validates and set the default values
func (s *PluginSlackAccessSettings) CheckAndSetDefaults() error {
if s.FallbackChannel == "" {
Expand Down
17 changes: 17 additions & 0 deletions lib/httplib/csrf/csrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
CookieName = "__Host-grv_csrf"
// HeaderName is the default HTTP request header to inspect.
HeaderName = "X-CSRF-Token"
// FormFieldName is the default form field to inspect.
FormFieldName = "csrf_token"
// tokenLenBytes is CSRF token length in bytes.
tokenLenBytes = 32
// defaultMaxAge is the default MaxAge for cookies.
Expand Down Expand Up @@ -76,6 +78,21 @@ func VerifyHTTPHeader(r *http.Request) error {
return nil
}

// VerifyFormField checks if HTTP form value matches the cookie.
func VerifyFormField(r *http.Request) error {
token := r.FormValue(FormFieldName)
if len(token) == 0 {
return trace.BadParameter("cannot retrieve CSRF token from form field %q", FormFieldName)
}

err := VerifyToken(token, r)
if err != nil {
return trace.Wrap(err)
}

return nil
}

// VerifyToken validates given token based on HTTP request cookie
func VerifyToken(token string, r *http.Request) error {
realToken, err := ExtractTokenFromCookie(r)
Expand Down
17 changes: 10 additions & 7 deletions lib/httplib/httplib.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,18 @@ func MakeStdHandlerWithErrorWriter(fn StdHandlerFunc, errWriter ErrorWriter) htt

// WithCSRFProtection ensures that request to unauthenticated API is checked against CSRF attacks
func WithCSRFProtection(fn HandlerFunc) httprouter.Handle {
hanlderFn := MakeHandler(fn)
handlerFn := MakeHandler(fn)
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
err := csrf.VerifyHTTPHeader(r)
if err != nil {
log.Warningf("unable to validate CSRF token %v", err)
trace.WriteError(w, trace.AccessDenied("access denied"))
return
if r.Method != http.MethodGet && r.Method != http.MethodHead {
errHeader := csrf.VerifyHTTPHeader(r)
errForm := csrf.VerifyFormField(r)
if errForm != nil && errHeader != nil {
log.Warningf("unable to validate CSRF token: %v, %v", errHeader, errForm)
trace.WriteError(w, trace.AccessDenied("access denied"))
return
}
}
hanlderFn(w, r, p)
handlerFn(w, r, p)
}
}

Expand Down
40 changes: 23 additions & 17 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,8 @@ import (
const (
// SSOLoginFailureMessage is a generic error message to avoid disclosing sensitive SSO failure messages.
SSOLoginFailureMessage = "Failed to login. Please check Teleport's log for more details."
metaRedirectHTML = `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Teleport Redirection Service</title>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="refresh" content="0;URL='{{.}}'" />
</head>
<body></body>
</html>
`
)

var metaRedirectTemplate = template.Must(template.New("meta-redirect").Parse(metaRedirectHTML))

// healthCheckAppServerFunc defines a function used to perform a health check
// to AppServer that can handle application requests (based on cluster name and
// public address).
Expand Down Expand Up @@ -860,6 +847,11 @@ func (h *Handler) getUserContext(w http.ResponseWriter, r *http.Request, p httpr
return userContext, nil
}

// PublicProxyAddr returns the publicly advertised proxy address
func (h *Handler) PublicProxyAddr() string {
return h.cfg.PublicProxyAddr
}

func localSettings(cap types.AuthPreference) (webclient.AuthenticationSettings, error) {
as := webclient.AuthenticationSettings{
Type: constants.Local,
Expand Down Expand Up @@ -1587,7 +1579,7 @@ func (h *Handler) installer(w http.ResponseWriter, r *http.Request, p httprouter
}

tmpl := installers.Template{
PublicProxyAddr: h.cfg.PublicProxyAddr,
PublicProxyAddr: h.PublicProxyAddr(),
MajorVersion: version,
TeleportPackage: teleportPackage,
RepoChannel: repoChannel,
Expand Down Expand Up @@ -3466,14 +3458,13 @@ func (h *Handler) WithRedirect(fn redirectHandlerFunc) httprouter.Handle {
// See https://github.com/gravitational/teleport/issues/7467.
func (h *Handler) WithMetaRedirect(fn redirectHandlerFunc) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
app.SetRedirectPageHeaders(w.Header(), "")
redirectURL := fn(w, r, p)
if !isValidRedirectURL(redirectURL) {
redirectURL = client.LoginFailedRedirectURL
}
err := metaRedirectTemplate.Execute(w, redirectURL)
err := app.MetaRedirect(w, redirectURL)
if err != nil {
h.log.WithError(err).Warn("Failed to execute template.")
h.log.WithError(err).Warn("Failed to issue a redirect.")
}
}
}
Expand All @@ -3489,6 +3480,21 @@ func (h *Handler) WithAuth(fn ContextHandler) httprouter.Handle {
})
}

// WithAuthCookieAndCSRF ensures that a request is authenticated
// for plain old non-AJAX requests (does not check the Bearer header).
// It enforces CSRF checks (except for "safe" methods).
func (h *Handler) WithAuthCookieAndCSRF(fn ContextHandler) httprouter.Handle {
f := func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
sctx, err := h.AuthenticateRequest(w, r, false)
if err != nil {
return nil, trace.Wrap(err)
}
return fn(w, r, p, sctx)
}

return httplib.WithCSRFProtection(f)
}

// WithLimiter adds IP-based rate limiting to fn.
func (h *Handler) WithLimiter(fn httplib.HandlerFunc) httprouter.Handle {
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
Expand Down
23 changes: 23 additions & 0 deletions lib/web/app/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,29 @@ package app

import (
"fmt"
"html/template"
"net/http"
"strings"

"github.com/gravitational/trace"

"github.com/gravitational/teleport/lib/httplib"
)

const metaRedirectHTML = `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Teleport Redirection Service</title>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="refresh" content="0;URL='{{.}}'" />
</head>
<body></body>
</html>
`

var metaRedirectTemplate = template.Must(template.New("meta-redirect").Parse(metaRedirectHTML))

func SetRedirectPageHeaders(h http.Header, nonce string) {
httplib.SetNoCacheHeaders(h)
httplib.SetDefaultSecurityHeaders(h)
Expand All @@ -42,3 +59,9 @@ func SetRedirectPageHeaders(h http.Header, nonce string) {
}, ";")
h.Set("Content-Security-Policy", csp)
}

// MetaRedirect issues a "meta refresh" redirect.
func MetaRedirect(w http.ResponseWriter, redirectURL string) error {
SetRedirectPageHeaders(w.Header(), "")
return trace.Wrap(metaRedirectTemplate.Execute(w, redirectURL))
}
2 changes: 1 addition & 1 deletion lib/web/connection_diagnostic.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (h *Handler) diagnoseConnection(w http.ResponseWriter, r *http.Request, p h
ResourceKind: req.ResourceKind,
UserClient: userClt,
ProxyHostPort: h.ProxyHostPort(),
PublicProxyAddr: h.cfg.PublicProxyAddr,
PublicProxyAddr: h.PublicProxyAddr(),
KubernetesPublicProxyAddr: h.kubeProxyHostPort(),
TLSRoutingEnabled: proxySettings.TLSRoutingEnabled,
}
Expand Down
Loading