teleport/lib/auth/apiserver.go

2512 lines
86 KiB
Go
Raw Normal View History

2015-10-31 18:56:49 +00:00
/*
Copyright 2015-2021 Gravitational, Inc.
2015-10-31 18:56:49 +00:00
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.
*/
2015-03-02 20:11:23 +00:00
package auth
import (
"bytes"
"context"
2016-04-05 16:58:16 +00:00
"encoding/json"
2015-03-02 20:11:23 +00:00
"fmt"
"io"
2017-05-27 01:03:19 +00:00
"io/ioutil"
2015-03-02 20:11:23 +00:00
"net/http"
2016-04-03 05:20:51 +00:00
"net/url"
"strconv"
"strings"
2015-03-02 20:11:23 +00:00
"time"
2015-08-25 17:54:16 +00:00
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
Web UI disconnects (#5276) * Use fake clock consistently in units tests. * Split web session management into two interfaces and implement them separately for clear separation * Split session management into New/Validate to make it aparent where the sessions are created and where existing sessions are managed. Remove ttlmap in favor of a simple map and handle expirations explicitly. Add web session management to gRPC server for the cache. * Reintroduce web sessions APIs under a getter interface. * Add SubKind to WatchKind for gRPC and add conversions from/to protobuf. Fix web sessions unit tests. * lib/web: create/insert session context in ValidateSession if the session has not yet been added to session cache. lib/cache: add event filter for web session in auth cache. lib/auth: propagate web session subkind in gRPC event. * Add implicit migrations for legacy web session key path for queries. * Integrate web token in lib/web * Add a bearer token when upserting a web session * Fix tests. Use fake clock wherever possible. * Converge session cache handling in lib/web * Clean up and add doc comments where necessary * Use correct form of sessions/tokens controller for ServerWithRoles. Use fake time in web tests * Converge the web sessions/tokens handling in lib/auth to match the old behavior w.r.t access checking (e.g. implicit handling of the local user identity). * Use cached reads and waiters only when necessary. Query sessions/tokens using best-effort - first looking in the cache and falling back to a proxy client * Properly propagate events about deletes for values with subkind. * Update to retrofit changes after recent teleport API refactorings * Update comment on removing legacy code to move the deadline to 7.x * Do not close the resources on the session when it expires - this beats the purpose of this PR. Also avoid a race between closing the cached clients and an existing reference to the session by letting the session linger for longer before removing it. * Move web session/token request structs to the api client proto package * Only set HTTP fs on the web handler if the UI is enabled * Properly tear down web session test by releasing resources at the end. Fix the web UI assets configuration by removing DisableUI and instead use the presence of assets (HTTP file system) as an indicator that the web UI has been enabled. * Decrease the expired session cache clean up threshold to 2m. Only log the expiration error message for errors other than not found * Add test for terminal disconnect when using two proxies in HA mode
2021-02-04 15:50:18 +00:00
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/plugin"
2015-10-05 17:36:55 +00:00
"github.com/gravitational/teleport/lib/services"
2015-10-05 14:33:25 +00:00
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/utils"
2015-07-09 03:57:34 +00:00
"github.com/gravitational/form"
2016-01-21 18:18:59 +00:00
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
2016-01-20 15:52:25 +00:00
"github.com/julienschmidt/httprouter"
2015-03-02 20:11:23 +00:00
)
type APIConfig struct {
PluginRegistry plugin.Registry
AuthServer *Server
2016-12-12 00:52:22 +00:00
SessionService session.Service
AuditLog events.IAuditLog
2017-01-31 04:18:15 +00:00
Authorizer Authorizer
Emitter events.Emitter
// KeepAlivePeriod defines period between keep alives
KeepAlivePeriod time.Duration
// KeepAliveCount specifies amount of missed keep alives
// to wait for until declaring connection as broken
KeepAliveCount int
// MetadataGetter retrieves additional metadata about session uploads.
// Will be nil if audit logging is not enabled.
2021-01-04 22:22:55 +00:00
MetadataGetter events.UploadMetadataGetter
}
// CheckAndSetDefaults checks and sets default values
func (a *APIConfig) CheckAndSetDefaults() error {
if a.KeepAlivePeriod == 0 {
a.KeepAlivePeriod = defaults.ServerKeepAliveTTL
}
if a.KeepAliveCount == 0 {
a.KeepAliveCount = defaults.KeepAliveCountMax
}
return nil
}
// APIServer implements http API server for AuthServer interface
type APIServer struct {
2016-12-12 00:52:22 +00:00
APIConfig
httprouter.Router
2017-04-07 23:51:31 +00:00
clockwork.Clock
}
// NewAPIServer returns a new instance of APIServer HTTP handler
func NewAPIServer(config *APIConfig) (http.Handler, error) {
srv := APIServer{
2016-12-13 03:26:59 +00:00
APIConfig: *config,
2017-04-07 23:51:31 +00:00
Clock: clockwork.NewRealClock(),
2015-03-02 20:11:23 +00:00
}
srv.Router = *httprouter.New()
// Kubernetes extensions
srv.POST("/:version/kube/csr", srv.withAuth(srv.processKubeCSR))
// Operations on certificate authorities
2016-12-29 20:23:58 +00:00
srv.GET("/:version/domain", srv.withAuth(srv.getDomainName))
srv.GET("/:version/cacert", srv.withAuth(srv.getClusterCACert))
2016-12-29 20:23:58 +00:00
srv.POST("/:version/authorities/:type", srv.withAuth(srv.upsertCertAuthority))
srv.POST("/:version/authorities/:type/rotate", srv.withAuth(srv.rotateCertAuthority))
srv.POST("/:version/authorities/:type/rotate/external", srv.withAuth(srv.rotateExternalCertAuthority))
2016-12-29 20:23:58 +00:00
srv.DELETE("/:version/authorities/:type/:domain", srv.withAuth(srv.deleteCertAuthority))
srv.GET("/:version/authorities/:type/:domain", srv.withAuth(srv.getCertAuthority))
srv.GET("/:version/authorities/:type", srv.withAuth(srv.getCertAuthorities))
2015-03-02 20:11:23 +00:00
// Generating certificates for user and host authorities
2016-12-29 20:23:58 +00:00
srv.POST("/:version/ca/host/certs", srv.withAuth(srv.generateHostCert))
srv.POST("/:version/ca/user/certs", srv.withAuth(srv.generateUserCert)) // DELETE IN: 4.2.0
2015-03-02 20:11:23 +00:00
// Operations on users
2016-12-29 20:23:58 +00:00
srv.GET("/:version/users", srv.withAuth(srv.getUsers))
srv.GET("/:version/users/:user", srv.withAuth(srv.getUser))
srv.DELETE("/:version/users/:user", srv.withAuth(srv.deleteUser)) // DELETE IN: 5.2 REST method is replaced by grpc method with context.
2015-03-02 20:11:23 +00:00
// Generating keypairs
2016-12-29 20:23:58 +00:00
srv.POST("/:version/keypair", srv.withAuth(srv.generateKeyPair))
// Passwords and sessions
2016-12-29 20:23:58 +00:00
srv.POST("/:version/users", srv.withAuth(srv.upsertUser))
2017-10-13 21:02:40 +00:00
srv.PUT("/:version/users/:user/web/password", srv.withAuth(srv.changePassword))
2016-12-29 20:23:58 +00:00
srv.POST("/:version/users/:user/web/password", srv.withAuth(srv.upsertPassword))
srv.POST("/:version/users/:user/web/password/check", srv.withRate(srv.withAuth(srv.checkPassword)))
srv.POST("/:version/users/:user/web/sessions", srv.withAuth(srv.createWebSession))
srv.POST("/:version/users/:user/web/authenticate", srv.withAuth(srv.authenticateWebUser))
srv.POST("/:version/users/:user/ssh/authenticate", srv.withAuth(srv.authenticateSSHUser))
2016-12-29 20:23:58 +00:00
srv.GET("/:version/users/:user/web/sessions/:sid", srv.withAuth(srv.getWebSession))
srv.DELETE("/:version/users/:user/web/sessions/:sid", srv.withAuth(srv.deleteWebSession))
Merge Teleport V4.3 UI branch to master (#3583) * Add monorepo * Add reset/passwd capability for local users (#3287) * Add UserTokens to allow password resets * Pass context down through ChangePasswordWithToken * Rename UserToken to ResetPasswordToken * Add auto formatting for proto files * Add common Marshaller interfaces to reset password token * Allow enterprise "tctl" reuse OSS user methods (#3344) * Pass localAuthEnabled flag to UI (#3412) * Added LocalAuthEnabled prop to WebConfigAuthSetting struct in webconfig.go * Added LocalAuthEnabled state as part of webCfg in apiserver.go * update e-refs * Fix a regression bug after merge * Update tctl CLI output msgs (#3442) * Use local user client when resolving user roles * Update webapps ref * Add and retrieve fields from Cluster struct (#3476) * Set Teleport versions for node, auth, proxy init heartbeat * Add and retrieve fields NodeCount, PublicURL, AuthVersion from Clusters * Remove debug logging to avoid log pollution when getting public_addr of proxy * Create helper func GuessProxyHost to get the public_addr of a proxy host * Refactor newResetPasswordToken to use GuessProxyHost and remove publicUrl func * Remove webapps submodule * Add webassets submodule * Replace webapps sub-module reference with webassets * Update webassets path in Makefile * Update webassets 1b11b26 Simplify and clean up Makefile (#62) https://github.com/gravitational/webapps/commit/1b11b26 * Retrieve cluster details for user context (#3515) * Let GuessProxyHost also return proxy's version * Unit test GuessProxyHostAndVersion & GetClusterDetails * Update webassets 4dfef4e Fix build pipeline (#66) https://github.com/gravitational/webapps/commit/4dfef4e * Update e-ref * Update webassets 0647568 Fix OSS redirects https://github.com/gravitational/webapps/commit/0647568 * update e-ref * Update webassets e0f4189 Address security audit warnings Updates "minimist" package which is used by 7y old "optimist". https://github.com/gravitational/webapps/commit/e0f4189 * Add new attr to Session struct (#3574) * Add fields ServerHostname and ServerAddr * Set these fields on newSession * Ensure webassets submodule during build * Update e-ref * Ensure webassets before running unit-tests * Update E-ref Co-authored-by: Lisa Kim <lisa@gravitational.com> Co-authored-by: Pierre Beaucamp <pierre@gravitational.com> Co-authored-by: Jenkins <jenkins@gravitational.io>
2020-04-15 19:35:26 +00:00
srv.POST("/:version/web/password/token", srv.withRate(srv.withAuth(srv.changePasswordWithToken)))
2015-07-11 00:24:44 +00:00
// Servers and presence heartbeat
2016-12-29 20:23:58 +00:00
srv.POST("/:version/namespaces/:namespace/nodes", srv.withAuth(srv.upsertNode))
Events and GRPC API This commit introduces several key changes to Teleport backend and API infrastructure in order to achieve scalability improvements on 10K+ node deployments. Events and plain keyspace -------------------------- New backend interface supports events, pagination and range queries and moves away from buckets to plain keyspace, what better aligns with DynamoDB and Etcd featuring similar interfaces. All backend implementations are exposing Events API, allowing multiple subscribers to consume the same event stream and avoid polling database. Replacing BoltDB, Dir with SQLite ------------------------------- BoltDB backend does not support having two processes access the database at the same time. This prevented Teleport using BoltDB backend to be live reloaded. SQLite supports reads/writes by multiple processes and makes Dir backend obsolete as SQLite is more efficient on larger collections, supports transactions and can detect data corruption. Teleport automatically migrates data from Bolt and Dir backends into SQLite. GRPC API and protobuf resources ------------------------------- GRPC API has been introduced for the auth server. The auth server now serves both GRPC and JSON-HTTP API on the same TLS socket and uses the same client certificate authentication. All future API methods should use GRPC and HTTP-JSON API is considered obsolete. In addition to that some resources like Server and CertificateAuthority are now generated from protobuf service specifications in a way that is fully backward compatible with original JSON spec and schema, so the same resource can be encoded and decoded from JSON, YAML and protobuf. All models should be refactored into new proto specification over time. Streaming presence service -------------------------- In order to cut bandwidth, nodes are sending full updates only when changes to labels or spec have occured, otherwise new light-weight GRPC keep alive updates are sent over to the presence service, reducing bandwidth usage on multi-node deployments. In addition to that nodes are no longer polling auth server for certificate authority rotation updates, instead they subscribe to event updates to detect updates as soon as they happen. This is a new API, so the errors are inevitable, that's why polling is still done, but on a way slower rate.
2018-11-07 23:33:38 +00:00
srv.POST("/:version/namespaces/:namespace/nodes/keepalive", srv.withAuth(srv.keepAliveNode))
srv.PUT("/:version/namespaces/:namespace/nodes", srv.withAuth(srv.upsertNodes))
2016-12-29 20:23:58 +00:00
srv.GET("/:version/namespaces/:namespace/nodes", srv.withAuth(srv.getNodes))
srv.DELETE("/:version/namespaces/:namespace/nodes", srv.withAuth(srv.deleteAllNodes))
srv.DELETE("/:version/namespaces/:namespace/nodes/:name", srv.withAuth(srv.deleteNode))
2016-12-29 20:23:58 +00:00
srv.POST("/:version/authservers", srv.withAuth(srv.upsertAuthServer))
srv.GET("/:version/authservers", srv.withAuth(srv.getAuthServers))
srv.POST("/:version/proxies", srv.withAuth(srv.upsertProxy))
srv.GET("/:version/proxies", srv.withAuth(srv.getProxies))
srv.DELETE("/:version/proxies", srv.withAuth(srv.deleteAllProxies))
srv.DELETE("/:version/proxies/:name", srv.withAuth(srv.deleteProxy))
2017-10-06 00:29:31 +00:00
srv.POST("/:version/tunnelconnections", srv.withAuth(srv.upsertTunnelConnection))
srv.GET("/:version/tunnelconnections/:cluster", srv.withAuth(srv.getTunnelConnections))
srv.GET("/:version/tunnelconnections", srv.withAuth(srv.getAllTunnelConnections))
2017-10-12 23:51:18 +00:00
srv.DELETE("/:version/tunnelconnections/:cluster/:conn", srv.withAuth(srv.deleteTunnelConnection))
2017-10-06 00:29:31 +00:00
srv.DELETE("/:version/tunnelconnections/:cluster", srv.withAuth(srv.deleteTunnelConnections))
srv.DELETE("/:version/tunnelconnections", srv.withAuth(srv.deleteAllTunnelConnections))
2015-03-02 20:11:23 +00:00
// Server Credentials
srv.POST("/:version/server/credentials", srv.withAuth(srv.generateServerKeys))
srv.POST("/:version/remoteclusters", srv.withAuth(srv.createRemoteCluster))
srv.GET("/:version/remoteclusters/:cluster", srv.withAuth(srv.getRemoteCluster))
srv.GET("/:version/remoteclusters", srv.withAuth(srv.getRemoteClusters))
srv.DELETE("/:version/remoteclusters/:cluster", srv.withAuth(srv.deleteRemoteCluster))
srv.DELETE("/:version/remoteclusters", srv.withAuth(srv.deleteAllRemoteClusters))
// Reverse tunnels
2016-12-29 20:23:58 +00:00
srv.POST("/:version/reversetunnels", srv.withAuth(srv.upsertReverseTunnel))
srv.GET("/:version/reversetunnels", srv.withAuth(srv.getReverseTunnels))
srv.DELETE("/:version/reversetunnels/:domain", srv.withAuth(srv.deleteReverseTunnel))
2017-03-02 19:50:35 +00:00
// trusted clusters
srv.POST("/:version/trustedclusters", srv.withAuth(srv.upsertTrustedCluster))
srv.POST("/:version/trustedclusters/validate", srv.withAuth(srv.validateTrustedCluster))
srv.GET("/:version/trustedclusters", srv.withAuth(srv.getTrustedClusters))
srv.GET("/:version/trustedclusters/:name", srv.withAuth(srv.getTrustedCluster))
srv.DELETE("/:version/trustedclusters/:name", srv.withAuth(srv.deleteTrustedCluster))
// Tokens
2016-12-29 20:23:58 +00:00
srv.POST("/:version/tokens", srv.withAuth(srv.generateToken))
srv.POST("/:version/tokens/register", srv.withAuth(srv.registerUsingToken))
srv.POST("/:version/tokens/register/auth", srv.withAuth(srv.registerNewAuthServer))
2017-08-24 00:31:07 +00:00
// active sesssions
2016-12-29 20:23:58 +00:00
srv.POST("/:version/namespaces/:namespace/sessions", srv.withAuth(srv.createSession))
srv.PUT("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.updateSession))
srv.DELETE("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.deleteSession))
2016-12-29 20:23:58 +00:00
srv.GET("/:version/namespaces/:namespace/sessions", srv.withAuth(srv.getSessions))
srv.GET("/:version/namespaces/:namespace/sessions/:id", srv.withAuth(srv.getSession))
2017-05-27 01:03:19 +00:00
srv.POST("/:version/namespaces/:namespace/sessions/:id/slice", srv.withAuth(srv.postSessionSlice))
srv.POST("/:version/namespaces/:namespace/sessions/:id/recording", srv.withAuth(srv.uploadSessionRecording))
2016-12-29 20:23:58 +00:00
srv.GET("/:version/namespaces/:namespace/sessions/:id/stream", srv.withAuth(srv.getSessionChunk))
srv.GET("/:version/namespaces/:namespace/sessions/:id/events", srv.withAuth(srv.getSessionEvents))
2015-06-30 23:12:18 +00:00
2016-12-13 03:26:59 +00:00
// Namespaces
2016-12-29 20:23:58 +00:00
srv.POST("/:version/namespaces", srv.withAuth(srv.upsertNamespace))
srv.GET("/:version/namespaces", srv.withAuth(srv.getNamespaces))
srv.GET("/:version/namespaces/:namespace", srv.withAuth(srv.getNamespace))
srv.DELETE("/:version/namespaces/:namespace", srv.withAuth(srv.deleteNamespace))
2016-12-13 03:26:59 +00:00
// Roles - Moved to grpc
// DELETE IN 7.0
2016-12-29 20:23:58 +00:00
srv.POST("/:version/roles", srv.withAuth(srv.upsertRole))
srv.GET("/:version/roles", srv.withAuth(srv.getRoles))
srv.GET("/:version/roles/:role", srv.withAuth(srv.getRole))
srv.DELETE("/:version/roles/:role", srv.withAuth(srv.deleteRole))
2016-12-13 03:26:59 +00:00
2017-07-28 18:37:12 +00:00
// cluster configuration
srv.GET("/:version/configuration", srv.withAuth(srv.getClusterConfig))
srv.POST("/:version/configuration", srv.withAuth(srv.setClusterConfig))
2017-07-28 18:37:12 +00:00
srv.GET("/:version/configuration/name", srv.withAuth(srv.getClusterName))
srv.POST("/:version/configuration/name", srv.withAuth(srv.setClusterName))
srv.GET("/:version/configuration/static_tokens", srv.withAuth(srv.getStaticTokens))
srv.DELETE("/:version/configuration/static_tokens", srv.withAuth(srv.deleteStaticTokens))
2017-07-28 18:37:12 +00:00
srv.POST("/:version/configuration/static_tokens", srv.withAuth(srv.setStaticTokens))
srv.GET("/:version/authentication/preference", srv.withAuth(srv.getClusterAuthPreference))
srv.POST("/:version/authentication/preference", srv.withAuth(srv.setClusterAuthPreference))
// OIDC
srv.POST("/:version/oidc/connectors", srv.withAuth(srv.upsertOIDCConnector))
srv.GET("/:version/oidc/connectors", srv.withAuth(srv.getOIDCConnectors))
srv.GET("/:version/oidc/connectors/:id", srv.withAuth(srv.getOIDCConnector))
srv.DELETE("/:version/oidc/connectors/:id", srv.withAuth(srv.deleteOIDCConnector))
srv.POST("/:version/oidc/requests/create", srv.withAuth(srv.createOIDCAuthRequest))
srv.POST("/:version/oidc/requests/validate", srv.withAuth(srv.validateOIDCAuthCallback))
2017-05-05 22:53:05 +00:00
// SAML handlers
srv.POST("/:version/saml/connectors", srv.withAuth(srv.createSAMLConnector))
srv.PUT("/:version/saml/connectors", srv.withAuth(srv.upsertSAMLConnector))
srv.GET("/:version/saml/connectors", srv.withAuth(srv.getSAMLConnectors))
srv.GET("/:version/saml/connectors/:id", srv.withAuth(srv.getSAMLConnector))
srv.DELETE("/:version/saml/connectors/:id", srv.withAuth(srv.deleteSAMLConnector))
srv.POST("/:version/saml/requests/create", srv.withAuth(srv.createSAMLAuthRequest))
srv.POST("/:version/saml/requests/validate", srv.withAuth(srv.validateSAMLResponse))
2017-12-14 21:41:38 +00:00
// Github connector
srv.POST("/:version/github/connectors", srv.withAuth(srv.createGithubConnector))
srv.PUT("/:version/github/connectors", srv.withAuth(srv.upsertGithubConnector))
srv.GET("/:version/github/connectors", srv.withAuth(srv.getGithubConnectors))
srv.GET("/:version/github/connectors/:id", srv.withAuth(srv.getGithubConnector))
srv.DELETE("/:version/github/connectors/:id", srv.withAuth(srv.deleteGithubConnector))
srv.POST("/:version/github/requests/create", srv.withAuth(srv.createGithubAuthRequest))
srv.POST("/:version/github/requests/validate", srv.withAuth(srv.validateGithubAuthCallback))
// U2F
2016-12-29 20:23:58 +00:00
srv.GET("/:version/u2f/signuptokens/:token", srv.withAuth(srv.getSignupU2FRegisterRequest))
srv.POST("/:version/u2f/users/:user/sign", srv.withAuth(srv.u2fSignRequest))
srv.GET("/:version/u2f/appid", srv.withAuth(srv.getU2FAppID))
// Provisioning tokens- Moved to grpc
// DELETE IN 8.0
2016-12-29 20:23:58 +00:00
srv.GET("/:version/tokens", srv.withAuth(srv.getTokens))
srv.GET("/:version/tokens/:token", srv.withAuth(srv.getToken))
srv.DELETE("/:version/tokens/:token", srv.withAuth(srv.deleteToken))
// Audit logs AKA events
2016-12-29 20:23:58 +00:00
srv.POST("/:version/events", srv.withAuth(srv.emitAuditEvent))
srv.GET("/:version/events", srv.withAuth(srv.searchEvents))
2017-08-24 00:31:07 +00:00
srv.GET("/:version/events/session", srv.withAuth(srv.searchSessionEvents))
if config.PluginRegistry != nil {
if err := config.PluginRegistry.RegisterAuthWebHandlers(&srv); err != nil {
return nil, trace.Wrap(err)
}
}
return httplib.RewritePaths(&srv.Router,
httplib.Rewrite("/v1/nodes", "/v1/namespaces/default/nodes"),
httplib.Rewrite("/v1/sessions", "/v1/namespaces/default/sessions"),
httplib.Rewrite("/v1/sessions/([^/]+)/(.*)", "/v1/namespaces/default/sessions/$1/$2"),
), nil
2015-03-02 20:11:23 +00:00
}
2016-12-12 00:52:22 +00:00
// HandlerWithAuthFunc is http handler with passed auth context
2016-12-29 23:46:19 +00:00
type HandlerWithAuthFunc func(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error)
2016-12-12 00:52:22 +00:00
2016-12-13 03:26:59 +00:00
func (s *APIServer) withAuth(handler HandlerWithAuthFunc) httprouter.Handle {
2017-01-29 22:43:08 +00:00
const accessDeniedMsg = "auth API: access denied "
2016-12-12 00:52:22 +00:00
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
Web UI disconnects (#5276) * Use fake clock consistently in units tests. * Split web session management into two interfaces and implement them separately for clear separation * Split session management into New/Validate to make it aparent where the sessions are created and where existing sessions are managed. Remove ttlmap in favor of a simple map and handle expirations explicitly. Add web session management to gRPC server for the cache. * Reintroduce web sessions APIs under a getter interface. * Add SubKind to WatchKind for gRPC and add conversions from/to protobuf. Fix web sessions unit tests. * lib/web: create/insert session context in ValidateSession if the session has not yet been added to session cache. lib/cache: add event filter for web session in auth cache. lib/auth: propagate web session subkind in gRPC event. * Add implicit migrations for legacy web session key path for queries. * Integrate web token in lib/web * Add a bearer token when upserting a web session * Fix tests. Use fake clock wherever possible. * Converge session cache handling in lib/web * Clean up and add doc comments where necessary * Use correct form of sessions/tokens controller for ServerWithRoles. Use fake time in web tests * Converge the web sessions/tokens handling in lib/auth to match the old behavior w.r.t access checking (e.g. implicit handling of the local user identity). * Use cached reads and waiters only when necessary. Query sessions/tokens using best-effort - first looking in the cache and falling back to a proxy client * Properly propagate events about deletes for values with subkind. * Update to retrofit changes after recent teleport API refactorings * Update comment on removing legacy code to move the deadline to 7.x * Do not close the resources on the session when it expires - this beats the purpose of this PR. Also avoid a race between closing the cached clients and an existing reference to the session by letting the session linger for longer before removing it. * Move web session/token request structs to the api client proto package * Only set HTTP fs on the web handler if the UI is enabled * Properly tear down web session test by releasing resources at the end. Fix the web UI assets configuration by removing DisableUI and instead use the presence of assets (HTTP file system) as an indicator that the web UI has been enabled. * Decrease the expired session cache clean up threshold to 2m. Only log the expiration error message for errors other than not found * Add test for terminal disconnect when using two proxies in HA mode
2021-02-04 15:50:18 +00:00
// HTTPS server expects auth context to be set by the auth middleware
2017-01-31 05:43:01 +00:00
authContext, err := s.Authorizer.Authorize(r.Context())
2016-12-12 00:52:22 +00:00
if err != nil {
// propagate connection problem error so we can differentiate
// between connection failed and access denied
if trace.IsConnectionProblem(err) {
return nil, trace.ConnectionProblem(err, "[07] failed to connect to the database")
} else if trace.IsAccessDenied(err) {
// don't print stack trace, just log the warning
log.Warn(err)
} else {
log.Warn(trace.DebugReport(err))
}
2017-01-31 05:43:01 +00:00
return nil, trace.AccessDenied(accessDeniedMsg + "[00]")
2016-12-12 00:52:22 +00:00
}
auth := &ServerWithRoles{
2016-12-12 00:52:22 +00:00
authServer: s.AuthServer,
context: *authContext,
2016-12-12 00:52:22 +00:00
sessions: s.SessionService,
alog: s.AuthServer.IAuditLog,
2016-12-12 00:52:22 +00:00
}
2016-12-29 23:46:19 +00:00
version := p.ByName("version")
if version == "" {
return nil, trace.BadParameter("missing version")
}
return handler(auth, w, r, p, version)
2016-12-12 00:52:22 +00:00
})
}
// withRate wrap a rate limiter around the passed in httprouter.Handle and
// returns a httprouter.Handle. Because the rate limiter wraps a http.Handler,
// internally withRate converts to the standard handler and back.
func (s *APIServer) withRate(handle httprouter.Handle) httprouter.Handle {
limiter := defaults.CheckPasswordLimiter()
fromStandard := func(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
ctx := context.WithValue(r.Context(), contextParams, p)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
}
}
toStandard := func(handle httprouter.Handle) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p, ok := r.Context().Value(contextParams).(httprouter.Params)
if !ok {
trace.WriteError(w, trace.BadParameter("parameters missing from request"))
return
}
handle(w, r, p)
})
}
limiter.WrapHandle(toStandard(handle))
return fromStandard(limiter)
}
2016-12-29 19:36:57 +00:00
type upsertServerRawReq struct {
Server json.RawMessage `json:"server"`
TTL time.Duration `json:"ttl"`
}
// upsertServer is a common utility function
Kubernetes request routing and cluster registration (#4670) This change has several parts: cluster registration, cache updates, routing and a new tctl flag. > cluster registration Cluster registration means adding `KubernetesClusters` to `ServerSpec` for servers with `KindKubeService`. `kubernetes_service` instances will parse their kubeconfig or local `kube_cluster_name` and add them to their `ServerSpec` sent to the auth server. They are effectively declaring that "I can serve k8s requests for k8s cluster X". > cache updates This is just cache plumbing for `kubernetes_service` presence, so that other teleport processes can fetch all of kube services. It was missed in the previous PR implementing CRUD for `kubernetes_service`. > routing Now the fun part - routing logic. This logic lives in `/lib/kube/proxy/forwarder.go` and is shared by both `proxy_service` (with kubernetes integration enabled) and `kubernetes_service`. The target k8s cluster name is passed in the client cert, along with k8s users/groups information. `kubernetes_service` only serves requests for its direct k8s cluster (from `Forwarder.creds`) and doesn't route requests to other teleport instances. `proxy_service` can serve requests: - directly to a k8s cluster (the way it works pre-5.0) - to a leaf teleport cluster (also same as pre-5.0, based on `RouteToCluster` field in the client cert) - to a `kubernetes_service` (directly or over a tunnel) The last two modes require the proxy to generate an ephemeral client TLS cert to do an outbound mTLS connection. > tctl flag A flag `--kube-cluster-name` for `tctl auth sign --format=kubernetes` which allows generating client certs for non-default k8s cluster name (as long as it's registered in a cluster). I used this for testing, but it could be used for automation too.
2020-11-09 19:40:02 +00:00
func (s *APIServer) upsertServer(auth services.Presence, role teleport.Role, r *http.Request, p httprouter.Params) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req upsertServerRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 19:36:57 +00:00
var kind string
switch role {
case teleport.RoleNode:
kind = services.KindNode
case teleport.RoleAuth:
kind = services.KindAuthServer
case teleport.RoleProxy:
kind = services.KindProxy
Kubernetes request routing and cluster registration (#4670) This change has several parts: cluster registration, cache updates, routing and a new tctl flag. > cluster registration Cluster registration means adding `KubernetesClusters` to `ServerSpec` for servers with `KindKubeService`. `kubernetes_service` instances will parse their kubeconfig or local `kube_cluster_name` and add them to their `ServerSpec` sent to the auth server. They are effectively declaring that "I can serve k8s requests for k8s cluster X". > cache updates This is just cache plumbing for `kubernetes_service` presence, so that other teleport processes can fetch all of kube services. It was missed in the previous PR implementing CRUD for `kubernetes_service`. > routing Now the fun part - routing logic. This logic lives in `/lib/kube/proxy/forwarder.go` and is shared by both `proxy_service` (with kubernetes integration enabled) and `kubernetes_service`. The target k8s cluster name is passed in the client cert, along with k8s users/groups information. `kubernetes_service` only serves requests for its direct k8s cluster (from `Forwarder.creds`) and doesn't route requests to other teleport instances. `proxy_service` can serve requests: - directly to a k8s cluster (the way it works pre-5.0) - to a leaf teleport cluster (also same as pre-5.0, based on `RouteToCluster` field in the client cert) - to a `kubernetes_service` (directly or over a tunnel) The last two modes require the proxy to generate an ephemeral client TLS cert to do an outbound mTLS connection. > tctl flag A flag `--kube-cluster-name` for `tctl auth sign --format=kubernetes` which allows generating client certs for non-default k8s cluster name (as long as it's registered in a cluster). I used this for testing, but it could be used for automation too.
2020-11-09 19:40:02 +00:00
default:
return nil, trace.BadParameter("upsertServer with unknown role: %q", role)
2016-12-29 19:36:57 +00:00
}
server, err := services.UnmarshalServer(req.Server, kind)
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-03-02 19:10:07 +00:00
// if server sent "local" IP address to us, replace the ip/host part with the remote address we see
// on the socket, but keep the original port:
2016-12-29 19:36:57 +00:00
server.SetAddr(utils.ReplaceLocalhost(server.GetAddr(), r.RemoteAddr))
2017-04-07 23:51:31 +00:00
if req.TTL != 0 {
server.SetExpiry(s.Now().UTC().Add(req.TTL))
2017-04-07 23:51:31 +00:00
}
switch role {
case teleport.RoleNode:
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
server.SetNamespace(namespace)
2021-04-29 01:27:12 +00:00
handle, err := auth.UpsertNode(r.Context(), server)
Events and GRPC API This commit introduces several key changes to Teleport backend and API infrastructure in order to achieve scalability improvements on 10K+ node deployments. Events and plain keyspace -------------------------- New backend interface supports events, pagination and range queries and moves away from buckets to plain keyspace, what better aligns with DynamoDB and Etcd featuring similar interfaces. All backend implementations are exposing Events API, allowing multiple subscribers to consume the same event stream and avoid polling database. Replacing BoltDB, Dir with SQLite ------------------------------- BoltDB backend does not support having two processes access the database at the same time. This prevented Teleport using BoltDB backend to be live reloaded. SQLite supports reads/writes by multiple processes and makes Dir backend obsolete as SQLite is more efficient on larger collections, supports transactions and can detect data corruption. Teleport automatically migrates data from Bolt and Dir backends into SQLite. GRPC API and protobuf resources ------------------------------- GRPC API has been introduced for the auth server. The auth server now serves both GRPC and JSON-HTTP API on the same TLS socket and uses the same client certificate authentication. All future API methods should use GRPC and HTTP-JSON API is considered obsolete. In addition to that some resources like Server and CertificateAuthority are now generated from protobuf service specifications in a way that is fully backward compatible with original JSON spec and schema, so the same resource can be encoded and decoded from JSON, YAML and protobuf. All models should be refactored into new proto specification over time. Streaming presence service -------------------------- In order to cut bandwidth, nodes are sending full updates only when changes to labels or spec have occured, otherwise new light-weight GRPC keep alive updates are sent over to the presence service, reducing bandwidth usage on multi-node deployments. In addition to that nodes are no longer polling auth server for certificate authority rotation updates, instead they subscribe to event updates to detect updates as soon as they happen. This is a new API, so the errors are inevitable, that's why polling is still done, but on a way slower rate.
2018-11-07 23:33:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
Events and GRPC API This commit introduces several key changes to Teleport backend and API infrastructure in order to achieve scalability improvements on 10K+ node deployments. Events and plain keyspace -------------------------- New backend interface supports events, pagination and range queries and moves away from buckets to plain keyspace, what better aligns with DynamoDB and Etcd featuring similar interfaces. All backend implementations are exposing Events API, allowing multiple subscribers to consume the same event stream and avoid polling database. Replacing BoltDB, Dir with SQLite ------------------------------- BoltDB backend does not support having two processes access the database at the same time. This prevented Teleport using BoltDB backend to be live reloaded. SQLite supports reads/writes by multiple processes and makes Dir backend obsolete as SQLite is more efficient on larger collections, supports transactions and can detect data corruption. Teleport automatically migrates data from Bolt and Dir backends into SQLite. GRPC API and protobuf resources ------------------------------- GRPC API has been introduced for the auth server. The auth server now serves both GRPC and JSON-HTTP API on the same TLS socket and uses the same client certificate authentication. All future API methods should use GRPC and HTTP-JSON API is considered obsolete. In addition to that some resources like Server and CertificateAuthority are now generated from protobuf service specifications in a way that is fully backward compatible with original JSON spec and schema, so the same resource can be encoded and decoded from JSON, YAML and protobuf. All models should be refactored into new proto specification over time. Streaming presence service -------------------------- In order to cut bandwidth, nodes are sending full updates only when changes to labels or spec have occured, otherwise new light-weight GRPC keep alive updates are sent over to the presence service, reducing bandwidth usage on multi-node deployments. In addition to that nodes are no longer polling auth server for certificate authority rotation updates, instead they subscribe to event updates to detect updates as soon as they happen. This is a new API, so the errors are inevitable, that's why polling is still done, but on a way slower rate.
2018-11-07 23:33:38 +00:00
return handle, nil
case teleport.RoleAuth:
2017-04-07 23:51:31 +00:00
if err := auth.UpsertAuthServer(server); err != nil {
return nil, trace.Wrap(err)
}
case teleport.RoleProxy:
2017-04-07 23:51:31 +00:00
if err := auth.UpsertProxy(server); err != nil {
return nil, trace.Wrap(err)
}
default:
return nil, trace.BadParameter("unknown server role %q", role)
}
return message("ok"), nil
}
Events and GRPC API This commit introduces several key changes to Teleport backend and API infrastructure in order to achieve scalability improvements on 10K+ node deployments. Events and plain keyspace -------------------------- New backend interface supports events, pagination and range queries and moves away from buckets to plain keyspace, what better aligns with DynamoDB and Etcd featuring similar interfaces. All backend implementations are exposing Events API, allowing multiple subscribers to consume the same event stream and avoid polling database. Replacing BoltDB, Dir with SQLite ------------------------------- BoltDB backend does not support having two processes access the database at the same time. This prevented Teleport using BoltDB backend to be live reloaded. SQLite supports reads/writes by multiple processes and makes Dir backend obsolete as SQLite is more efficient on larger collections, supports transactions and can detect data corruption. Teleport automatically migrates data from Bolt and Dir backends into SQLite. GRPC API and protobuf resources ------------------------------- GRPC API has been introduced for the auth server. The auth server now serves both GRPC and JSON-HTTP API on the same TLS socket and uses the same client certificate authentication. All future API methods should use GRPC and HTTP-JSON API is considered obsolete. In addition to that some resources like Server and CertificateAuthority are now generated from protobuf service specifications in a way that is fully backward compatible with original JSON spec and schema, so the same resource can be encoded and decoded from JSON, YAML and protobuf. All models should be refactored into new proto specification over time. Streaming presence service -------------------------- In order to cut bandwidth, nodes are sending full updates only when changes to labels or spec have occured, otherwise new light-weight GRPC keep alive updates are sent over to the presence service, reducing bandwidth usage on multi-node deployments. In addition to that nodes are no longer polling auth server for certificate authority rotation updates, instead they subscribe to event updates to detect updates as soon as they happen. This is a new API, so the errors are inevitable, that's why polling is still done, but on a way slower rate.
2018-11-07 23:33:38 +00:00
// keepAliveNode updates node TTL in the backend
func (s *APIServer) keepAliveNode(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var handle services.KeepAlive
if err := httplib.ReadJSON(r, &handle); err != nil {
return nil, trace.Wrap(err)
}
if err := auth.KeepAliveServer(r.Context(), handle); err != nil {
Events and GRPC API This commit introduces several key changes to Teleport backend and API infrastructure in order to achieve scalability improvements on 10K+ node deployments. Events and plain keyspace -------------------------- New backend interface supports events, pagination and range queries and moves away from buckets to plain keyspace, what better aligns with DynamoDB and Etcd featuring similar interfaces. All backend implementations are exposing Events API, allowing multiple subscribers to consume the same event stream and avoid polling database. Replacing BoltDB, Dir with SQLite ------------------------------- BoltDB backend does not support having two processes access the database at the same time. This prevented Teleport using BoltDB backend to be live reloaded. SQLite supports reads/writes by multiple processes and makes Dir backend obsolete as SQLite is more efficient on larger collections, supports transactions and can detect data corruption. Teleport automatically migrates data from Bolt and Dir backends into SQLite. GRPC API and protobuf resources ------------------------------- GRPC API has been introduced for the auth server. The auth server now serves both GRPC and JSON-HTTP API on the same TLS socket and uses the same client certificate authentication. All future API methods should use GRPC and HTTP-JSON API is considered obsolete. In addition to that some resources like Server and CertificateAuthority are now generated from protobuf service specifications in a way that is fully backward compatible with original JSON spec and schema, so the same resource can be encoded and decoded from JSON, YAML and protobuf. All models should be refactored into new proto specification over time. Streaming presence service -------------------------- In order to cut bandwidth, nodes are sending full updates only when changes to labels or spec have occured, otherwise new light-weight GRPC keep alive updates are sent over to the presence service, reducing bandwidth usage on multi-node deployments. In addition to that nodes are no longer polling auth server for certificate authority rotation updates, instead they subscribe to event updates to detect updates as soon as they happen. This is a new API, so the errors are inevitable, that's why polling is still done, but on a way slower rate.
2018-11-07 23:33:38 +00:00
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
type upsertNodesReq struct {
Nodes json.RawMessage `json:"nodes"`
Namespace string `json:"namespace"`
}
// upsertNodes is used to bulk insert nodes into the backend.
func (s *APIServer) upsertNodes(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req upsertNodesReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2021-04-29 01:27:12 +00:00
if !services.IsValidNamespace(req.Namespace) {
return nil, trace.BadParameter("invalid namespace %q", req.Namespace)
}
nodes, err := services.UnmarshalServers(req.Nodes)
if err != nil {
return nil, trace.Wrap(err)
}
err = auth.UpsertNodes(req.Namespace, nodes)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// upsertNode is called by remote SSH nodes when they ping back into the auth service
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertNode(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
Kubernetes request routing and cluster registration (#4670) This change has several parts: cluster registration, cache updates, routing and a new tctl flag. > cluster registration Cluster registration means adding `KubernetesClusters` to `ServerSpec` for servers with `KindKubeService`. `kubernetes_service` instances will parse their kubeconfig or local `kube_cluster_name` and add them to their `ServerSpec` sent to the auth server. They are effectively declaring that "I can serve k8s requests for k8s cluster X". > cache updates This is just cache plumbing for `kubernetes_service` presence, so that other teleport processes can fetch all of kube services. It was missed in the previous PR implementing CRUD for `kubernetes_service`. > routing Now the fun part - routing logic. This logic lives in `/lib/kube/proxy/forwarder.go` and is shared by both `proxy_service` (with kubernetes integration enabled) and `kubernetes_service`. The target k8s cluster name is passed in the client cert, along with k8s users/groups information. `kubernetes_service` only serves requests for its direct k8s cluster (from `Forwarder.creds`) and doesn't route requests to other teleport instances. `proxy_service` can serve requests: - directly to a k8s cluster (the way it works pre-5.0) - to a leaf teleport cluster (also same as pre-5.0, based on `RouteToCluster` field in the client cert) - to a `kubernetes_service` (directly or over a tunnel) The last two modes require the proxy to generate an ephemeral client TLS cert to do an outbound mTLS connection. > tctl flag A flag `--kube-cluster-name` for `tctl auth sign --format=kubernetes` which allows generating client certs for non-default k8s cluster name (as long as it's registered in a cluster). I used this for testing, but it could be used for automation too.
2020-11-09 19:40:02 +00:00
return s.upsertServer(auth, teleport.RoleNode, r, p)
}
// getNodes returns registered SSH nodes
2016-12-29 23:46:19 +00:00
func (s *APIServer) getNodes(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
Remove JSON schema validation (#6685) * Remove JSON schema validation Removing JSON schema validation from all resource unmarshalers. --- what JSON schema gets us Looking at the JSON schema spec and our usage, here are the supposed benefits: - type validation - make sure incoming data uses the right types for the right fields - required fields - make sure that mandatory fields are set - defaulting - set defaults for fields - documentation - schema definition for our API objects Note that it does _not_ do: - fail on unknown fields in data - fail on a required field with an empty value --- what replaces it Based on the above, it may seem like JSON schema provides value. But it's not the case, let's break it down one by one: - type validation - unmarshaling JSON into a typed Go struct does this - required fields - only checks that the field was provided, doesn't actually check that a value is set (e.g. `"name": ""` will pass the `required` check) - so it's pretty useless for any real validation - and we already have a separate place for proper validation - `CheckAndSetDefaults` methods - defaulting - done in `CheckAndSetDefaults` methods - `Version` is the only annoying field, had to add it in a bunch of objects - documentation - protobuf definitions are the source of truth for our API schema --- the benefits - performance - schema validation does a few rounds of `json.Marshal/Unmarshal` in addition to actual validation; now we simply skip all that - maintenance - no need to keep protobuf and JSON schema definitions in sync anymore - creating new API objects - one error-prone step removed - (future) fewer dependencies - we can _almost_ remove the Go libraries for schema validation (one transient dependency keeping them around) * Remove services.SkipValidation No more JSON schema validation so this option is a noop.
2021-06-01 22:27:20 +00:00
servers, err := auth.GetNodes(r.Context(), namespace)
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 23:46:19 +00:00
return marshalServers(servers, version)
}
// deleteAllNodes deletes all nodes
func (s *APIServer) deleteAllNodes(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
2021-04-29 01:27:12 +00:00
err := auth.DeleteAllNodes(r.Context(), namespace)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// deleteNode deletes node
func (s *APIServer) deleteNode(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
name := p.ByName("name")
if name == "" {
return nil, trace.BadParameter("missing node name")
}
2021-04-29 01:27:12 +00:00
err := auth.DeleteNode(r.Context(), namespace, name)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// upsertProxy is called by remote SSH nodes when they ping back into the auth service
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertProxy(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
Kubernetes request routing and cluster registration (#4670) This change has several parts: cluster registration, cache updates, routing and a new tctl flag. > cluster registration Cluster registration means adding `KubernetesClusters` to `ServerSpec` for servers with `KindKubeService`. `kubernetes_service` instances will parse their kubeconfig or local `kube_cluster_name` and add them to their `ServerSpec` sent to the auth server. They are effectively declaring that "I can serve k8s requests for k8s cluster X". > cache updates This is just cache plumbing for `kubernetes_service` presence, so that other teleport processes can fetch all of kube services. It was missed in the previous PR implementing CRUD for `kubernetes_service`. > routing Now the fun part - routing logic. This logic lives in `/lib/kube/proxy/forwarder.go` and is shared by both `proxy_service` (with kubernetes integration enabled) and `kubernetes_service`. The target k8s cluster name is passed in the client cert, along with k8s users/groups information. `kubernetes_service` only serves requests for its direct k8s cluster (from `Forwarder.creds`) and doesn't route requests to other teleport instances. `proxy_service` can serve requests: - directly to a k8s cluster (the way it works pre-5.0) - to a leaf teleport cluster (also same as pre-5.0, based on `RouteToCluster` field in the client cert) - to a `kubernetes_service` (directly or over a tunnel) The last two modes require the proxy to generate an ephemeral client TLS cert to do an outbound mTLS connection. > tctl flag A flag `--kube-cluster-name` for `tctl auth sign --format=kubernetes` which allows generating client certs for non-default k8s cluster name (as long as it's registered in a cluster). I used this for testing, but it could be used for automation too.
2020-11-09 19:40:02 +00:00
return s.upsertServer(auth, teleport.RoleProxy, r, p)
}
// getProxies returns registered proxies
2016-12-29 23:46:19 +00:00
func (s *APIServer) getProxies(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
servers, err := auth.GetProxies()
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 23:46:19 +00:00
return marshalServers(servers, version)
}
// deleteAllProxies deletes all proxies
func (s *APIServer) deleteAllProxies(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteAllProxies()
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// deleteProxy deletes proxy
func (s *APIServer) deleteProxy(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
name := p.ByName("name")
if name == "" {
return nil, trace.BadParameter("missing proxy name")
}
err := auth.DeleteProxy(name)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// upsertAuthServer is called by remote Auth servers when they ping back into the auth service
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertAuthServer(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
Kubernetes request routing and cluster registration (#4670) This change has several parts: cluster registration, cache updates, routing and a new tctl flag. > cluster registration Cluster registration means adding `KubernetesClusters` to `ServerSpec` for servers with `KindKubeService`. `kubernetes_service` instances will parse their kubeconfig or local `kube_cluster_name` and add them to their `ServerSpec` sent to the auth server. They are effectively declaring that "I can serve k8s requests for k8s cluster X". > cache updates This is just cache plumbing for `kubernetes_service` presence, so that other teleport processes can fetch all of kube services. It was missed in the previous PR implementing CRUD for `kubernetes_service`. > routing Now the fun part - routing logic. This logic lives in `/lib/kube/proxy/forwarder.go` and is shared by both `proxy_service` (with kubernetes integration enabled) and `kubernetes_service`. The target k8s cluster name is passed in the client cert, along with k8s users/groups information. `kubernetes_service` only serves requests for its direct k8s cluster (from `Forwarder.creds`) and doesn't route requests to other teleport instances. `proxy_service` can serve requests: - directly to a k8s cluster (the way it works pre-5.0) - to a leaf teleport cluster (also same as pre-5.0, based on `RouteToCluster` field in the client cert) - to a `kubernetes_service` (directly or over a tunnel) The last two modes require the proxy to generate an ephemeral client TLS cert to do an outbound mTLS connection. > tctl flag A flag `--kube-cluster-name` for `tctl auth sign --format=kubernetes` which allows generating client certs for non-default k8s cluster name (as long as it's registered in a cluster). I used this for testing, but it could be used for automation too.
2020-11-09 19:40:02 +00:00
return s.upsertServer(auth, teleport.RoleAuth, r, p)
}
// getAuthServers returns registered auth servers
2016-12-29 23:46:19 +00:00
func (s *APIServer) getAuthServers(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
servers, err := auth.GetAuthServers()
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 23:46:19 +00:00
return marshalServers(servers, version)
2016-12-29 19:36:57 +00:00
}
2016-12-29 23:46:19 +00:00
func marshalServers(servers []services.Server, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
items := make([]json.RawMessage, len(servers))
for i, server := range servers {
data, err := services.MarshalServer(server, services.WithVersion(version), services.PreserveResourceID())
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
2016-12-29 19:36:57 +00:00
type upsertReverseTunnelRawReq struct {
ReverseTunnel json.RawMessage `json:"reverse_tunnel"`
TTL time.Duration `json:"ttl"`
}
// upsertReverseTunnel is called by admin to create a reverse tunnel to remote proxy
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertReverseTunnel(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req upsertReverseTunnelRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
tun, err := services.UnmarshalReverseTunnel(req.ReverseTunnel)
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateReverseTunnel(tun); err != nil {
return nil, trace.Wrap(err)
}
if req.TTL != 0 {
tun.SetExpiry(s.Now().UTC().Add(req.TTL))
}
2017-04-07 23:51:31 +00:00
if err := auth.UpsertReverseTunnel(tun); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// getReverseTunnels returns a list of reverse tunnels
2016-12-29 23:46:19 +00:00
func (s *APIServer) getReverseTunnels(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
reverseTunnels, err := auth.GetReverseTunnels()
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 19:36:57 +00:00
items := make([]json.RawMessage, len(reverseTunnels))
for i, tunnel := range reverseTunnels {
data, err := services.MarshalReverseTunnel(tunnel, services.WithVersion(version), services.PreserveResourceID())
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
// deleteReverseTunnel deletes reverse tunnel
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteReverseTunnel(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-30 02:57:20 +00:00
domainName := p.ByName("domain")
2016-12-13 03:26:59 +00:00
err := auth.DeleteReverseTunnel(domainName)
if err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("reverse tunnel %v deleted", domainName)), nil
}
2017-03-02 19:50:35 +00:00
type upsertTrustedClusterReq struct {
TrustedCluster json.RawMessage `json:"trusted_cluster"`
}
// upsertTrustedCluster creates or updates a trusted cluster.
2017-03-02 19:50:35 +00:00
func (s *APIServer) upsertTrustedCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *upsertTrustedClusterReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
trustedCluster, err := services.UnmarshalTrustedCluster(req.TrustedCluster)
2017-03-02 19:50:35 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateTrustedCluster(trustedCluster); err != nil {
return nil, trace.Wrap(err)
}
out, err := auth.UpsertTrustedCluster(r.Context(), trustedCluster)
2017-03-02 19:50:35 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalTrustedCluster(out, services.WithVersion(version), services.PreserveResourceID()))
2017-03-02 19:50:35 +00:00
}
func (s *APIServer) validateTrustedCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var validateRequestRaw ValidateTrustedClusterRequestRaw
if err := httplib.ReadJSON(r, &validateRequestRaw); err != nil {
return nil, trace.Wrap(err)
}
validateRequest, err := validateRequestRaw.ToNative()
if err != nil {
return nil, trace.Wrap(err)
}
validateResponse, err := auth.ValidateTrustedCluster(validateRequest)
if err != nil {
return nil, trace.Wrap(err)
}
validateResponseRaw, err := validateResponse.ToRaw()
if err != nil {
return nil, trace.Wrap(err)
}
return validateResponseRaw, nil
}
func (s *APIServer) getTrustedCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
return auth.GetTrustedCluster(r.Context(), p.ByName("name"))
2017-03-02 19:50:35 +00:00
}
func (s *APIServer) getTrustedClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
return auth.GetTrustedClusters(r.Context())
2017-03-02 19:50:35 +00:00
}
// deleteTrustedCluster deletes a trusted cluster by name.
2017-03-02 19:50:35 +00:00
func (s *APIServer) deleteTrustedCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteTrustedCluster(r.Context(), p.ByName("name"))
2017-03-02 19:50:35 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// getTokens returns a list of active provisioning tokens. expired (inactive) tokens are not returned
2016-12-29 23:46:19 +00:00
func (s *APIServer) getTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
tokens, err := auth.GetTokens(r.Context())
if err != nil {
return nil, trace.Wrap(err)
}
return services.ProvisionTokensToV1(tokens), nil
}
// getTokens returns provisioning token by name
2016-12-29 23:46:19 +00:00
func (s *APIServer) getToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
token, err := auth.GetToken(r.Context(), p.ByName("token"))
if err != nil {
return nil, trace.Wrap(err)
}
return token, nil
}
// deleteToken deletes (revokes) a token by its value
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
token := p.ByName("token")
if err := auth.DeleteToken(r.Context(), token); err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("Token %v deleted", token)), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
Web UI disconnects (#5276) * Use fake clock consistently in units tests. * Split web session management into two interfaces and implement them separately for clear separation * Split session management into New/Validate to make it aparent where the sessions are created and where existing sessions are managed. Remove ttlmap in favor of a simple map and handle expirations explicitly. Add web session management to gRPC server for the cache. * Reintroduce web sessions APIs under a getter interface. * Add SubKind to WatchKind for gRPC and add conversions from/to protobuf. Fix web sessions unit tests. * lib/web: create/insert session context in ValidateSession if the session has not yet been added to session cache. lib/cache: add event filter for web session in auth cache. lib/auth: propagate web session subkind in gRPC event. * Add implicit migrations for legacy web session key path for queries. * Integrate web token in lib/web * Add a bearer token when upserting a web session * Fix tests. Use fake clock wherever possible. * Converge session cache handling in lib/web * Clean up and add doc comments where necessary * Use correct form of sessions/tokens controller for ServerWithRoles. Use fake time in web tests * Converge the web sessions/tokens handling in lib/auth to match the old behavior w.r.t access checking (e.g. implicit handling of the local user identity). * Use cached reads and waiters only when necessary. Query sessions/tokens using best-effort - first looking in the cache and falling back to a proxy client * Properly propagate events about deletes for values with subkind. * Update to retrofit changes after recent teleport API refactorings * Update comment on removing legacy code to move the deadline to 7.x * Do not close the resources on the session when it expires - this beats the purpose of this PR. Also avoid a race between closing the cached clients and an existing reference to the session by letting the session linger for longer before removing it. * Move web session/token request structs to the api client proto package * Only set HTTP fs on the web handler if the UI is enabled * Properly tear down web session test by releasing resources at the end. Fix the web UI assets configuration by removing DisableUI and instead use the presence of assets (HTTP file system) as an indicator that the web UI has been enabled. * Decrease the expired session cache clean up threshold to 2m. Only log the expiration error message for errors other than not found * Add test for terminal disconnect when using two proxies in HA mode
2021-02-04 15:50:18 +00:00
user, sessionID := p.ByName("user"), p.ByName("sid")
err := auth.WebSessions().Delete(r.Context(), types.DeleteWebSessionRequest{
User: user,
SessionID: sessionID,
})
if err != nil {
return nil, trace.Wrap(err)
}
Web UI disconnects (#5276) * Use fake clock consistently in units tests. * Split web session management into two interfaces and implement them separately for clear separation * Split session management into New/Validate to make it aparent where the sessions are created and where existing sessions are managed. Remove ttlmap in favor of a simple map and handle expirations explicitly. Add web session management to gRPC server for the cache. * Reintroduce web sessions APIs under a getter interface. * Add SubKind to WatchKind for gRPC and add conversions from/to protobuf. Fix web sessions unit tests. * lib/web: create/insert session context in ValidateSession if the session has not yet been added to session cache. lib/cache: add event filter for web session in auth cache. lib/auth: propagate web session subkind in gRPC event. * Add implicit migrations for legacy web session key path for queries. * Integrate web token in lib/web * Add a bearer token when upserting a web session * Fix tests. Use fake clock wherever possible. * Converge session cache handling in lib/web * Clean up and add doc comments where necessary * Use correct form of sessions/tokens controller for ServerWithRoles. Use fake time in web tests * Converge the web sessions/tokens handling in lib/auth to match the old behavior w.r.t access checking (e.g. implicit handling of the local user identity). * Use cached reads and waiters only when necessary. Query sessions/tokens using best-effort - first looking in the cache and falling back to a proxy client * Properly propagate events about deletes for values with subkind. * Update to retrofit changes after recent teleport API refactorings * Update comment on removing legacy code to move the deadline to 7.x * Do not close the resources on the session when it expires - this beats the purpose of this PR. Also avoid a race between closing the cached clients and an existing reference to the session by letting the session linger for longer before removing it. * Move web session/token request structs to the api client proto package * Only set HTTP fs on the web handler if the UI is enabled * Properly tear down web session test by releasing resources at the end. Fix the web UI assets configuration by removing DisableUI and instead use the presence of assets (HTTP file system) as an indicator that the web UI has been enabled. * Decrease the expired session cache clean up threshold to 2m. Only log the expiration error message for errors other than not found * Add test for terminal disconnect when using two proxies in HA mode
2021-02-04 15:50:18 +00:00
return message(fmt.Sprintf("session %q for user %q deleted", sessionID, user)), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-30 02:57:20 +00:00
user, sid := p.ByName("user"), p.ByName("sid")
Web UI disconnects (#5276) * Use fake clock consistently in units tests. * Split web session management into two interfaces and implement them separately for clear separation * Split session management into New/Validate to make it aparent where the sessions are created and where existing sessions are managed. Remove ttlmap in favor of a simple map and handle expirations explicitly. Add web session management to gRPC server for the cache. * Reintroduce web sessions APIs under a getter interface. * Add SubKind to WatchKind for gRPC and add conversions from/to protobuf. Fix web sessions unit tests. * lib/web: create/insert session context in ValidateSession if the session has not yet been added to session cache. lib/cache: add event filter for web session in auth cache. lib/auth: propagate web session subkind in gRPC event. * Add implicit migrations for legacy web session key path for queries. * Integrate web token in lib/web * Add a bearer token when upserting a web session * Fix tests. Use fake clock wherever possible. * Converge session cache handling in lib/web * Clean up and add doc comments where necessary * Use correct form of sessions/tokens controller for ServerWithRoles. Use fake time in web tests * Converge the web sessions/tokens handling in lib/auth to match the old behavior w.r.t access checking (e.g. implicit handling of the local user identity). * Use cached reads and waiters only when necessary. Query sessions/tokens using best-effort - first looking in the cache and falling back to a proxy client * Properly propagate events about deletes for values with subkind. * Update to retrofit changes after recent teleport API refactorings * Update comment on removing legacy code to move the deadline to 7.x * Do not close the resources on the session when it expires - this beats the purpose of this PR. Also avoid a race between closing the cached clients and an existing reference to the session by letting the session linger for longer before removing it. * Move web session/token request structs to the api client proto package * Only set HTTP fs on the web handler if the UI is enabled * Properly tear down web session test by releasing resources at the end. Fix the web UI assets configuration by removing DisableUI and instead use the presence of assets (HTTP file system) as an indicator that the web UI has been enabled. * Decrease the expired session cache clean up threshold to 2m. Only log the expiration error message for errors other than not found * Add test for terminal disconnect when using two proxies in HA mode
2021-02-04 15:50:18 +00:00
sess, err := auth.GetWebSessionInfo(r.Context(), user, sid)
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
}
// DELETE IN: 4.2.0
2018-05-04 00:12:52 +00:00
type generateUserCertReq struct {
Key []byte `json:"key"`
User string `json:"user"`
TTL time.Duration `json:"ttl"`
Compatibility string `json:"compatibility,omitempty"`
}
// DELETE IN: 4.2.0
2018-05-04 00:12:52 +00:00
func (s *APIServer) generateUserCert(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req *generateUserCertReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
certificateFormat, err := utils.CheckCertificateFormatFlag(req.Compatibility)
if err != nil {
return nil, trace.Wrap(err)
}
certs, err := auth.GenerateUserCerts(r.Context(), proto.UserCertsRequest{
PublicKey: req.Key,
Username: req.User,
Expires: s.Now().UTC().Add(req.TTL),
Format: certificateFormat,
})
2018-05-04 00:12:52 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return string(certs.SSH), nil
2018-05-04 00:12:52 +00:00
}
type signInReq struct {
Password string `json:"password"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) u2fSignRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *signInReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
user := p.ByName("user")
pass := []byte(req.Password)
u2fSignReq, err := auth.GetMFAAuthenticateChallenge(user, pass)
if err != nil {
return nil, trace.Wrap(err)
}
return u2fSignReq, nil
}
type WebSessionReq struct {
// User is the user name associated with the session id.
User string `json:"user"`
// PrevSessionID is the id of current session.
PrevSessionID string `json:"prev_session_id"`
// AccessRequestID is an optional field that holds the id of an approved access request.
AccessRequestID string `json:"access_request_id"`
// Switchback is a flag to indicate if user is wanting to switchback from an assumed role
// back to their default role.
Switchback bool `json:"switchback"`
}
func (s *APIServer) createWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req WebSessionReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
// DELETE IN 8.0: proxy v5 sends request with no user field.
// And since proxy v6, request will come with user field set, so grabbing user
// by param is not required.
if req.User == "" {
req.User = p.ByName("user")
}
if req.PrevSessionID != "" {
sess, err := auth.ExtendWebSession(req)
if err != nil {
return nil, trace.Wrap(err)
}
return sess, nil
2016-04-10 20:29:32 +00:00
}
sess, err := auth.CreateWebSession(req.User)
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
}
func (s *APIServer) authenticateWebUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req AuthenticateUserRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
req.Username = p.ByName("user")
sess, err := auth.AuthenticateWebUser(req)
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
}
func (s *APIServer) authenticateSSHUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req AuthenticateSSHRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
req.Username = p.ByName("user")
return auth.AuthenticateSSHUser(req)
}
// changePassword updates users password based on the old password.
2017-10-13 21:02:40 +00:00
func (s *APIServer) changePassword(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req services.ChangePasswordReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
if err := auth.ChangePassword(req); err != nil {
2017-10-13 21:02:40 +00:00
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("password has been changed for user %q", req.User)), nil
}
type upsertPasswordReq struct {
Password string `json:"password"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertPassword(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *upsertPasswordReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
user := p.ByName("user")
err := auth.UpsertPassword(user, []byte(req.Password))
if err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("password for for user %q upserted", user)), nil
}
2016-12-29 19:36:57 +00:00
type upsertUserRawReq struct {
2016-04-05 16:58:16 +00:00
User json.RawMessage `json:"user"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req *upsertUserRawReq
2016-03-31 18:40:22 +00:00
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
user, err := services.UnmarshalUser(req.User)
2016-04-05 16:58:16 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-13 03:26:59 +00:00
err = auth.UpsertUser(user)
2016-03-31 18:40:22 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-04-05 16:58:16 +00:00
return message(fmt.Sprintf("'%v' user upserted", user.GetName())), nil
2016-03-31 18:40:22 +00:00
}
type checkPasswordReq struct {
Password string `json:"password"`
OTPToken string `json:"otp_token"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) checkPassword(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req checkPasswordReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
user := p.ByName("user")
if err := auth.CheckPassword(user, []byte(req.Password), req.OTPToken); err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("%q user password matches", user)), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
user, err := auth.GetUser(p.ByName("user"), false)
2016-04-05 16:58:16 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalUser(user, services.WithVersion(version), services.PreserveResourceID()))
2016-12-30 21:25:35 +00:00
}
func rawMessage(data []byte, err error) (interface{}, error) {
if err != nil {
return nil, trace.Wrap(err)
}
m := json.RawMessage(data)
return &m, nil
2016-04-05 16:58:16 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getUsers(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
users, err := auth.GetUsers(false)
2015-03-02 20:11:23 +00:00
if err != nil {
return nil, trace.Wrap(err)
2015-03-02 20:11:23 +00:00
}
2016-12-29 23:46:19 +00:00
out := make([]json.RawMessage, len(users))
for i, user := range users {
data, err := services.MarshalUser(user, services.WithVersion(version), services.PreserveResourceID())
2016-12-29 23:46:19 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
out[i] = data
}
return out, nil
2015-03-02 20:11:23 +00:00
}
// DELETE IN: 5.2 REST method is replaced by grpc method with context.
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-30 02:57:20 +00:00
user := p.ByName("user")
if err := auth.DeleteUser(r.Context(), user); err != nil {
return nil, trace.Wrap(err)
2015-03-02 20:11:23 +00:00
}
return message(fmt.Sprintf("user %q deleted", user)), nil
}
2015-03-02 20:11:23 +00:00
type generateKeyPairReq struct {
Password string `json:"password"`
2015-03-02 20:11:23 +00:00
}
type generateKeyPairResponse struct {
PrivKey []byte `json:"privkey"`
PubKey string `json:"pubkey"`
}
2015-03-02 20:11:23 +00:00
2016-12-29 23:46:19 +00:00
func (s *APIServer) generateKeyPair(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req *generateKeyPairReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
2015-03-02 20:11:23 +00:00
}
2016-12-13 03:26:59 +00:00
priv, pub, err := auth.GenerateKeyPair(req.Password)
2015-03-02 20:11:23 +00:00
if err != nil {
return nil, trace.Wrap(err)
2015-03-02 20:11:23 +00:00
}
return &generateKeyPairResponse{PrivKey: priv, PubKey: string(pub)}, nil
}
2015-03-02 20:11:23 +00:00
type generateHostCertReq struct {
Key []byte `json:"key"`
HostID string `json:"hostname"`
NodeName string `json:"node_name"`
Principals []string `json:"principals"`
ClusterName string `json:"auth_domain"`
Roles teleport.Roles `json:"roles"`
TTL time.Duration `json:"ttl"`
2015-03-02 20:11:23 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) generateHostCert(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req *generateHostCertReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
cert, err := auth.GenerateHostCert(req.Key, req.HostID, req.NodeName, req.Principals, req.ClusterName, req.Roles, req.TTL)
2015-03-02 20:11:23 +00:00
if err != nil {
return nil, trace.Wrap(err)
2015-03-02 20:11:23 +00:00
}
return string(cert), nil
2015-03-02 20:11:23 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) generateToken(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req GenerateTokenRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
token, err := auth.GenerateToken(r.Context(), req)
if err != nil {
return nil, trace.Wrap(err)
}
return token, nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) registerUsingToken(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req RegisterUsingTokenRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
// Pass along the remote address the request came from to the registration function.
req.RemoteAddr = r.RemoteAddr
keys, err := auth.RegisterUsingToken(req)
if err != nil {
return nil, trace.Wrap(err)
}
return keys, nil
}
type registerNewAuthServerReq struct {
2016-03-10 17:41:01 +00:00
Token string `json:"token"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) registerNewAuthServer(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req *registerNewAuthServerReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
err := auth.RegisterNewAuthServer(r.Context(), req.Token)
if err != nil {
return nil, trace.Wrap(err)
}
2016-03-10 17:41:01 +00:00
return message("ok"), nil
}
func (s *APIServer) generateServerKeys(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
var req GenerateServerKeysRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
// Pass along the remote address the request came from to the registration function.
req.RemoteAddr = r.RemoteAddr
keys, err := auth.GenerateServerKeys(req)
if err != nil {
return nil, trace.Wrap(err)
}
return keys, nil
}
func (s *APIServer) rotateCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req RotateRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
if err := auth.RotateCertAuthority(req); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 19:36:57 +00:00
type upsertCertAuthorityRawReq struct {
CA json.RawMessage `json:"ca"`
TTL time.Duration `json:"ttl"`
2015-03-02 20:11:23 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req *upsertCertAuthorityRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
ca, err := services.UnmarshalCertAuthority(req.CA)
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2017-04-07 23:51:31 +00:00
if req.TTL != 0 {
ca.SetExpiry(s.Now().UTC().Add(req.TTL))
2017-04-07 23:51:31 +00:00
}
if err = services.ValidateCertAuthority(ca); err != nil {
return nil, trace.Wrap(err)
}
2017-04-07 23:51:31 +00:00
if err := auth.UpsertCertAuthority(ca); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
type rotateExternalCertAuthorityRawReq struct {
CA json.RawMessage `json:"ca"`
}
func (s *APIServer) rotateExternalCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req rotateExternalCertAuthorityRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
ca, err := services.UnmarshalCertAuthority(req.CA)
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.RotateExternalCertAuthority(ca); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getCertAuthorities(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-03 22:06:50 +00:00
loadKeys, _, err := httplib.ParseBool(r.URL.Query(), "load_keys")
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
certs, err := auth.GetCertAuthorities(services.CertAuthType(p.ByName("type")), loadKeys)
2017-03-02 19:50:35 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 19:36:57 +00:00
items := make([]json.RawMessage, len(certs))
for i, cert := range certs {
data, err := services.MarshalCertAuthority(cert, services.WithVersion(version), services.PreserveResourceID())
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
loadKeys, _, err := httplib.ParseBool(r.URL.Query(), "load_keys")
if err != nil {
return nil, trace.Wrap(err)
}
id := services.CertAuthID{
Type: services.CertAuthType(p.ByName("type")),
DomainName: p.ByName("domain"),
}
ca, err := auth.GetCertAuthority(id, loadKeys)
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalCertAuthority(ca, services.WithVersion(version), services.PreserveResourceID()))
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getDomainName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
domain, err := auth.GetDomainName()
if err != nil {
return nil, trace.Wrap(err)
}
return domain, nil
}
// getClusterCACert returns the CAs for the local cluster without signing keys.
func (s *APIServer) getClusterCACert(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
localCA, err := auth.GetClusterCACert()
if err != nil {
return nil, trace.Wrap(err)
}
return localCA, nil
}
Merge Teleport V4.3 UI branch to master (#3583) * Add monorepo * Add reset/passwd capability for local users (#3287) * Add UserTokens to allow password resets * Pass context down through ChangePasswordWithToken * Rename UserToken to ResetPasswordToken * Add auto formatting for proto files * Add common Marshaller interfaces to reset password token * Allow enterprise "tctl" reuse OSS user methods (#3344) * Pass localAuthEnabled flag to UI (#3412) * Added LocalAuthEnabled prop to WebConfigAuthSetting struct in webconfig.go * Added LocalAuthEnabled state as part of webCfg in apiserver.go * update e-refs * Fix a regression bug after merge * Update tctl CLI output msgs (#3442) * Use local user client when resolving user roles * Update webapps ref * Add and retrieve fields from Cluster struct (#3476) * Set Teleport versions for node, auth, proxy init heartbeat * Add and retrieve fields NodeCount, PublicURL, AuthVersion from Clusters * Remove debug logging to avoid log pollution when getting public_addr of proxy * Create helper func GuessProxyHost to get the public_addr of a proxy host * Refactor newResetPasswordToken to use GuessProxyHost and remove publicUrl func * Remove webapps submodule * Add webassets submodule * Replace webapps sub-module reference with webassets * Update webassets path in Makefile * Update webassets 1b11b26 Simplify and clean up Makefile (#62) https://github.com/gravitational/webapps/commit/1b11b26 * Retrieve cluster details for user context (#3515) * Let GuessProxyHost also return proxy's version * Unit test GuessProxyHostAndVersion & GetClusterDetails * Update webassets 4dfef4e Fix build pipeline (#66) https://github.com/gravitational/webapps/commit/4dfef4e * Update e-ref * Update webassets 0647568 Fix OSS redirects https://github.com/gravitational/webapps/commit/0647568 * update e-ref * Update webassets e0f4189 Address security audit warnings Updates "minimist" package which is used by 7y old "optimist". https://github.com/gravitational/webapps/commit/e0f4189 * Add new attr to Session struct (#3574) * Add fields ServerHostname and ServerAddr * Set these fields on newSession * Ensure webassets submodule during build * Update e-ref * Ensure webassets before running unit-tests * Update E-ref Co-authored-by: Lisa Kim <lisa@gravitational.com> Co-authored-by: Pierre Beaucamp <pierre@gravitational.com> Co-authored-by: Jenkins <jenkins@gravitational.io>
2020-04-15 19:35:26 +00:00
func (s *APIServer) changePasswordWithToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req ChangePasswordWithTokenRequest
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
webSession, err := auth.ChangePasswordWithToken(r.Context(), req)
if err != nil {
log.Debugf("Failed to change user password with token: %v.", err)
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalWebSession(webSession, services.WithVersion(version)))
Merge Teleport V4.3 UI branch to master (#3583) * Add monorepo * Add reset/passwd capability for local users (#3287) * Add UserTokens to allow password resets * Pass context down through ChangePasswordWithToken * Rename UserToken to ResetPasswordToken * Add auto formatting for proto files * Add common Marshaller interfaces to reset password token * Allow enterprise "tctl" reuse OSS user methods (#3344) * Pass localAuthEnabled flag to UI (#3412) * Added LocalAuthEnabled prop to WebConfigAuthSetting struct in webconfig.go * Added LocalAuthEnabled state as part of webCfg in apiserver.go * update e-refs * Fix a regression bug after merge * Update tctl CLI output msgs (#3442) * Use local user client when resolving user roles * Update webapps ref * Add and retrieve fields from Cluster struct (#3476) * Set Teleport versions for node, auth, proxy init heartbeat * Add and retrieve fields NodeCount, PublicURL, AuthVersion from Clusters * Remove debug logging to avoid log pollution when getting public_addr of proxy * Create helper func GuessProxyHost to get the public_addr of a proxy host * Refactor newResetPasswordToken to use GuessProxyHost and remove publicUrl func * Remove webapps submodule * Add webassets submodule * Replace webapps sub-module reference with webassets * Update webassets path in Makefile * Update webassets 1b11b26 Simplify and clean up Makefile (#62) https://github.com/gravitational/webapps/commit/1b11b26 * Retrieve cluster details for user context (#3515) * Let GuessProxyHost also return proxy's version * Unit test GuessProxyHostAndVersion & GetClusterDetails * Update webassets 4dfef4e Fix build pipeline (#66) https://github.com/gravitational/webapps/commit/4dfef4e * Update e-ref * Update webassets 0647568 Fix OSS redirects https://github.com/gravitational/webapps/commit/0647568 * update e-ref * Update webassets e0f4189 Address security audit warnings Updates "minimist" package which is used by 7y old "optimist". https://github.com/gravitational/webapps/commit/e0f4189 * Add new attr to Session struct (#3574) * Add fields ServerHostname and ServerAddr * Set these fields on newSession * Ensure webassets submodule during build * Update e-ref * Ensure webassets before running unit-tests * Update E-ref Co-authored-by: Lisa Kim <lisa@gravitational.com> Co-authored-by: Pierre Beaucamp <pierre@gravitational.com> Co-authored-by: Jenkins <jenkins@gravitational.io>
2020-04-15 19:35:26 +00:00
}
// getU2FAppID returns the U2F AppID in the auth configuration
2016-12-29 23:46:19 +00:00
func (s *APIServer) getU2FAppID(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2017-07-28 18:37:12 +00:00
cap, err := auth.GetAuthPreference()
if err != nil {
return nil, trace.Wrap(err)
}
universalSecondFactor, err := cap.GetU2F()
if err != nil {
return nil, trace.Wrap(err)
}
w.Header().Set("Content-Type", "application/fido.trusted-apps+json")
2017-07-28 18:37:12 +00:00
return universalSecondFactor.AppID, nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
id := services.CertAuthID{
2016-12-30 02:57:20 +00:00
DomainName: p.ByName("domain"),
Type: services.CertAuthType(p.ByName("type")),
}
2016-12-13 03:26:59 +00:00
if err := auth.DeleteCertAuthority(id); err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("cert '%v' deleted", id)), nil
}
2016-03-01 21:19:43 +00:00
type createSessionReq struct {
Session session.Session `json:"session"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) createSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-03-01 21:19:43 +00:00
var req *createSessionReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
req.Session.Namespace = namespace
2016-12-13 03:26:59 +00:00
if err := auth.CreateSession(req.Session); err != nil {
return nil, trace.Wrap(err)
}
2016-03-01 21:19:43 +00:00
return message("ok"), nil
}
type updateSessionReq struct {
Update session.UpdateRequest `json:"update"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) updateSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-03-01 21:19:43 +00:00
var req *updateSessionReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
req.Update.Namespace = namespace
2016-12-13 03:26:59 +00:00
if err := auth.UpdateSession(req.Update); err != nil {
2016-03-01 21:19:43 +00:00
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) deleteSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteSession(p.ByName("namespace"), session.ID(p.ByName("id")))
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getSessions(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
sessions, err := auth.GetSessions(namespace)
2015-06-30 23:12:18 +00:00
if err != nil {
return nil, trace.Wrap(err)
2015-06-30 23:12:18 +00:00
}
return sessions, nil
2015-06-30 23:12:18 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-19 00:58:53 +00:00
sid, err := session.ParseID(p.ByName("id"))
if err != nil {
return nil, trace.Wrap(err)
}
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
se, err := auth.GetSession(namespace, *sid)
2015-06-30 23:12:18 +00:00
if err != nil {
return nil, trace.Wrap(err)
2015-06-30 23:12:18 +00:00
}
return se, nil
2015-06-30 23:12:18 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getSignupU2FRegisterRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-30 02:57:20 +00:00
token := p.ByName("token")
2016-12-15 00:36:55 +00:00
u2fRegReq, err := auth.GetSignupU2FRegisterRequest(token)
if err != nil {
return nil, trace.Wrap(err)
}
return u2fRegReq, nil
}
2016-12-29 19:36:57 +00:00
type upsertOIDCConnectorRawReq struct {
Connector json.RawMessage `json:"connector"`
TTL time.Duration `json:"ttl"`
2016-04-03 05:20:51 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertOIDCConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req *upsertOIDCConnectorRawReq
2016-04-03 05:20:51 +00:00
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
connector, err := services.UnmarshalOIDCConnector(req.Connector)
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2017-04-07 23:51:31 +00:00
if req.TTL != 0 {
connector.SetExpiry(s.Now().UTC().Add(req.TTL))
2017-04-07 23:51:31 +00:00
}
if err = services.ValidateOIDCConnector(connector); err != nil {
return nil, trace.Wrap(err)
}
err = auth.UpsertOIDCConnector(r.Context(), connector)
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getOIDCConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-03 22:06:50 +00:00
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
connector, err := auth.GetOIDCConnector(r.Context(), p.ByName("id"), withSecrets)
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalOIDCConnector(connector, services.WithVersion(version)))
2016-04-03 05:20:51 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteOIDCConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteOIDCConnector(r.Context(), p.ByName("id"))
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getOIDCConnectors(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-03 22:06:50 +00:00
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
connectors, err := auth.GetOIDCConnectors(r.Context(), withSecrets)
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 19:36:57 +00:00
items := make([]json.RawMessage, len(connectors))
for i, connector := range connectors {
data, err := services.MarshalOIDCConnector(connector, services.WithVersion(version))
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
2016-04-03 05:20:51 +00:00
}
type createOIDCAuthRequestReq struct {
Req services.OIDCAuthRequest `json:"req"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) createOIDCAuthRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-03 05:20:51 +00:00
var req *createOIDCAuthRequestReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-13 03:26:59 +00:00
response, err := auth.CreateOIDCAuthRequest(req.Req)
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return response, nil
}
type validateOIDCAuthCallbackReq struct {
2016-04-15 00:50:54 +00:00
Query url.Values `json:"query"`
2016-04-03 05:20:51 +00:00
}
2016-12-29 20:23:58 +00:00
// oidcAuthRawResponse is returned when auth server validated callback parameters
// returned from OIDC provider
type oidcAuthRawResponse struct {
// Username is authenticated teleport username
Username string `json:"username"`
// Identity contains validated OIDC identity
2017-05-05 22:53:05 +00:00
Identity services.ExternalIdentity `json:"identity"`
2016-12-29 20:23:58 +00:00
// Web session will be generated by auth server if requested in OIDCAuthRequest
2017-02-11 02:55:51 +00:00
Session json.RawMessage `json:"session,omitempty"`
2016-12-29 20:23:58 +00:00
// Cert will be generated by certificate authority
Cert []byte `json:"cert,omitempty"`
// TLSCert is PEM encoded TLS certificate
TLSCert []byte `json:"tls_cert,omitempty"`
2016-12-29 20:23:58 +00:00
// Req is original oidc auth request
Req services.OIDCAuthRequest `json:"req"`
// HostSigners is a list of signing host public keys
// trusted by proxy, used in console login
HostSigners []json.RawMessage `json:"host_signers"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) validateOIDCAuthCallback(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-03 05:20:51 +00:00
var req *validateOIDCAuthCallbackReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
2016-12-13 03:26:59 +00:00
response, err := auth.ValidateOIDCAuthCallback(req.Query)
2016-04-03 05:20:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-29 20:23:58 +00:00
raw := oidcAuthRawResponse{
Username: response.Username,
Identity: response.Identity,
Cert: response.Cert,
TLSCert: response.TLSCert,
2016-12-29 20:23:58 +00:00
Req: response.Req,
}
2017-02-11 02:55:51 +00:00
if response.Session != nil {
rawSession, err := services.MarshalWebSession(response.Session, services.WithVersion(version))
2017-02-11 02:55:51 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.Session = rawSession
2017-05-05 22:53:05 +00:00
}
raw.HostSigners = make([]json.RawMessage, len(response.HostSigners))
for i, ca := range response.HostSigners {
data, err := services.MarshalCertAuthority(ca, services.WithVersion(version))
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.HostSigners[i] = data
}
return &raw, nil
}
type createSAMLConnectorRawReq struct {
Connector json.RawMessage `json:"connector"`
}
func (s *APIServer) createSAMLConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *createSAMLConnectorRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
connector, err := services.UnmarshalSAMLConnector(req.Connector)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateSAMLConnector(connector); err != nil {
return nil, trace.Wrap(err)
}
err = auth.CreateSAMLConnector(r.Context(), connector)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
type upsertSAMLConnectorRawReq struct {
Connector json.RawMessage `json:"connector"`
}
func (s *APIServer) upsertSAMLConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *upsertSAMLConnectorRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
connector, err := services.UnmarshalSAMLConnector(req.Connector)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateSAMLConnector(connector); err != nil {
return nil, trace.Wrap(err)
}
err = auth.UpsertSAMLConnector(r.Context(), connector)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) getSAMLConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
if err != nil {
return nil, trace.Wrap(err)
}
connector, err := auth.GetSAMLConnector(r.Context(), p.ByName("id"), withSecrets)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalSAMLConnector(connector, services.WithVersion(version)))
2017-05-05 22:53:05 +00:00
}
func (s *APIServer) deleteSAMLConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteSAMLConnector(r.Context(), p.ByName("id"))
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) getSAMLConnectors(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
if err != nil {
return nil, trace.Wrap(err)
}
connectors, err := auth.GetSAMLConnectors(r.Context(), withSecrets)
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items := make([]json.RawMessage, len(connectors))
for i, connector := range connectors {
data, err := services.MarshalSAMLConnector(connector, services.WithVersion(version))
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
type createSAMLAuthRequestReq struct {
Req services.SAMLAuthRequest `json:"req"`
}
func (s *APIServer) createSAMLAuthRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *createSAMLAuthRequestReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
response, err := auth.CreateSAMLAuthRequest(req.Req)
if err != nil {
return nil, trace.Wrap(err)
}
return response, nil
}
type validateSAMLResponseReq struct {
Response string `json:"response"`
}
// samlAuthRawResponse is returned when auth server validated callback parameters
// returned from SAML provider
type samlAuthRawResponse struct {
// Username is authenticated teleport username
Username string `json:"username"`
// Identity contains validated OIDC identity
Identity services.ExternalIdentity `json:"identity"`
// Web session will be generated by auth server if requested in OIDCAuthRequest
Session json.RawMessage `json:"session,omitempty"`
// Cert will be generated by certificate authority
Cert []byte `json:"cert,omitempty"`
// Req is original oidc auth request
Req services.SAMLAuthRequest `json:"req"`
// HostSigners is a list of signing host public keys
// trusted by proxy, used in console login
HostSigners []json.RawMessage `json:"host_signers"`
2018-01-16 20:22:39 +00:00
// TLSCert is TLS certificate authority certificate
TLSCert []byte `json:"tls_cert,omitempty"`
2017-05-05 22:53:05 +00:00
}
func (s *APIServer) validateSAMLResponse(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *validateSAMLResponseReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
response, err := auth.ValidateSAMLResponse(req.Response)
if err != nil {
return nil, trace.Wrap(err)
}
raw := samlAuthRawResponse{
Username: response.Username,
Identity: response.Identity,
Cert: response.Cert,
Req: response.Req,
2018-01-16 20:22:39 +00:00
TLSCert: response.TLSCert,
2017-05-05 22:53:05 +00:00
}
if response.Session != nil {
rawSession, err := services.MarshalWebSession(response.Session, services.WithVersion(version))
2017-05-05 22:53:05 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.Session = rawSession
2017-02-11 02:55:51 +00:00
}
2016-12-29 20:23:58 +00:00
raw.HostSigners = make([]json.RawMessage, len(response.HostSigners))
for i, ca := range response.HostSigners {
data, err := services.MarshalCertAuthority(ca, services.WithVersion(version))
2016-12-29 20:23:58 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.HostSigners[i] = data
}
2017-02-11 19:05:09 +00:00
return &raw, nil
2016-04-03 05:20:51 +00:00
}
2017-12-14 21:41:38 +00:00
// createGithubConnectorRawReq is a request to create a new Github connector
type createGithubConnectorRawReq struct {
// Connector is the connector data
Connector json.RawMessage `json:"connector"`
}
/* createGithubConnector creates a new Github connector
POST /:version/github/connectors
Success response: {"message": "ok"}
*/
func (s *APIServer) createGithubConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req createGithubConnectorRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
connector, err := services.UnmarshalGithubConnector(req.Connector)
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.CreateGithubConnector(connector); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// upsertGithubConnectorRawReq is a request to upsert a Github connector
type upsertGithubConnectorRawReq struct {
// Connector is the connector data
Connector json.RawMessage `json:"connector"`
}
/* upsertGithubConnector creates or updates a Github connector
PUT /:version/github/connectors
Success response: {"message": "ok"}
*/
func (s *APIServer) upsertGithubConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req upsertGithubConnectorRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
connector, err := services.UnmarshalGithubConnector(req.Connector)
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.UpsertGithubConnector(r.Context(), connector); err != nil {
2017-12-14 21:41:38 +00:00
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
/* getGithubConnectors returns a list of all configured Github connectors
GET /:version/github/connectors
Success response: []services.GithubConnector
*/
func (s *APIServer) getGithubConnectors(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
if err != nil {
return nil, trace.Wrap(err)
}
connectors, err := auth.GetGithubConnectors(r.Context(), withSecrets)
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items := make([]json.RawMessage, len(connectors))
for i, connector := range connectors {
cbytes, err := services.MarshalGithubConnector(connector, services.PreserveResourceID())
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = cbytes
2017-12-14 21:41:38 +00:00
}
return items, nil
}
/* getGithubConnector returns the specified Github connector
GET /:version/github/connectors/:id
Success response: services.GithubConnector
*/
func (s *APIServer) getGithubConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
withSecrets, _, err := httplib.ParseBool(r.URL.Query(), "with_secrets")
if err != nil {
return nil, trace.Wrap(err)
}
connector, err := auth.GetGithubConnector(r.Context(), p.ByName("id"), withSecrets)
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalGithubConnector(connector, services.PreserveResourceID()))
2017-12-14 21:41:38 +00:00
}
/* deleteGithubConnector deletes the specified Github connector
DELETE /:version/github/connectors/:id
Success response: {"message": "ok"}
*/
func (s *APIServer) deleteGithubConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
if err := auth.DeleteGithubConnector(r.Context(), p.ByName("id")); err != nil {
2017-12-14 21:41:38 +00:00
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// createGithubAuthRequestReq is a request to start Github OAuth2 flow
type createGithubAuthRequestReq struct {
// Req is the request parameters
Req services.GithubAuthRequest `json:"req"`
}
/* createGithubAuthRequest creates a new request for Github OAuth2 flow
POST /:version/github/requests/create
Success response: services.GithubAuthRequest
*/
func (s *APIServer) createGithubAuthRequest(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req createGithubAuthRequestReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
response, err := auth.CreateGithubAuthRequest(req.Req)
if err != nil {
return nil, trace.Wrap(err)
}
return response, nil
}
// validateGithubAuthCallbackReq is a request to validate Github OAuth2 callback
type validateGithubAuthCallbackReq struct {
// Query is the callback query string
Query url.Values `json:"query"`
}
// githubAuthRawResponse is returned when auth server validated callback
// parameters returned from Github during OAuth2 flow
type githubAuthRawResponse struct {
// Username is authenticated teleport username
Username string `json:"username"`
// Identity contains validated OIDC identity
Identity services.ExternalIdentity `json:"identity"`
// Web session will be generated by auth server if requested in OIDCAuthRequest
Session json.RawMessage `json:"session,omitempty"`
// Cert will be generated by certificate authority
Cert []byte `json:"cert,omitempty"`
// TLSCert is PEM encoded TLS certificate
TLSCert []byte `json:"tls_cert,omitempty"`
2017-12-14 21:41:38 +00:00
// Req is original oidc auth request
Req services.GithubAuthRequest `json:"req"`
// HostSigners is a list of signing host public keys
// trusted by proxy, used in console login
HostSigners []json.RawMessage `json:"host_signers"`
}
/* validateGithubAuthRequest validates Github auth callback redirect
POST /:version/github/requests/validate
Success response: githubAuthRawResponse
*/
func (s *APIServer) validateGithubAuthCallback(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req validateGithubAuthCallbackReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
response, err := auth.ValidateGithubAuthCallback(req.Query)
if err != nil {
return nil, trace.Wrap(err)
}
raw := githubAuthRawResponse{
Username: response.Username,
Identity: response.Identity,
Cert: response.Cert,
TLSCert: response.TLSCert,
2017-12-14 21:41:38 +00:00
Req: response.Req,
}
if response.Session != nil {
rawSession, err := services.MarshalWebSession(
response.Session, services.WithVersion(version), services.PreserveResourceID())
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.Session = rawSession
}
raw.HostSigners = make([]json.RawMessage, len(response.HostSigners))
for i, ca := range response.HostSigners {
data, err := services.MarshalCertAuthority(
ca, services.WithVersion(version), services.PreserveResourceID())
2017-12-14 21:41:38 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
raw.HostSigners[i] = data
}
return &raw, nil
}
2016-12-29 20:23:58 +00:00
// HTTP GET /:version/events?query
2016-04-29 09:42:08 +00:00
//
// Query fields:
// 'from' : time filter in RFC3339 format
// 'to' : time filter in RFC3339 format
// ... : other fields are passed directly to the audit backend
2016-12-29 23:46:19 +00:00
func (s *APIServer) searchEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-04-29 09:42:08 +00:00
var err error
to := time.Now().In(time.UTC)
from := to.AddDate(0, -1, 0) // one month ago
query := r.URL.Query()
// parse 'to' and 'from' params:
fromStr := query.Get("from")
if fromStr != "" {
from, err = time.Parse(time.RFC3339, fromStr)
if err != nil {
return nil, trace.BadParameter("from")
}
}
toStr := query.Get("to")
if toStr != "" {
to, err = time.Parse(time.RFC3339, toStr)
if err != nil {
return nil, trace.BadParameter("to")
}
}
var limit int
limitStr := query.Get("limit")
if limitStr != "" {
limit, err = strconv.Atoi(limitStr)
if err != nil {
return nil, trace.BadParameter("failed to parse limit: %q", limit)
}
}
eventTypes := query[events.EventType]
eventsList, _, err := auth.SearchEvents(from, to, defaults.Namespace, eventTypes, limit, "")
2016-04-29 09:42:08 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return eventsList, nil
2016-04-29 09:42:08 +00:00
}
2017-08-24 00:31:07 +00:00
// searchSessionEvents only allows searching audit log for events related to session playback.
func (s *APIServer) searchSessionEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var err error
// default values for "to" and "from" fields
to := time.Now().In(time.UTC) // now
from := to.AddDate(0, -1, 0) // one month ago
// parse query for "to" and "from"
query := r.URL.Query()
fromStr := query.Get("from")
if fromStr != "" {
from, err = time.Parse(time.RFC3339, fromStr)
if err != nil {
return nil, trace.BadParameter("from")
}
}
toStr := query.Get("to")
if toStr != "" {
to, err = time.Parse(time.RFC3339, toStr)
if err != nil {
return nil, trace.BadParameter("to")
}
}
var limit int
limitStr := query.Get("limit")
if limitStr != "" {
limit, err = strconv.Atoi(limitStr)
if err != nil {
return nil, trace.BadParameter("failed to parse limit: %q", limit)
}
}
2017-08-24 00:31:07 +00:00
// only pull back start and end events to build list of completed sessions
eventsList, _, err := auth.SearchSessionEvents(from, to, limit, "")
2017-08-24 00:31:07 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return eventsList, nil
}
type auditEventReq struct {
// Event is the event that's being emitted.
Event events.Event `json:"event"`
// Fields is the additional event fields.
Fields events.EventFields `json:"fields"`
// Type is the event type.
//
// This field is obsolete and kept for backwards compatibility.
Type string `json:"type"`
}
2016-12-29 20:23:58 +00:00
// HTTP POST /:version/events
2016-12-29 23:46:19 +00:00
func (s *APIServer) emitAuditEvent(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req auditEventReq
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
// Validate serverID field in event matches server ID from x509 identity. This
// check makes sure nodes can only submit events for themselves.
serverID, err := s.getServerID(r)
if err != nil {
return nil, trace.Wrap(err)
}
err = events.ValidateEvent(req.Fields, serverID)
if err != nil {
log.Warnf("Rejecting audit event %v from %v: %v. System may be under attack, a "+
"node is attempting to submit events for an identity other than its own.",
req.Type, serverID, err)
return nil, trace.AccessDenied("failed to validate event")
}
// DELETE IN: 4.1.0.
//
// For backwards compatibility, check if the full event struct has
// been sent in the request or just the event type.
if req.Event.Name != "" {
err = auth.EmitAuditEventLegacy(req.Event, req.Fields)
} else {
err = auth.EmitAuditEventLegacy(events.Event{Name: req.Type}, req.Fields)
}
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2017-05-27 01:03:19 +00:00
// HTTP POST /:version/sessions/:id/slice
func (s *APIServer) postSessionSlice(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
2017-05-26 01:56:32 +00:00
return nil, trace.Wrap(err)
}
2017-05-27 01:03:19 +00:00
var slice events.SessionSlice
if err := slice.Unmarshal(data); err != nil {
return nil, trace.BadParameter("failed to unmarshal %v", err)
}
// Validate serverID field in event matches server ID from x509 identity. This
// check makes sure nodes can only submit events for themselves.
serverID, err := s.getServerID(r)
if err != nil {
return nil, trace.Wrap(err)
}
for _, v := range slice.GetChunks() {
var f events.EventFields
err = utils.FastUnmarshal(v.GetData(), &f)
if err != nil {
return nil, trace.Wrap(err)
}
err := events.ValidateEvent(f, serverID)
if err != nil {
log.Warnf("Rejecting audit event %v from %v: %v. System may be under attack, a "+
"node is attempting to submit events for an identity other than its own.",
f.GetType(), serverID, err)
return nil, trace.AccessDenied("failed to validate event")
}
}
2017-05-27 01:03:19 +00:00
if err := auth.PostSessionSlice(slice); err != nil {
2017-05-26 01:56:32 +00:00
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// HTTP POST /:version/sessions/:id/recording
func (s *APIServer) uploadSessionRecording(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var files form.Files
var namespace, sid string
err := form.Parse(r,
form.FileSlice("recording", &files),
form.String("namespace", &namespace, form.Required()),
form.String("sid", &sid, form.Required()),
)
if err != nil {
return nil, trace.Wrap(err)
}
2018-09-25 18:09:13 +00:00
if r.MultipartForm != nil {
defer r.MultipartForm.RemoveAll()
}
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
if len(files) != 1 {
return nil, trace.BadParameter("expected a single file parameter but got %d", len(files))
}
defer files[0].Close()
_, err = session.ParseID(sid)
if err != nil {
return nil, trace.Wrap(err)
}
// Make a copy of the archive because it needs to be read twice: once to
// validate it and then again to upload it.
var buf bytes.Buffer
recording := io.TeeReader(files[0], &buf)
// Validate namespace and serverID fields in the archive match namespace and
// serverID of the authenticated client. This check makes sure nodes can
// only submit recordings for themselves.
serverID, err := s.getServerID(r)
if err != nil {
return nil, trace.Wrap(err)
}
err = events.ValidateArchive(recording, serverID)
if err != nil {
log.Warnf("Rejecting session recording from %v: %v. System may be under attack, a "+
"node is attempting to submit events for an identity other than its own.",
serverID, err)
return nil, trace.BadParameter("failed to validate archive")
}
if err = auth.UploadSessionRecording(events.SessionRecording{
SessionID: session.ID(sid),
Namespace: namespace,
Recording: &buf,
}); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 20:23:58 +00:00
// HTTP GET /:version/sessions/:id/stream?offset=x&bytes=y
// Query parameters:
// "offset" : bytes from the beginning
// "bytes" : number of bytes to read (it won't return more than 512Kb)
2016-12-29 23:46:19 +00:00
func (s *APIServer) getSessionChunk(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
sid, err := session.ParseID(p.ByName("id"))
if err != nil {
2016-12-13 03:26:59 +00:00
return nil, trace.BadParameter("missing parameter id")
}
2016-12-13 03:26:59 +00:00
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
2016-12-13 03:26:59 +00:00
// "offset bytes" query param
offsetBytes, err := strconv.Atoi(r.URL.Query().Get("offset"))
if err != nil || offsetBytes < 0 {
offsetBytes = 0
}
// "max bytes" query param
max, err := strconv.Atoi(r.URL.Query().Get("bytes"))
if err != nil || offsetBytes < 0 {
offsetBytes = 0
}
2016-12-19 17:48:55 +00:00
log.Debugf("apiserver.GetSessionChunk(%v, %v, offset=%d)", namespace, *sid, offsetBytes)
w.Header().Set("Content-Type", "text/plain")
2016-12-13 03:26:59 +00:00
buffer, err := auth.GetSessionChunk(namespace, *sid, offsetBytes, max)
if err != nil {
2016-12-13 03:26:59 +00:00
return nil, trace.Wrap(err)
}
if _, err = w.Write(buffer); err != nil {
2016-12-13 03:26:59 +00:00
return nil, trace.Wrap(err)
}
w.Header().Set("Content-Type", "application/octet-stream")
2016-12-13 03:26:59 +00:00
return nil, nil
}
2016-12-29 20:23:58 +00:00
// HTTP GET /:version/sessions/:id/events?maxage=n
// Query:
// 'after' : cursor value to return events newer than N. Defaults to 0, (return all)
2016-12-29 23:46:19 +00:00
func (s *APIServer) getSessionEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
sid, err := session.ParseID(p.ByName("id"))
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-13 03:26:59 +00:00
namespace := p.ByName("namespace")
if !services.IsValidNamespace(namespace) {
return nil, trace.BadParameter("invalid namespace %q", namespace)
}
afterN, err := strconv.Atoi(r.URL.Query().Get("after"))
if err != nil {
afterN = 0
}
External events and sessions storage. Updates #1755 Design ------ This commit adds support for pluggable events and sessions recordings and adds several plugins. In case if external sessions recording storage is used, nodes or proxies depending on configuration store the session recordings locally and then upload the recordings in the background. Non-print session events are always sent to the remote auth server as usual. In case if remote events storage is used, auth servers download recordings from it during playbacks. DynamoDB event backend ---------------------- Transient DynamoDB backend is added for events storage. Events are stored with default TTL of 1 year. External lambda functions should be used to forward events from DynamoDB. Parameter audit_table_name in storage section turns on dynamodb backend. The table will be auto created. S3 sessions backend ------------------- If audit_sessions_uri is specified to s3://bucket-name node or proxy depending on recording mode will start uploading the recorded sessions to the bucket. If the bucket does not exist, teleport will attempt to create a bucket with versioning and encryption turned on by default. Teleport will turn on bucket-side encryption for the tarballs using aws:kms key. File sessions backend --------------------- If audit_sessions_uri is specified to file:///folder teleport will start writing tarballs to this folder instead of sending records to the file server. This is helpful for plugin writers who can use fuse or NFS mounted storage to handle the data. Working dynamic configuration.
2018-03-04 02:26:44 +00:00
includePrintEvents, err := strconv.ParseBool(r.URL.Query().Get("print"))
if err != nil {
includePrintEvents = false
}
return auth.GetSessionEvents(namespace, *sid, afterN, includePrintEvents)
2016-12-13 03:26:59 +00:00
}
type upsertNamespaceReq struct {
Namespace services.Namespace `json:"namespace"`
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
var req *upsertNamespaceReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
if err := auth.UpsertNamespace(req.Namespace); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getNamespaces(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
namespaces, err := auth.GetNamespaces()
if err != nil {
return nil, trace.Wrap(err)
}
return namespaces, nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
name := p.ByName("namespace")
if !services.IsValidNamespace(name) {
return nil, trace.BadParameter("invalid namespace %q", name)
}
2016-12-13 03:26:59 +00:00
namespace, err := auth.GetNamespace(name)
if err != nil {
return nil, trace.Wrap(err)
}
return namespace, nil
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-13 03:26:59 +00:00
name := p.ByName("namespace")
if !services.IsValidNamespace(name) {
return nil, trace.BadParameter("invalid namespace %q", name)
}
2016-12-13 03:26:59 +00:00
err := auth.DeleteNamespace(name)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2016-12-29 19:36:57 +00:00
type upsertRoleRawReq struct {
2016-12-17 03:33:18 +00:00
Role json.RawMessage `json:"role"`
2016-12-13 03:26:59 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) upsertRole(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-29 19:36:57 +00:00
var req *upsertRoleRawReq
2016-12-13 03:26:59 +00:00
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
role, err := services.UnmarshalRole(req.Role)
2016-12-13 03:26:59 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
if err = services.ValidateRole(role); err != nil {
return nil, trace.Wrap(err)
}
err = auth.UpsertRole(r.Context(), role)
2016-12-13 03:26:59 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-23 03:06:07 +00:00
return message(fmt.Sprintf("'%v' role upserted", role.GetName())), nil
2016-12-13 03:26:59 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getRole(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
role, err := auth.GetRole(r.Context(), p.ByName("role"))
2016-12-13 03:26:59 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalRole(role, services.WithVersion(version), services.PreserveResourceID()))
2016-12-13 03:26:59 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) getRoles(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
roles, err := auth.GetRoles(r.Context())
2016-12-13 03:26:59 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
out := make([]json.RawMessage, len(roles))
2016-12-29 19:36:57 +00:00
for i, role := range roles {
raw, err := services.MarshalRole(role, services.WithVersion(version), services.PreserveResourceID())
2016-12-29 19:36:57 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
2016-12-30 02:57:20 +00:00
out[i] = raw
2016-12-29 19:36:57 +00:00
}
return out, nil
2016-12-13 03:26:59 +00:00
}
2016-12-29 23:46:19 +00:00
func (s *APIServer) deleteRole(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2016-12-30 02:57:20 +00:00
role := p.ByName("role")
if err := auth.DeleteRole(r.Context(), role); err != nil {
2016-12-13 03:26:59 +00:00
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("role %q deleted", role)), nil
}
func (s *APIServer) getClusterConfig(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
cc, err := auth.GetClusterConfig()
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalClusterConfig(cc, services.WithVersion(version), services.PreserveResourceID()))
}
type setClusterConfigReq struct {
ClusterConfig json.RawMessage `json:"cluster_config"`
}
func (s *APIServer) setClusterConfig(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req setClusterConfigReq
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
cc, err := services.UnmarshalClusterConfig(req.ClusterConfig)
if err != nil {
return nil, trace.Wrap(err)
}
err = auth.SetClusterConfig(cc)
if err != nil {
return nil, trace.Wrap(err)
}
return message("cluster config set"), nil
}
2017-07-28 18:37:12 +00:00
func (s *APIServer) getClusterName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
cn, err := auth.GetClusterName()
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalClusterName(cn, services.WithVersion(version), services.PreserveResourceID()))
}
2017-07-28 18:37:12 +00:00
type setClusterNameReq struct {
ClusterName json.RawMessage `json:"cluster_name"`
}
2017-07-28 18:37:12 +00:00
func (s *APIServer) setClusterName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2017-08-09 18:06:00 +00:00
var req setClusterNameReq
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
cn, err := services.UnmarshalClusterName(req.ClusterName)
if err != nil {
return nil, trace.Wrap(err)
}
2017-07-28 18:37:12 +00:00
err = auth.SetClusterName(cn)
if err != nil {
return nil, trace.Wrap(err)
}
2017-07-28 18:37:12 +00:00
return message(fmt.Sprintf("cluster name set: %+v", cn)), nil
}
func (s *APIServer) getStaticTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
st, err := auth.GetStaticTokens()
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalStaticTokens(st, services.WithVersion(version), services.PreserveResourceID()))
}
func (s *APIServer) deleteStaticTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteStaticTokens()
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
2017-07-28 18:37:12 +00:00
}
type setStaticTokensReq struct {
StaticTokens json.RawMessage `json:"static_tokens"`
}
func (s *APIServer) setStaticTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
2017-08-09 18:06:00 +00:00
var req setStaticTokensReq
2017-07-28 18:37:12 +00:00
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
st, err := services.UnmarshalStaticTokens(req.StaticTokens)
2017-07-28 18:37:12 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
err = auth.SetStaticTokens(st)
if err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("static tokens set: %+v", st)), nil
}
2017-07-28 18:37:12 +00:00
func (s *APIServer) getClusterAuthPreference(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
cap, err := auth.GetAuthPreference()
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalAuthPreference(cap, services.WithVersion(version), services.PreserveResourceID()))
}
2017-07-28 18:37:12 +00:00
type setClusterAuthPreferenceReq struct {
ClusterAuthPreference json.RawMessage `json:"cluster_auth_prerference"`
}
2017-07-28 18:37:12 +00:00
func (s *APIServer) setClusterAuthPreference(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *setClusterAuthPreferenceReq
err := httplib.ReadJSON(r, &req)
if err != nil {
return nil, trace.Wrap(err)
}
cap, err := services.UnmarshalAuthPreference(req.ClusterAuthPreference)
if err != nil {
return nil, trace.Wrap(err)
}
cap.SetOrigin(types.OriginDynamic)
2017-07-28 18:37:12 +00:00
err = auth.SetAuthPreference(cap)
if err != nil {
return nil, trace.Wrap(err)
}
return message(fmt.Sprintf("cluster authentication preference set: %+v", cap)), nil
}
2017-10-06 00:29:31 +00:00
type upsertTunnelConnectionRawReq struct {
TunnelConnection json.RawMessage `json:"tunnel_connection"`
}
// upsertTunnelConnection updates or inserts tunnel connection
func (s *APIServer) upsertTunnelConnection(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req upsertTunnelConnectionRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
conn, err := services.UnmarshalTunnelConnection(req.TunnelConnection)
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.UpsertTunnelConnection(conn); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// getTunnelConnections returns a list of tunnel connections from a cluster
func (s *APIServer) getTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
conns, err := auth.GetTunnelConnections(p.ByName("cluster"))
if err != nil {
return nil, trace.Wrap(err)
}
items := make([]json.RawMessage, len(conns))
for i, conn := range conns {
data, err := services.MarshalTunnelConnection(conn, services.WithVersion(version), services.PreserveResourceID())
2017-10-06 00:29:31 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
// getAllTunnelConnections returns a list of tunnel connections from a cluster
func (s *APIServer) getAllTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
conns, err := auth.GetAllTunnelConnections()
if err != nil {
return nil, trace.Wrap(err)
}
items := make([]json.RawMessage, len(conns))
for i, conn := range conns {
data, err := services.MarshalTunnelConnection(conn, services.WithVersion(version), services.PreserveResourceID())
2017-10-06 00:29:31 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
2017-10-12 23:51:18 +00:00
// deleteTunnelConnection deletes tunnel connection by name
func (s *APIServer) deleteTunnelConnection(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteTunnelConnection(p.ByName("cluster"), p.ByName("conn"))
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
2017-10-06 00:29:31 +00:00
// deleteTunnelConnections deletes all tunnel connections for cluster
func (s *APIServer) deleteTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteTunnelConnections(p.ByName("cluster"))
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// deleteAllTunnelConnections deletes all tunnel connections
func (s *APIServer) deleteAllTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteAllTunnelConnections()
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
type createRemoteClusterRawReq struct {
// RemoteCluster is marshalled remote cluster resource
RemoteCluster json.RawMessage `json:"remote_cluster"`
}
// createRemoteCluster creates remote cluster
func (s *APIServer) createRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req createRemoteClusterRawReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
conn, err := services.UnmarshalRemoteCluster(req.RemoteCluster)
if err != nil {
return nil, trace.Wrap(err)
}
if err := auth.CreateRemoteCluster(conn); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// getRemoteClusters returns a list of remote clusters
func (s *APIServer) getRemoteClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
clusters, err := auth.GetRemoteClusters()
if err != nil {
return nil, trace.Wrap(err)
}
items := make([]json.RawMessage, len(clusters))
for i, cluster := range clusters {
data, err := services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID())
if err != nil {
return nil, trace.Wrap(err)
}
items[i] = data
}
return items, nil
}
// getRemoteCluster returns a remote cluster by name
func (s *APIServer) getRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
cluster, err := auth.GetRemoteCluster(p.ByName("cluster"))
if err != nil {
return nil, trace.Wrap(err)
}
return rawMessage(services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID()))
}
// deleteRemoteCluster deletes remote cluster by name
func (s *APIServer) deleteRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteRemoteCluster(p.ByName("cluster"))
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
// deleteAllRemoteClusters deletes all remote clusters
func (s *APIServer) deleteAllRemoteClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
err := auth.DeleteAllRemoteClusters()
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) processKubeCSR(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req KubeCSR
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
re, err := auth.ProcessKubeCSR(req)
if err != nil {
return nil, trace.Wrap(err)
}
return re, nil
}
// getServerID returns the ID of the connected client.
func (s *APIServer) getServerID(r *http.Request) (string, error) {
role, ok := r.Context().Value(ContextUser).(BuiltinRole)
if !ok {
return "", trace.BadParameter("invalid role %T", r.Context().Value(ContextUser))
}
clusterName, err := s.AuthServer.GetDomainName()
if err != nil {
return "", trace.Wrap(err)
}
// The username extracted from the node's identity (x.509 certificate)
// is expected to consist of "<server-id>.<cluster-name>" so strip the
// cluster name suffix to get the server id.
//
// Note that as of right now Teleport expects server id to be a uuid4
// but older Gravity clusters used to override it with strings like
// "192_168_1_1.<cluster-name>" so this code can't rely on it being
// uuid4 to account for clusters upgraded from older versions.
return strings.TrimSuffix(role.Username, "."+clusterName), nil
}
2015-03-02 20:11:23 +00:00
func message(msg string) map[string]interface{} {
return map[string]interface{}{"message": msg}
}
Fix staticcheck findings in lib/auth Fixed findings: ``` lib/auth/clt.go:294:12: SA1019: grpc.WithDialer is deprecated: use WithContextDialer instead. Will be supported throughout 1.x. (staticcheck) dialer := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {ion error: desc = "transport: Erro ^ lib/auth/clt.go:1462:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)site-A Type:proxy Addr:{Addr:local if grpc.Code(err) != codes.Unimplemented { ^ lib/auth/clt.go:1500:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)rsetunnel/agentpool.go:312 if grpc.Code(err) != codes.Unimplemented { ^ lib/auth/saml.go:294:33: SA5011: possible nil pointer dereference (staticcheck) events.EventUser: re.auth.Username, ^ lib/auth/auth_test.go:119:2: SA4006: this value of `ws` is never used (staticcheck)ats. stats:map[connected:1 connect ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{ ^ lib/auth/auth_test.go:160:2: SA4006: this value of `ws` is never used (staticcheck)ver.go:425 ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{ ^ lib/auth/auth_test.go:243:2: SA4006: this value of `roles` is never used (staticcheck)es that discovery protocol recover roles, err = s.a.ValidateToken(tok) ^ lib/auth/password_test.go:228:2: SA4006: this value of `err` is never used (staticcheck)necting -> connected. leaseID:5 ta token, err := s.a.CreateResetPasswordToken(context.TODO(), CreateResetPasswordTokenRequest{7.0.0.1:46150. reversetunnel/srv.g ^ lib/auth/tls_test.go:109:2: SA4006: this value of `err` is never used (staticcheck)ry(c *check.C) { err = s.server.AuthServer.Trust(remoteServer, nil) ^ lib/auth/tls_test.go:195:2: SA4006: this value of `err` is never used (staticcheck)meout=10000&_sync=OFF, poll stream err = s.server.AuthServer.Trust(remoteServer, nil) ^ lib/auth/tls_test.go:360:2: SA4006: this value of `newProxy` is never used (staticcheck) be received and the connection ad newProxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))o/src/github.com/gravitational/tel ^ lib/auth/tls_test.go:388:2: SA4006: this value of `err` is never used (staticcheck)o/src/github.com/gravitational/tel err = s.server.Auth().autoRotateCertAuthorities() ^ lib/auth/tls_test.go:1074:2: SA4006: this value of `err` is never used (staticcheck)uest, we will recover session id a err = os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)reversetunnel/agent.go:458 ^ lib/auth/tls_test.go:1263:2: SA4006: this value of `err` is never used (staticcheck)quests channel. leaseID:5 target:l err = clt.UpsertPassword(user, pass) ^ lib/auth/tls_test.go:1691:2: SA4006: this value of `err` is never used (staticcheck) userCerts, err = adminClient.GenerateUserCerts(context.TODO(), proto.UserCertsRequest{et:localhost:20088 reversetunnel/t ^ lib/auth/auth_with_roles.go:860:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) return context.WithValue(ctx, events.AccessRequestUpdateBy, user)ts is received, if it's been longe ^ lib/auth/auth_with_roles.go:876:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) return context.WithValue(ctx, events.AccessRequestDelegator, delegator)ime = f.process.Clock.Now() ^ lib/auth/middleware.go:316:69: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) requestWithContext := r.WithContext(context.WithValue(baseContext, ContextUser, user))rocess.Infof("Teleport has recover ^ion to @remote-auth-server [] in r lib/auth/apiserver.go:285:42: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) ctx := context.WithValue(r.Context(), contextParams, p)et:localhost:20088 reversetunnel/t ^ ```
2020-04-27 17:48:36 +00:00
type contextParamsKey string
// contextParams is the name of of the key that holds httprouter.Params in
// a context.
Fix staticcheck findings in lib/auth Fixed findings: ``` lib/auth/clt.go:294:12: SA1019: grpc.WithDialer is deprecated: use WithContextDialer instead. Will be supported throughout 1.x. (staticcheck) dialer := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {ion error: desc = "transport: Erro ^ lib/auth/clt.go:1462:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)site-A Type:proxy Addr:{Addr:local if grpc.Code(err) != codes.Unimplemented { ^ lib/auth/clt.go:1500:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)rsetunnel/agentpool.go:312 if grpc.Code(err) != codes.Unimplemented { ^ lib/auth/saml.go:294:33: SA5011: possible nil pointer dereference (staticcheck) events.EventUser: re.auth.Username, ^ lib/auth/auth_test.go:119:2: SA4006: this value of `ws` is never used (staticcheck)ats. stats:map[connected:1 connect ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{ ^ lib/auth/auth_test.go:160:2: SA4006: this value of `ws` is never used (staticcheck)ver.go:425 ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{ ^ lib/auth/auth_test.go:243:2: SA4006: this value of `roles` is never used (staticcheck)es that discovery protocol recover roles, err = s.a.ValidateToken(tok) ^ lib/auth/password_test.go:228:2: SA4006: this value of `err` is never used (staticcheck)necting -> connected. leaseID:5 ta token, err := s.a.CreateResetPasswordToken(context.TODO(), CreateResetPasswordTokenRequest{7.0.0.1:46150. reversetunnel/srv.g ^ lib/auth/tls_test.go:109:2: SA4006: this value of `err` is never used (staticcheck)ry(c *check.C) { err = s.server.AuthServer.Trust(remoteServer, nil) ^ lib/auth/tls_test.go:195:2: SA4006: this value of `err` is never used (staticcheck)meout=10000&_sync=OFF, poll stream err = s.server.AuthServer.Trust(remoteServer, nil) ^ lib/auth/tls_test.go:360:2: SA4006: this value of `newProxy` is never used (staticcheck) be received and the connection ad newProxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))o/src/github.com/gravitational/tel ^ lib/auth/tls_test.go:388:2: SA4006: this value of `err` is never used (staticcheck)o/src/github.com/gravitational/tel err = s.server.Auth().autoRotateCertAuthorities() ^ lib/auth/tls_test.go:1074:2: SA4006: this value of `err` is never used (staticcheck)uest, we will recover session id a err = os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)reversetunnel/agent.go:458 ^ lib/auth/tls_test.go:1263:2: SA4006: this value of `err` is never used (staticcheck)quests channel. leaseID:5 target:l err = clt.UpsertPassword(user, pass) ^ lib/auth/tls_test.go:1691:2: SA4006: this value of `err` is never used (staticcheck) userCerts, err = adminClient.GenerateUserCerts(context.TODO(), proto.UserCertsRequest{et:localhost:20088 reversetunnel/t ^ lib/auth/auth_with_roles.go:860:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) return context.WithValue(ctx, events.AccessRequestUpdateBy, user)ts is received, if it's been longe ^ lib/auth/auth_with_roles.go:876:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) return context.WithValue(ctx, events.AccessRequestDelegator, delegator)ime = f.process.Clock.Now() ^ lib/auth/middleware.go:316:69: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) requestWithContext := r.WithContext(context.WithValue(baseContext, ContextUser, user))rocess.Infof("Teleport has recover ^ion to @remote-auth-server [] in r lib/auth/apiserver.go:285:42: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) ctx := context.WithValue(r.Context(), contextParams, p)et:localhost:20088 reversetunnel/t ^ ```
2020-04-27 17:48:36 +00:00
const contextParams contextParamsKey = "params"