2015-10-31 18:56:49 +00:00
|
|
|
/*
|
2018-12-12 00:22:44 +00:00
|
|
|
Copyright 2015-2019 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.
|
|
|
|
*/
|
2016-02-23 20:03:34 +00:00
|
|
|
|
2015-03-02 20:11:23 +00:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2016-04-05 16:58:16 +00:00
|
|
|
"encoding/json"
|
2015-03-02 20:11:23 +00:00
|
|
|
"fmt"
|
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"
|
2016-04-27 05:55:06 +00:00
|
|
|
"strconv"
|
2015-03-02 20:11:23 +00:00
|
|
|
"time"
|
|
|
|
|
2015-08-25 17:54:16 +00:00
|
|
|
"github.com/gravitational/teleport"
|
2016-04-25 06:32:44 +00:00
|
|
|
"github.com/gravitational/teleport/lib/events"
|
2016-02-23 20:03:34 +00:00
|
|
|
"github.com/gravitational/teleport/lib/httplib"
|
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"
|
2016-03-02 01:18:06 +00:00
|
|
|
"github.com/gravitational/teleport/lib/utils"
|
2015-07-09 03:57:34 +00:00
|
|
|
|
2018-03-29 20:47:53 +00:00
|
|
|
"github.com/gravitational/form"
|
2016-01-21 18:18:59 +00:00
|
|
|
"github.com/gravitational/trace"
|
2017-11-22 01:35:58 +00:00
|
|
|
"github.com/jonboulle/clockwork"
|
2016-01-20 15:52:25 +00:00
|
|
|
"github.com/julienschmidt/httprouter"
|
2016-10-14 06:51:16 +00:00
|
|
|
"github.com/tstranex/u2f"
|
2015-03-02 20:11:23 +00:00
|
|
|
)
|
|
|
|
|
2016-05-30 08:27:33 +00:00
|
|
|
type APIConfig struct {
|
2016-12-12 00:52:22 +00:00
|
|
|
AuthServer *AuthServer
|
|
|
|
SessionService session.Service
|
|
|
|
AuditLog events.IAuditLog
|
2017-01-31 04:18:15 +00:00
|
|
|
Authorizer Authorizer
|
2016-05-30 08:27:33 +00:00
|
|
|
}
|
|
|
|
|
2016-05-30 18:54:22 +00:00
|
|
|
// APIServer implements http API server for AuthServer interface
|
|
|
|
type APIServer struct {
|
2016-12-12 00:52:22 +00:00
|
|
|
APIConfig
|
2016-05-30 18:54:22 +00:00
|
|
|
httprouter.Router
|
2017-04-07 23:51:31 +00:00
|
|
|
clockwork.Clock
|
2016-05-30 08:27:33 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
// NewAPIServer returns a new instance of APIServer HTTP handler
|
2016-12-13 22:20:52 +00:00
|
|
|
func NewAPIServer(config *APIConfig) http.Handler {
|
2016-05-30 18:54:22 +00:00
|
|
|
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()
|
|
|
|
|
2018-05-19 23:58:14 +00:00
|
|
|
// Kubernetes extensions
|
|
|
|
srv.POST("/:version/kube/csr", srv.withAuth(srv.processKubeCSR))
|
|
|
|
|
2016-02-16 17:36:02 +00:00
|
|
|
// Operations on certificate authorities
|
2016-12-29 20:23:58 +00:00
|
|
|
srv.GET("/:version/domain", srv.withAuth(srv.getDomainName))
|
2018-10-03 19:35:57 +00:00
|
|
|
srv.GET("/:version/cacert", srv.withAuth(srv.getClusterCACert))
|
2018-04-08 21:37:33 +00:00
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
srv.POST("/:version/authorities/:type", srv.withAuth(srv.upsertCertAuthority))
|
2018-04-08 21:37:33 +00:00
|
|
|
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))
|
2018-05-04 00:12:52 +00:00
|
|
|
srv.POST("/:version/ca/user/certs", srv.withAuth(srv.generateUserCert))
|
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))
|
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))
|
2015-03-19 04:13:56 +00:00
|
|
|
|
|
|
|
// 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.withAuth(srv.checkPassword))
|
|
|
|
srv.POST("/:version/users/:user/web/sessions", srv.withAuth(srv.createWebSession))
|
2017-11-25 01:09:11 +00:00
|
|
|
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))
|
|
|
|
srv.GET("/:version/signuptokens/:token", srv.withAuth(srv.getSignupTokenData))
|
|
|
|
srv.POST("/:version/signuptokens/users", srv.withAuth(srv.createUserWithToken))
|
|
|
|
srv.POST("/:version/signuptokens", srv.withAuth(srv.createSignupToken))
|
2015-03-19 04:13:56 +00:00
|
|
|
|
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))
|
2018-06-29 18:39:06 +00:00
|
|
|
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))
|
2018-12-12 00:22:44 +00:00
|
|
|
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))
|
2018-12-12 00:22:44 +00:00
|
|
|
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
|
|
|
|
2017-11-25 01:09:11 +00:00
|
|
|
// Server Credentials
|
|
|
|
srv.POST("/:version/server/credentials", srv.withAuth(srv.generateServerKeys))
|
|
|
|
|
2017-12-28 02:51:46 +00:00
|
|
|
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))
|
|
|
|
|
2016-03-18 01:42:04 +00:00
|
|
|
// 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))
|
2016-03-18 01:42:04 +00:00
|
|
|
|
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))
|
|
|
|
|
2015-05-07 03:10:44 +00:00
|
|
|
// 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))
|
2016-02-23 20:03:34 +00:00
|
|
|
|
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.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))
|
2018-03-29 20:47:53 +00:00
|
|
|
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
|
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
|
2017-10-24 20:52:29 +00:00
|
|
|
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))
|
2018-12-12 00:22:44 +00:00
|
|
|
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))
|
2017-02-14 02:29:27 +00:00
|
|
|
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))
|
|
|
|
|
2017-01-17 19:24:17 +00:00
|
|
|
// U2F
|
2016-12-29 20:23:58 +00:00
|
|
|
srv.GET("/:version/u2f/signuptokens/:token", srv.withAuth(srv.getSignupU2FRegisterRequest))
|
|
|
|
srv.POST("/:version/u2f/users", srv.withAuth(srv.createUserWithU2FToken))
|
|
|
|
srv.POST("/:version/u2f/users/:user/sign", srv.withAuth(srv.u2fSignRequest))
|
|
|
|
srv.GET("/:version/u2f/appid", srv.withAuth(srv.getU2FAppID))
|
2016-11-09 06:49:59 +00:00
|
|
|
|
2016-05-17 01:55:56 +00:00
|
|
|
// Provisioning tokens
|
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))
|
2016-05-17 01:55:56 +00:00
|
|
|
|
2016-04-24 22:35:33 +00:00
|
|
|
// 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))
|
2016-04-24 22:35:33 +00:00
|
|
|
|
2017-11-22 01:35:58 +00:00
|
|
|
if plugin := GetPlugin(); plugin != nil {
|
|
|
|
plugin.AddHandlers(&srv)
|
|
|
|
}
|
|
|
|
|
2016-12-13 22:20:52 +00:00
|
|
|
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"),
|
|
|
|
)
|
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) {
|
2018-05-19 23:58:14 +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 {
|
2017-07-02 22:26:09 +00:00
|
|
|
// 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")
|
2017-11-25 01:09:11 +00:00
|
|
|
} else if trace.IsAccessDenied(err) {
|
|
|
|
// don't print stack trace, just log the warning
|
|
|
|
log.Warn(err)
|
|
|
|
} else {
|
|
|
|
log.Warn(trace.DebugReport(err))
|
2017-07-02 22:26:09 +00:00
|
|
|
}
|
2017-11-25 01:09:11 +00:00
|
|
|
|
2017-01-31 05:43:01 +00:00
|
|
|
return nil, trace.AccessDenied(accessDeniedMsg + "[00]")
|
2016-12-12 00:52:22 +00:00
|
|
|
}
|
|
|
|
auth := &AuthWithRoles{
|
|
|
|
authServer: s.AuthServer,
|
2017-05-19 17:06:48 +00:00
|
|
|
user: authContext.User,
|
2017-01-31 04:18:15 +00:00
|
|
|
checker: authContext.Checker,
|
2016-12-12 00:52:22 +00:00
|
|
|
sessions: s.SessionService,
|
2017-11-22 01:35:58 +00:00
|
|
|
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
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-12-29 19:36:57 +00:00
|
|
|
type upsertServerRawReq struct {
|
|
|
|
Server json.RawMessage `json:"server"`
|
2016-02-23 20:03:34 +00:00
|
|
|
TTL time.Duration `json:"ttl"`
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2016-12-13 22:20:52 +00:00
|
|
|
// upsertServer is a common utility function
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) upsertServer(auth ClientI, role teleport.Role, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
2016-12-29 19:36:57 +00:00
|
|
|
var req upsertServerRawReq
|
2016-02-23 20:03:34 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
server, err := services.GetServerMarshaler().UnmarshalServer(req.Server, kind)
|
|
|
|
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.SetTTL(s, req.TTL)
|
|
|
|
}
|
2016-03-12 04:09:40 +00:00
|
|
|
switch role {
|
|
|
|
case teleport.RoleNode:
|
2017-04-26 20:31:27 +00:00
|
|
|
namespace := p.ByName("namespace")
|
|
|
|
if !services.IsValidNamespace(namespace) {
|
|
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
|
|
}
|
|
|
|
server.SetNamespace(namespace)
|
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
|
|
|
handle, err := auth.UpsertNode(server)
|
|
|
|
if err != nil {
|
2016-03-12 04:09:40 +00:00
|
|
|
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
|
2016-03-12 04:09:40 +00:00
|
|
|
case teleport.RoleAuth:
|
2017-04-07 23:51:31 +00:00
|
|
|
if err := auth.UpsertAuthServer(server); err != nil {
|
2016-03-12 04:09:40 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
case teleport.RoleProxy:
|
2017-04-07 23:51:31 +00:00
|
|
|
if err := auth.UpsertProxy(server); err != nil {
|
2016-03-12 04:09:40 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return message("ok"), nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
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.KeepAliveNode(r.Context(), handle); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return message("ok"), nil
|
|
|
|
}
|
|
|
|
|
2018-06-29 18:39:06 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
if !services.IsValidNamespace(req.Namespace) {
|
|
|
|
return nil, trace.BadParameter("invalid namespace %q", req.Namespace)
|
|
|
|
}
|
|
|
|
|
|
|
|
nodes, err := services.GetServerMarshaler().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
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// 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) {
|
|
|
|
return s.upsertServer(auth, teleport.RoleNode, w, r, p, version)
|
2016-03-12 04:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2017-04-26 20:31:27 +00:00
|
|
|
namespace := p.ByName("namespace")
|
|
|
|
if !services.IsValidNamespace(namespace) {
|
|
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
|
|
}
|
2018-06-29 18:39:06 +00:00
|
|
|
skipValidation, _, err := httplib.ParseBool(r.URL.Query(), "skip_validation")
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
var opts []services.MarshalOption
|
|
|
|
if skipValidation {
|
|
|
|
opts = append(opts, services.SkipValidation())
|
|
|
|
}
|
|
|
|
|
|
|
|
servers, err := auth.GetNodes(namespace, opts...)
|
2016-03-12 04:09:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-12-29 23:46:19 +00:00
|
|
|
return marshalServers(servers, version)
|
2016-03-12 04:09:40 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
err := auth.DeleteAllNodes(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")
|
|
|
|
}
|
|
|
|
err := auth.DeleteNode(namespace, name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return message("ok"), nil
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// 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) {
|
|
|
|
return s.upsertServer(auth, teleport.RoleProxy, w, r, p, version)
|
2016-03-12 04:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-12-29 23:46:19 +00:00
|
|
|
return marshalServers(servers, version)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// 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) {
|
|
|
|
return s.upsertServer(auth, teleport.RoleAuth, w, r, p, version)
|
2016-03-12 04:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
data, err := services.GetServerMarshaler().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
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 19:36:57 +00:00
|
|
|
type upsertReverseTunnelRawReq struct {
|
|
|
|
ReverseTunnel json.RawMessage `json:"reverse_tunnel"`
|
|
|
|
TTL time.Duration `json:"ttl"`
|
2016-03-18 01:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2016-03-18 01:42:04 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-12-29 19:36:57 +00:00
|
|
|
tun, err := services.GetReverseTunnelMarshaler().UnmarshalReverseTunnel(req.ReverseTunnel)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-11-25 01:09:11 +00:00
|
|
|
if req.TTL != 0 {
|
|
|
|
tun.SetTTL(s, req.TTL)
|
|
|
|
}
|
2017-04-07 23:51:31 +00:00
|
|
|
if err := auth.UpsertReverseTunnel(tun); err != nil {
|
2016-03-18 01:42:04 +00:00
|
|
|
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()
|
2016-03-18 01:42:04 +00:00
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
data, err := services.GetReverseTunnelMarshaler().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
|
2016-03-18 01:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2016-03-18 01:42:04 +00:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
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.GetTrustedClusterMarshaler().Unmarshal(req.TrustedCluster)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-01-11 22:09:30 +00:00
|
|
|
out, err := auth.UpsertTrustedCluster(trustedCluster)
|
2017-03-02 19:50:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetTrustedClusterMarshaler().Marshal(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(p.ByName("name"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *APIServer) getTrustedClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
|
|
return auth.GetTrustedClusters()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *APIServer) deleteTrustedCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
|
|
err := auth.DeleteTrustedCluster(p.ByName("name"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return message("ok"), nil
|
|
|
|
}
|
|
|
|
|
2016-05-17 01:55:56 +00:00
|
|
|
// 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) {
|
2016-12-13 03:26:59 +00:00
|
|
|
tokens, err := auth.GetTokens()
|
2016-05-17 03:10:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return services.ProvisionTokensToV1(tokens), nil
|
2016-05-17 01:55:56 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 23:48:36 +00:00
|
|
|
// 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) {
|
2016-12-14 23:48:36 +00:00
|
|
|
token, err := auth.GetToken(p.ByName("token"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
|
2016-05-17 01:55:56 +00:00
|
|
|
// 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) {
|
2016-05-17 01:55:56 +00:00
|
|
|
token := p.ByName("token")
|
2016-12-13 03:26:59 +00:00
|
|
|
if err := auth.DeleteToken(token); err != nil {
|
2016-05-17 03:10:59 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return message(fmt.Sprintf("Token %v deleted", token)), nil
|
2016-05-17 01:55:56 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2016-12-30 02:57:20 +00:00
|
|
|
user, sid := p.ByName("user"), p.ByName("sid")
|
2016-12-13 03:26:59 +00:00
|
|
|
err := auth.DeleteWebSession(user, sid)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return message(fmt.Sprintf("session '%v' for user '%v' deleted", sid, user)), nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 02:55:51 +00:00
|
|
|
// sessionV1 is a V1 style web session, used in legacy v1 API
|
|
|
|
type sessionV1 struct {
|
|
|
|
// ID is a session ID
|
|
|
|
ID string `json:"id"`
|
|
|
|
// Username is a user this session belongs to
|
|
|
|
Username string `json:"username"`
|
|
|
|
// ExpiresAt is an optional expiry time, if set
|
|
|
|
// that means this web session and all derived web sessions
|
|
|
|
// can not continue after this time, used in OIDC use case
|
|
|
|
// when expiry is set by external identity provider, so user
|
|
|
|
// has to relogin (or later on we'd need to refresh the token)
|
|
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
|
|
// WS is a private keypair used for signing requests
|
|
|
|
WS services.WebSessionV1 `json:"web"`
|
|
|
|
}
|
|
|
|
|
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")
|
2016-12-13 03:26:59 +00:00
|
|
|
sess, err := auth.GetWebSessionInfo(user, sid)
|
2015-03-19 04:13:56 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2017-02-11 02:55:51 +00:00
|
|
|
if version == services.V1 {
|
|
|
|
return &sessionV1{
|
|
|
|
ID: sess.GetName(),
|
|
|
|
Username: sess.GetUser(),
|
|
|
|
ExpiresAt: sess.GetExpiryTime(),
|
|
|
|
WS: *(sess.V1()),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
return rawMessage(services.GetWebSessionMarshaler().MarshalWebSession(sess, services.WithVersion(version)))
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
cert, err := auth.GenerateUserCert(req.Key, req.User, req.TTL, certificateFormat)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return string(cert), nil
|
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type signInReq struct {
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
2015-03-19 04:13:56 +00:00
|
|
|
|
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) {
|
2016-10-14 06:51:16 +00:00
|
|
|
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")
|
2016-10-14 06:51:16 +00:00
|
|
|
pass := []byte(req.Password)
|
2016-12-15 00:36:55 +00:00
|
|
|
u2fSignReq, err := auth.GetU2FSignRequest(user, pass)
|
2016-10-14 06:51:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return u2fSignReq, nil
|
|
|
|
}
|
|
|
|
|
2016-02-26 22:57:51 +00:00
|
|
|
type createWebSessionReq struct {
|
|
|
|
PrevSessionID string `json:"prev_session_id"`
|
|
|
|
}
|
|
|
|
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) createWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
2016-02-26 22:57:51 +00:00
|
|
|
var req *createWebSessionReq
|
|
|
|
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")
|
2016-04-10 20:29:32 +00:00
|
|
|
if req.PrevSessionID != "" {
|
2016-12-13 03:26:59 +00:00
|
|
|
sess, err := auth.ExtendWebSession(user, req.PrevSessionID)
|
2016-04-10 20:29:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return sess, nil
|
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
sess, err := auth.CreateWebSession(user)
|
2016-02-26 22:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-02-11 02:55:51 +00:00
|
|
|
return rawMessage(services.GetWebSessionMarshaler().MarshalWebSession(sess, services.WithVersion(version)))
|
2016-02-26 22:57:51 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 01:09:11 +00:00
|
|
|
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.GetWebSessionMarshaler().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)
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := auth.ChangePassword(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return message(fmt.Sprintf("password has been changed for user %q", req.User)), nil
|
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type upsertPasswordReq struct {
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
2015-03-19 04:13:56 +00:00
|
|
|
|
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) {
|
2016-02-23 20:03:34 +00:00
|
|
|
var req *upsertPasswordReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2016-12-30 02:57:20 +00:00
|
|
|
user := p.ByName("user")
|
2017-01-17 20:25:39 +00:00
|
|
|
err := auth.UpsertPassword(user, []byte(req.Password))
|
2015-10-23 00:45:51 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2017-01-17 20:25:39 +00:00
|
|
|
return message(fmt.Sprintf("password for for user %q upserted", user)), nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
2016-12-10 01:31:05 +00:00
|
|
|
user, err := services.GetUserMarshaler().UnmarshalUser(req.User)
|
2016-04-05 16:58:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-09-06 05:13:40 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type checkPasswordReq struct {
|
2017-01-17 19:24:17 +00:00
|
|
|
Password string `json:"password"`
|
|
|
|
OTPToken string `json:"otp_token"`
|
2016-02-23 20:03:34 +00:00
|
|
|
}
|
2015-03-19 04:13:56 +00:00
|
|
|
|
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) {
|
2016-02-23 20:03:34 +00:00
|
|
|
var req checkPasswordReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2016-12-30 02:57:20 +00:00
|
|
|
user := p.ByName("user")
|
2017-01-17 19:24:17 +00:00
|
|
|
if err := auth.CheckPassword(user, []byte(req.Password), req.OTPToken); err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
|
|
|
return message(fmt.Sprintf("%q user password matches", user)), nil
|
2015-03-19 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2016-12-30 02:57:20 +00:00
|
|
|
user, err := auth.GetUser(p.ByName("user"))
|
2016-04-05 16:58:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetUserMarshaler().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) {
|
2016-12-13 03:26:59 +00:00
|
|
|
users, err := auth.GetUsers()
|
2015-03-02 20:11:23 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
data, err := services.GetUserMarshaler().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
|
|
|
}
|
|
|
|
|
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")
|
2016-12-13 03:26:59 +00:00
|
|
|
if err := auth.DeleteUser(user); err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return message(fmt.Sprintf("user '%v' deleted", user)), nil
|
|
|
|
}
|
2015-03-02 20:11:23 +00:00
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type generateKeyPairReq struct {
|
|
|
|
Password string `json:"password"`
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +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) {
|
2016-02-23 20:03:34 +00:00
|
|
|
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 {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return &generateKeyPairResponse{PrivKey: priv, PubKey: string(pub)}, nil
|
|
|
|
}
|
2015-03-02 20:11:23 +00:00
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type generateHostCertReq struct {
|
2017-01-21 02:30:55 +00:00
|
|
|
Key []byte `json:"key"`
|
|
|
|
HostID string `json:"hostname"`
|
|
|
|
NodeName string `json:"node_name"`
|
2017-11-16 00:47:42 +00:00
|
|
|
Principals []string `json:"principals"`
|
2017-01-21 02:30:55 +00:00
|
|
|
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) {
|
2016-02-23 20:03:34 +00:00
|
|
|
var req *generateHostCertReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2016-02-19 02:07:43 +00:00
|
|
|
}
|
2017-01-21 02:30:55 +00:00
|
|
|
|
2017-11-16 00:47:42 +00:00
|
|
|
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 {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-03-02 20:11:23 +00:00
|
|
|
}
|
2017-01-21 02:30:55 +00:00
|
|
|
|
2016-02-23 20:03:34 +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) {
|
2018-01-18 02:38:11 +00:00
|
|
|
var req GenerateTokenRequest
|
2016-02-23 20:03:34 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
2018-01-18 02:38:11 +00:00
|
|
|
token, err := auth.GenerateToken(req)
|
2015-05-07 03:10:44 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return string(token), nil
|
2015-05-07 03:10:44 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2018-01-09 04:28:53 +00:00
|
|
|
var req RegisterUsingTokenRequest
|
2016-02-23 20:03:34 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2018-11-20 01:36:19 +00:00
|
|
|
|
|
|
|
// Pass along the remote address the request came from to the registration function.
|
|
|
|
req.RemoteAddr = r.RemoteAddr
|
|
|
|
|
2018-01-09 04:28:53 +00:00
|
|
|
keys, err := auth.RegisterUsingToken(req)
|
2015-11-13 01:32:45 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return keys, nil
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type registerNewAuthServerReq struct {
|
2016-03-10 17:41:01 +00:00
|
|
|
Token string `json:"token"`
|
2016-02-23 20:03:34 +00:00
|
|
|
}
|
2015-11-13 01:32:45 +00:00
|
|
|
|
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) {
|
2016-02-23 20:03:34 +00:00
|
|
|
var req *registerNewAuthServerReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
err := auth.RegisterNewAuthServer(req.Token)
|
2015-11-13 01:32:45 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
2016-03-10 17:41:01 +00:00
|
|
|
return message("ok"), nil
|
2015-11-13 01:32:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 01:09:11 +00:00
|
|
|
func (s *APIServer) generateServerKeys(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
|
2018-01-09 04:28:53 +00:00
|
|
|
var req GenerateServerKeysRequest
|
2017-11-25 01:09:11 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-11-20 01:36:19 +00:00
|
|
|
// Pass along the remote address the request came from to the registration function.
|
|
|
|
req.RemoteAddr = r.RemoteAddr
|
|
|
|
|
2018-01-09 04:28:53 +00:00
|
|
|
keys, err := auth.GenerateServerKeys(req)
|
2017-11-25 01:09:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
|
2018-04-08 21:37:33 +00:00
|
|
|
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
|
2016-02-23 20:03:34 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
2016-12-29 19:36:57 +00:00
|
|
|
ca, err := services.GetCertAuthorityMarshaler().UnmarshalCertAuthority(req.CA)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-04-07 23:51:31 +00:00
|
|
|
if req.TTL != 0 {
|
|
|
|
ca.SetTTL(s, req.TTL)
|
|
|
|
}
|
|
|
|
if err := auth.UpsertCertAuthority(ca); err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-02-16 17:36:02 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return message("ok"), nil
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
|
|
|
|
2018-04-08 21:37:33 +00:00
|
|
|
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.GetCertAuthorityMarshaler().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-03-28 19:58:34 +00:00
|
|
|
}
|
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
|
|
|
|
2015-11-18 04:39:19 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-11-18 04:39:19 +00:00
|
|
|
}
|
2016-12-29 19:36:57 +00:00
|
|
|
items := make([]json.RawMessage, len(certs))
|
|
|
|
for i, cert := range certs {
|
2018-12-12 00:22:44 +00:00
|
|
|
data, err := services.GetCertAuthorityMarshaler().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
|
2015-11-18 04:39:19 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2016-12-14 23:48:36 +00:00
|
|
|
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)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetCertAuthorityMarshaler().MarshalCertAuthority(ca, services.WithVersion(version), services.PreserveResourceID()))
|
2016-12-14 23:48:36 +00:00
|
|
|
}
|
|
|
|
|
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()
|
2015-06-22 21:05:15 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return domain, nil
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
|
|
|
|
2018-10-03 19:35:57 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-12-08 10:23:51 +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()
|
2016-11-28 08:21:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-02-14 02:29:27 +00:00
|
|
|
|
2016-12-08 10:23:51 +00:00
|
|
|
w.Header().Set("Content-Type", "application/fido.trusted-apps+json")
|
2017-07-28 18:37:12 +00:00
|
|
|
return universalSecondFactor.AppID, nil
|
2016-11-28 08:21:37 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2016-02-16 17:36:02 +00:00
|
|
|
id := services.CertAuthID{
|
2016-12-30 02:57:20 +00:00
|
|
|
DomainName: p.ByName("domain"),
|
|
|
|
Type: services.CertAuthType(p.ByName("type")),
|
2016-02-16 17:36:02 +00:00
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
if err := auth.DeleteCertAuthority(id); err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return message(fmt.Sprintf("cert '%v' deleted", id)), nil
|
2015-06-22 21:05:15 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 21:19:43 +00:00
|
|
|
type createSessionReq struct {
|
|
|
|
Session session.Session `json:"session"`
|
2016-02-23 20:03:34 +00:00
|
|
|
}
|
2015-07-03 20:08:26 +00:00
|
|
|
|
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
|
2016-02-23 20:03:34 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2015-07-03 20:08:26 +00:00
|
|
|
}
|
2017-04-26 20:31:27 +00:00
|
|
|
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 {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-07-03 20:08:26 +00:00
|
|
|
}
|
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)
|
|
|
|
}
|
2017-04-26 20:31:27 +00:00
|
|
|
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
|
2015-07-03 20:08:26 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2017-04-26 20:31:27 +00:00
|
|
|
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 {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-06-30 23:12:18 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +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"))
|
2016-03-20 01:16:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-04-26 20:31:27 +00:00
|
|
|
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 {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2015-06-30 23:12:18 +00:00
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
return se, nil
|
2015-06-30 23:12:18 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type getSignupTokenDataResponse struct {
|
2017-01-17 19:24:17 +00:00
|
|
|
User string `json:"user"`
|
|
|
|
QRImg []byte `json:"qrimg"`
|
2016-02-23 20:03:34 +00:00
|
|
|
}
|
|
|
|
|
2017-01-17 19:24:17 +00:00
|
|
|
// getSignupTokenData returns the signup data for a token.
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) getSignupTokenData(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-01-21 00:05:23 +00:00
|
|
|
|
2017-01-17 19:24:17 +00:00
|
|
|
user, otpQRCode, err := auth.GetSignupTokenData(token)
|
2016-01-21 00:05:23 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-01-21 00:05:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
return &getSignupTokenDataResponse{
|
2017-01-17 19:24:17 +00:00
|
|
|
User: user,
|
|
|
|
QRImg: otpQRCode,
|
2016-02-23 20:03:34 +00:00
|
|
|
}, nil
|
2016-01-21 00:05:23 +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)
|
2016-10-14 06:51:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return u2fRegReq, nil
|
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type createSignupTokenReq struct {
|
2016-12-30 02:57:20 +00:00
|
|
|
User services.UserV1 `json:"user"`
|
2017-11-29 03:39:12 +00:00
|
|
|
TTL time.Duration `json:"ttl"`
|
2016-04-05 16:58:16 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) createSignupToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
2016-12-30 02:57:20 +00:00
|
|
|
var req *createSignupTokenReq
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
2016-01-21 00:05:23 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2016-12-30 02:57:20 +00:00
|
|
|
if err := req.User.Check(); err != nil {
|
2016-12-30 00:17:56 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2017-11-29 03:39:12 +00:00
|
|
|
token, err := auth.CreateSignupToken(req.User, req.TTL)
|
2016-01-21 00:05:23 +00:00
|
|
|
if err != nil {
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-01-21 00:05:23 +00:00
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
return token, nil
|
2015-05-12 00:04:22 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 20:03:34 +00:00
|
|
|
type createUserWithTokenReq struct {
|
2017-01-17 19:24:17 +00:00
|
|
|
Token string `json:"token"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
OTPToken string `json:"otp_token"`
|
2015-07-11 00:24:44 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) createUserWithToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
2016-02-23 20:03:34 +00:00
|
|
|
var req *createUserWithTokenReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
cap, err := auth.GetAuthPreference()
|
2017-03-22 02:08:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var webSession services.WebSession
|
|
|
|
|
|
|
|
switch cap.GetSecondFactor() {
|
|
|
|
case teleport.OFF:
|
|
|
|
webSession, err = auth.CreateUserWithoutOTP(req.Token, req.Password)
|
|
|
|
case teleport.OTP, teleport.TOTP, teleport.HOTP:
|
|
|
|
webSession, err = auth.CreateUserWithOTP(req.Token, req.Password, req.OTPToken)
|
|
|
|
}
|
2016-02-23 20:03:34 +00:00
|
|
|
if err != nil {
|
2017-11-25 01:09:11 +00:00
|
|
|
log.Warningf("failed to create user: %v", err.Error())
|
2016-02-23 20:03:34 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-01-17 19:24:17 +00:00
|
|
|
|
2017-03-22 02:08:27 +00:00
|
|
|
return rawMessage(services.GetWebSessionMarshaler().MarshalWebSession(webSession, services.WithVersion(version)))
|
2015-12-07 20:05:54 +00:00
|
|
|
}
|
|
|
|
|
2016-12-08 10:23:51 +00:00
|
|
|
type createUserWithU2FTokenReq struct {
|
2016-12-15 00:36:55 +00:00
|
|
|
Token string `json:"token"`
|
|
|
|
Password string `json:"password"`
|
2016-12-08 10:23:51 +00:00
|
|
|
U2FRegisterResponse u2f.RegisterResponse `json:"u2f_register_response"`
|
2016-10-14 06:51:16 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 23:46:19 +00:00
|
|
|
func (s *APIServer) createUserWithU2FToken(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
2016-12-08 10:23:51 +00:00
|
|
|
var req *createUserWithU2FTokenReq
|
2016-10-14 06:51:16 +00:00
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2016-12-15 00:36:55 +00:00
|
|
|
sess, err := auth.CreateUserWithU2FToken(req.Token, req.Password, req.U2FRegisterResponse)
|
2016-10-14 06:51:16 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return nil, trace.Wrap(err)
|
2016-02-23 20:03:34 +00:00
|
|
|
}
|
2017-02-11 02:55:51 +00:00
|
|
|
return rawMessage(services.GetWebSessionMarshaler().MarshalWebSession(sess, services.WithVersion(version)))
|
2015-12-07 20:05:54 +00:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
2016-12-29 19:36:57 +00:00
|
|
|
connector, err := services.GetOIDCConnectorMarshaler().UnmarshalOIDCConnector(req.Connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2017-04-07 23:51:31 +00:00
|
|
|
if req.TTL != 0 {
|
|
|
|
connector.SetTTL(s, req.TTL)
|
|
|
|
}
|
|
|
|
err = auth.UpsertOIDCConnector(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)
|
|
|
|
}
|
2016-12-30 02:57:20 +00:00
|
|
|
connector, err := auth.GetOIDCConnector(p.ByName("id"), withSecrets)
|
2016-04-03 05:20:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-12-30 21:25:35 +00:00
|
|
|
return rawMessage(services.GetOIDCConnectorMarshaler().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) {
|
2016-12-30 02:57:20 +00:00
|
|
|
err := auth.DeleteOIDCConnector(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)
|
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
connectors, err := auth.GetOIDCConnectors(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 {
|
2016-12-29 23:46:19 +00:00
|
|
|
data, err := services.GetOIDCConnectorMarshaler().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"`
|
2017-11-25 01:09:11 +00:00
|
|
|
// 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,
|
2017-11-25 01:09:11 +00:00
|
|
|
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.GetWebSessionMarshaler().MarshalWebSession(response.Session, services.WithVersion(version))
|
|
|
|
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.GetCertAuthorityMarshaler().MarshalCertAuthority(ca, services.WithVersion(version))
|
|
|
|
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.GetSAMLConnectorMarshaler().UnmarshalSAMLConnector(req.Connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
err = auth.CreateSAMLConnector(connector)
|
|
|
|
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.GetSAMLConnectorMarshaler().UnmarshalSAMLConnector(req.Connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
err = auth.UpsertSAMLConnector(connector)
|
|
|
|
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(p.ByName("id"), withSecrets)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return rawMessage(services.GetSAMLConnectorMarshaler().MarshalSAMLConnector(connector, services.WithVersion(version)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *APIServer) deleteSAMLConnector(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
|
|
err := auth.DeleteSAMLConnector(p.ByName("id"))
|
|
|
|
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(withSecrets)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
items := make([]json.RawMessage, len(connectors))
|
|
|
|
for i, connector := range connectors {
|
|
|
|
data, err := services.GetSAMLConnectorMarshaler().MarshalSAMLConnector(connector, services.WithVersion(version))
|
|
|
|
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.GetWebSessionMarshaler().MarshalWebSession(response.Session, services.WithVersion(version))
|
|
|
|
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 {
|
2016-12-29 23:46:19 +00:00
|
|
|
data, err := services.GetCertAuthorityMarshaler().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.GetGithubConnectorMarshaler().Unmarshal(req.Connector)
|
|
|
|
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.GetGithubConnectorMarshaler().Unmarshal(req.Connector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
if err := auth.UpsertGithubConnector(connector); err != nil {
|
|
|
|
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(withSecrets)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
items := make([]json.RawMessage, len(connectors))
|
|
|
|
for i, connector := range connectors {
|
2018-12-12 00:22:44 +00:00
|
|
|
bytes, err := services.GetGithubConnectorMarshaler().Marshal(connector, services.PreserveResourceID())
|
2017-12-14 21:41:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
items[i] = bytes
|
|
|
|
}
|
|
|
|
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(p.ByName("id"), withSecrets)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetGithubConnectorMarshaler().Marshal(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(p.ByName("id")); err != nil {
|
|
|
|
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"`
|
2018-02-17 00:46:37 +00:00
|
|
|
// 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,
|
2018-02-17 00:46:37 +00:00
|
|
|
TLSCert: response.TLSCert,
|
2017-12-14 21:41:38 +00:00
|
|
|
Req: response.Req,
|
|
|
|
}
|
|
|
|
if response.Session != nil {
|
|
|
|
rawSession, err := services.GetWebSessionMarshaler().MarshalWebSession(
|
2018-12-12 00:22:44 +00:00
|
|
|
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.GetCertAuthorityMarshaler().MarshalCertAuthority(
|
2018-12-12 00:22:44 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2018-02-02 02:04:51 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// remove 'to', 'from' and 'limit' fields, passing the rest of the query unmodified
|
2016-05-08 04:17:28 +00:00
|
|
|
// to whatever pluggable search is implemented by the backend
|
2016-04-29 09:42:08 +00:00
|
|
|
query.Del("to")
|
|
|
|
query.Del("from")
|
2018-02-02 02:04:51 +00:00
|
|
|
query.Del("limit")
|
|
|
|
eventsList, err := auth.SearchEvents(from, to, query.Encode(), limit)
|
2016-04-29 09:42:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-04-30 05:43:22 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2018-02-02 02:04:51 +00:00
|
|
|
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
|
2018-02-02 02:04:51 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-04-24 23:49:03 +00:00
|
|
|
type auditEventReq struct {
|
2016-04-25 06:32:44 +00:00
|
|
|
Type string `json:"type"`
|
|
|
|
Fields events.EventFields `json:"fields"`
|
2016-04-24 23:49:03 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2016-04-24 23:49:03 +00:00
|
|
|
var req auditEventReq
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
2016-04-24 22:35:33 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
if err := auth.EmitAuditEvent(req.Type, req.Fields); err != nil {
|
2016-04-24 22:35:33 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
if err := auth.PostSessionSlice(slice); err != nil {
|
2017-05-26 01:56:32 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return message("ok"), nil
|
|
|
|
}
|
|
|
|
|
2018-03-29 20:47:53 +00:00
|
|
|
// 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()
|
|
|
|
}
|
2018-03-29 20:47:53 +00:00
|
|
|
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()
|
|
|
|
if err = auth.UploadSessionRecording(events.SessionRecording{
|
|
|
|
SessionID: session.ID(sid),
|
|
|
|
Namespace: namespace,
|
|
|
|
Recording: files[0],
|
|
|
|
}); 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
|
2016-05-06 06:51:56 +00:00
|
|
|
// 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) {
|
2016-04-26 00:19:37 +00:00
|
|
|
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-05-06 06:51:56 +00:00
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
namespace := p.ByName("namespace")
|
2017-04-26 20:31:27 +00:00
|
|
|
if !services.IsValidNamespace(namespace) {
|
|
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
|
|
}
|
2016-12-13 03:26:59 +00:00
|
|
|
|
2016-05-06 06:51:56 +00:00
|
|
|
// "offset bytes" query param
|
|
|
|
offsetBytes, err := strconv.Atoi(r.URL.Query().Get("offset"))
|
|
|
|
if err != nil || offsetBytes < 0 {
|
|
|
|
offsetBytes = 0
|
2016-04-26 00:19:37 +00:00
|
|
|
}
|
2016-05-06 06:51:56 +00:00
|
|
|
// "max bytes" query param
|
|
|
|
max, err := strconv.Atoi(r.URL.Query().Get("bytes"))
|
2016-04-27 05:55:06 +00:00
|
|
|
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)
|
2016-05-06 06:51:56 +00:00
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
2016-12-13 03:26:59 +00:00
|
|
|
buffer, err := auth.GetSessionChunk(namespace, *sid, offsetBytes, max)
|
2016-04-26 00:19:37 +00:00
|
|
|
if err != nil {
|
2016-12-13 03:26:59 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-04-26 00:19:37 +00:00
|
|
|
}
|
2016-05-06 06:51:56 +00:00
|
|
|
if _, err = w.Write(buffer); err != nil {
|
2016-12-13 03:26:59 +00:00
|
|
|
return nil, trace.Wrap(err)
|
2016-04-26 00:19:37 +00:00
|
|
|
}
|
2016-05-06 06:51:56 +00:00
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
2016-12-13 03:26:59 +00:00
|
|
|
return nil, nil
|
2016-04-26 00:19:37 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 20:23:58 +00:00
|
|
|
// HTTP GET /:version/sessions/:id/events?maxage=n
|
2016-04-30 05:43:22 +00:00
|
|
|
// 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) {
|
2016-04-27 05:55:06 +00:00
|
|
|
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")
|
2017-04-26 20:31:27 +00:00
|
|
|
if !services.IsValidNamespace(namespace) {
|
|
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
|
|
}
|
2016-04-30 05:43:22 +00:00
|
|
|
afterN, err := strconv.Atoi(r.URL.Query().Get("after"))
|
|
|
|
if err != nil {
|
|
|
|
afterN = 0
|
|
|
|
}
|
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")
|
2017-04-26 20:31:27 +00:00
|
|
|
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")
|
2017-04-26 20:31:27 +00:00
|
|
|
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.GetRoleMarshaler().UnmarshalRole(req.Role)
|
|
|
|
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
|
|
|
err = auth.UpsertRole(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) {
|
2016-12-30 02:57:20 +00:00
|
|
|
role, err := auth.GetRole(p.ByName("role"))
|
2016-12-13 03:26:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetRoleMarshaler().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) {
|
2016-12-13 03:26:59 +00:00
|
|
|
roles, err := auth.GetRoles()
|
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
raw, err := services.GetRoleMarshaler().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")
|
2016-12-13 03:26:59 +00:00
|
|
|
if err := auth.DeleteRole(role); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return message(fmt.Sprintf("role '%v' deleted", role)), nil
|
2016-04-27 05:55:06 +00:00
|
|
|
}
|
|
|
|
|
2017-10-24 20:52:29 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetClusterConfigMarshaler().Marshal(cc, services.WithVersion(version), services.PreserveResourceID()))
|
2017-10-24 20:52:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.GetClusterConfigMarshaler().Unmarshal(req.ClusterConfig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = auth.SetClusterConfig(cc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-08-02 00:25:16 +00:00
|
|
|
return message("cluster config set"), nil
|
2017-10-24 20:52:29 +00:00
|
|
|
}
|
|
|
|
|
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()
|
2017-02-14 02:29:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetClusterNameMarshaler().Marshal(cn, services.WithVersion(version), services.PreserveResourceID()))
|
2017-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
type setClusterNameReq struct {
|
|
|
|
ClusterName json.RawMessage `json:"cluster_name"`
|
2017-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
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
|
2017-02-14 02:29:27 +00:00
|
|
|
|
|
|
|
err := httplib.ReadJSON(r, &req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
cn, err := services.GetClusterNameMarshaler().Unmarshal(req.ClusterName)
|
2017-02-14 02:29:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
err = auth.SetClusterName(cn)
|
2017-02-14 02:29:27 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetStaticTokensMarshaler().Marshal(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.GetStaticTokensMarshaler().Unmarshal(req.StaticTokens)
|
|
|
|
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-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
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()
|
2017-02-14 02:29:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.GetAuthPreferenceMarshaler().Marshal(cap, services.WithVersion(version), services.PreserveResourceID()))
|
2017-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
type setClusterAuthPreferenceReq struct {
|
|
|
|
ClusterAuthPreference json.RawMessage `json:"cluster_auth_prerference"`
|
2017-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
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
|
2017-02-14 02:29:27 +00:00
|
|
|
|
|
|
|
err := httplib.ReadJSON(r, &req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
cap, err := services.GetAuthPreferenceMarshaler().Unmarshal(req.ClusterAuthPreference)
|
2017-02-14 02:29:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2017-07-28 18:37:12 +00:00
|
|
|
err = auth.SetAuthPreference(cap)
|
2017-02-14 02:29:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2017-10-20 08:20:26 +00:00
|
|
|
return message(fmt.Sprintf("cluster authentication preference set: %+v", cap)), nil
|
2017-02-14 02:29:27 +00:00
|
|
|
}
|
|
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-12-28 02:51:46 +00:00
|
|
|
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 {
|
2018-12-12 00:22:44 +00:00
|
|
|
data, err := services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID())
|
2017-12-28 02:51:46 +00:00
|
|
|
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)
|
|
|
|
}
|
2018-12-12 00:22:44 +00:00
|
|
|
return rawMessage(services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID()))
|
2017-12-28 02:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2018-05-19 23:58:14 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-03-02 20:11:23 +00:00
|
|
|
func message(msg string) map[string]interface{} {
|
|
|
|
return map[string]interface{}{"message": msg}
|
|
|
|
}
|