mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
Adds `UpsertCertAuthority` to the `teleport.v1.trust.TrustService` and deprecates the HTTP api. The `services.Trust` interface was also updated to include a `context.Context` in both `CreateCertAuthority` and `UpsertCertAuthority. To ensure backwards compatibility the `auth.Client` will first attempt to use the gRPC client and if that fails with a not implemented error it will fallback to using HTTP. Part of #6394
1224 lines
42 KiB
Go
1224 lines
42 KiB
Go
/*
|
|
Copyright 2015-2021 Gravitational, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gravitational/trace"
|
|
"github.com/jonboulle/clockwork"
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
apidefaults "github.com/gravitational/teleport/api/defaults"
|
|
"github.com/gravitational/teleport/api/types"
|
|
apievents "github.com/gravitational/teleport/api/types/events"
|
|
"github.com/gravitational/teleport/lib/authz"
|
|
"github.com/gravitational/teleport/lib/events"
|
|
"github.com/gravitational/teleport/lib/httplib"
|
|
"github.com/gravitational/teleport/lib/plugin"
|
|
"github.com/gravitational/teleport/lib/services"
|
|
"github.com/gravitational/teleport/lib/session"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
)
|
|
|
|
type APIConfig struct {
|
|
PluginRegistry plugin.Registry
|
|
AuthServer *Server
|
|
AuditLog events.AuditLogSessionStreamer
|
|
Authorizer authz.Authorizer
|
|
Emitter apievents.Emitter
|
|
// KeepAlivePeriod defines period between keep alives
|
|
KeepAlivePeriod time.Duration
|
|
// KeepAliveCount specifies amount of missed keep alives
|
|
// to wait for until declaring connection as broken
|
|
KeepAliveCount int
|
|
// MetadataGetter retrieves additional metadata about session uploads.
|
|
// Will be nil if audit logging is not enabled.
|
|
MetadataGetter events.UploadMetadataGetter
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets default values
|
|
func (a *APIConfig) CheckAndSetDefaults() error {
|
|
if a.KeepAlivePeriod == 0 {
|
|
a.KeepAlivePeriod = apidefaults.ServerKeepAliveTTL()
|
|
}
|
|
if a.KeepAliveCount == 0 {
|
|
a.KeepAliveCount = apidefaults.KeepAliveCountMax
|
|
}
|
|
if a.Authorizer == nil {
|
|
return trace.BadParameter("authorizer is missing")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// APIServer implements http API server for AuthServer interface
|
|
type APIServer struct {
|
|
APIConfig
|
|
httprouter.Router
|
|
clockwork.Clock
|
|
}
|
|
|
|
// NewAPIServer returns a new instance of APIServer HTTP handler
|
|
func NewAPIServer(config *APIConfig) (http.Handler, error) {
|
|
srv := APIServer{
|
|
APIConfig: *config,
|
|
Clock: clockwork.NewRealClock(),
|
|
}
|
|
srv.Router = *httprouter.New()
|
|
srv.Router.UseRawPath = true
|
|
|
|
// Kubernetes extensions
|
|
srv.POST("/:version/kube/csr", srv.WithAuth(srv.processKubeCSR))
|
|
|
|
srv.POST("/:version/authorities/:type", srv.WithAuth(srv.upsertCertAuthority))
|
|
srv.POST("/:version/authorities/:type/rotate", srv.WithAuth(srv.rotateCertAuthority))
|
|
srv.POST("/:version/authorities/:type/rotate/external", srv.WithAuth(srv.rotateExternalCertAuthority))
|
|
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))
|
|
|
|
// Generating certificates for user and host authorities
|
|
srv.POST("/:version/ca/host/certs", srv.WithAuth(srv.generateHostCert))
|
|
|
|
// Operations on users
|
|
srv.GET("/:version/users", srv.WithAuth(srv.getUsers))
|
|
srv.GET("/:version/users/:user", srv.WithAuth(srv.getUser))
|
|
srv.DELETE("/:version/users/:user", srv.WithAuth(srv.deleteUser)) // DELETE IN: 5.2 REST method is replaced by grpc method with context.
|
|
|
|
// Passwords and sessions
|
|
srv.POST("/:version/users", srv.WithAuth(srv.upsertUser))
|
|
srv.POST("/:version/users/:user/web/sessions", srv.WithAuth(srv.createWebSession))
|
|
srv.POST("/:version/users/:user/web/authenticate", srv.WithAuth(srv.authenticateWebUser))
|
|
srv.POST("/:version/users/:user/ssh/authenticate", srv.WithAuth(srv.authenticateSSHUser))
|
|
srv.GET("/:version/users/:user/web/sessions/:sid", srv.WithAuth(srv.getWebSession))
|
|
srv.DELETE("/:version/users/:user/web/sessions/:sid", srv.WithAuth(srv.deleteWebSession))
|
|
|
|
// Servers and presence heartbeat
|
|
srv.POST("/:version/namespaces/:namespace/nodes/keepalive", srv.WithAuth(srv.keepAliveNode))
|
|
srv.POST("/:version/authservers", srv.WithAuth(srv.upsertAuthServer))
|
|
srv.GET("/:version/authservers", srv.WithAuth(srv.getAuthServers))
|
|
srv.POST("/:version/proxies", srv.WithAuth(srv.upsertProxy))
|
|
srv.GET("/:version/proxies", srv.WithAuth(srv.getProxies))
|
|
srv.DELETE("/:version/proxies", srv.WithAuth(srv.deleteAllProxies))
|
|
srv.DELETE("/:version/proxies/:name", srv.WithAuth(srv.deleteProxy))
|
|
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))
|
|
srv.DELETE("/:version/tunnelconnections/:cluster/:conn", srv.WithAuth(srv.deleteTunnelConnection))
|
|
srv.DELETE("/:version/tunnelconnections/:cluster", srv.WithAuth(srv.deleteTunnelConnections))
|
|
srv.DELETE("/:version/tunnelconnections", srv.WithAuth(srv.deleteAllTunnelConnections))
|
|
|
|
// Remote clusters
|
|
srv.POST("/:version/remoteclusters", srv.WithAuth(srv.createRemoteCluster))
|
|
srv.GET("/:version/remoteclusters/:cluster", srv.WithAuth(srv.getRemoteCluster))
|
|
srv.GET("/:version/remoteclusters", srv.WithAuth(srv.getRemoteClusters))
|
|
srv.DELETE("/:version/remoteclusters/:cluster", srv.WithAuth(srv.deleteRemoteCluster))
|
|
srv.DELETE("/:version/remoteclusters", srv.WithAuth(srv.deleteAllRemoteClusters))
|
|
|
|
// Reverse tunnels
|
|
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))
|
|
|
|
// trusted clusters
|
|
srv.POST("/:version/trustedclusters/validate", srv.WithAuth(srv.validateTrustedCluster))
|
|
|
|
// Tokens
|
|
srv.POST("/:version/tokens/register", srv.WithAuth(srv.registerUsingToken))
|
|
|
|
// Active sessions
|
|
srv.GET("/:version/namespaces/:namespace/sessions/:id/stream", srv.WithAuth(srv.getSessionChunk))
|
|
srv.GET("/:version/namespaces/:namespace/sessions/:id/events", srv.WithAuth(srv.getSessionEvents))
|
|
|
|
// Namespaces
|
|
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))
|
|
|
|
// cluster configuration
|
|
srv.GET("/:version/configuration/name", srv.WithAuth(srv.getClusterName))
|
|
srv.POST("/:version/configuration/name", srv.WithAuth(srv.setClusterName))
|
|
srv.GET("/:version/configuration/static_tokens", srv.WithAuth(srv.getStaticTokens))
|
|
srv.DELETE("/:version/configuration/static_tokens", srv.WithAuth(srv.deleteStaticTokens))
|
|
srv.POST("/:version/configuration/static_tokens", srv.WithAuth(srv.setStaticTokens))
|
|
|
|
// SSO validation handlers
|
|
srv.POST("/:version/github/requests/validate", srv.WithAuth(srv.validateGithubAuthCallback))
|
|
|
|
// Audit logs AKA events
|
|
srv.GET("/:version/events", srv.WithAuth(srv.searchEvents))
|
|
srv.GET("/:version/events/session", srv.WithAuth(srv.searchSessionEvents))
|
|
|
|
if config.PluginRegistry != nil {
|
|
if err := config.PluginRegistry.RegisterAuthWebHandlers(&srv); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
}
|
|
|
|
return httplib.RewritePaths(&srv.Router,
|
|
httplib.Rewrite("/v1/nodes", "/v1/namespaces/default/nodes"),
|
|
httplib.Rewrite("/v1/sessions", "/v1/namespaces/default/sessions"),
|
|
httplib.Rewrite("/v1/sessions/([^/]+)/(.*)", "/v1/namespaces/default/sessions/$1/$2"),
|
|
), nil
|
|
}
|
|
|
|
// HandlerWithAuthFunc is http handler with passed auth context
|
|
type HandlerWithAuthFunc func(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error)
|
|
|
|
func (s *APIServer) WithAuth(handler HandlerWithAuthFunc) httprouter.Handle {
|
|
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
|
|
// HTTPS server expects auth context to be set by the auth middleware
|
|
authContext, err := s.Authorizer.Authorize(r.Context())
|
|
if err != nil {
|
|
return nil, authz.ConvertAuthorizerError(r.Context(), log, err)
|
|
}
|
|
auth := &ServerWithRoles{
|
|
authServer: s.AuthServer,
|
|
context: *authContext,
|
|
alog: s.AuthServer,
|
|
}
|
|
version := p.ByName("version")
|
|
if version == "" {
|
|
return nil, trace.BadParameter("missing version")
|
|
}
|
|
return handler(auth, w, r, p, version)
|
|
})
|
|
}
|
|
|
|
type upsertServerRawReq struct {
|
|
Server json.RawMessage `json:"server"`
|
|
TTL time.Duration `json:"ttl"`
|
|
}
|
|
|
|
// upsertServer is a common utility function
|
|
func (s *APIServer) upsertServer(auth services.Presence, role types.SystemRole, r *http.Request, p httprouter.Params) (interface{}, error) {
|
|
var req upsertServerRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
var kind string
|
|
switch role {
|
|
case types.RoleNode:
|
|
kind = types.KindNode
|
|
case types.RoleAuth:
|
|
kind = types.KindAuthServer
|
|
case types.RoleProxy:
|
|
kind = types.KindProxy
|
|
default:
|
|
return nil, trace.BadParameter("upsertServer with unknown role: %q", role)
|
|
}
|
|
server, err := services.UnmarshalServer(req.Server, kind)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
// 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:
|
|
server.SetAddr(utils.ReplaceLocalhost(server.GetAddr(), r.RemoteAddr))
|
|
if req.TTL != 0 {
|
|
server.SetExpiry(s.Now().UTC().Add(req.TTL))
|
|
}
|
|
switch role {
|
|
case types.RoleNode:
|
|
namespace := p.ByName("namespace")
|
|
if !types.IsValidNamespace(namespace) {
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
}
|
|
server.SetNamespace(namespace)
|
|
handle, err := auth.UpsertNode(r.Context(), server)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return handle, nil
|
|
case types.RoleAuth:
|
|
if err := auth.UpsertAuthServer(server); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
case types.RoleProxy:
|
|
if err := auth.UpsertProxy(server); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
default:
|
|
return nil, trace.BadParameter("unknown server role %q", role)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// 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 types.KeepAlive
|
|
if err := httplib.ReadJSON(r, &handle); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := auth.KeepAliveServer(r.Context(), handle); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// upsertProxy is called by remote SSH nodes when they ping back into the auth service
|
|
func (s *APIServer) upsertProxy(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
return s.upsertServer(auth, types.RoleProxy, r, p)
|
|
}
|
|
|
|
// getProxies returns registered proxies
|
|
func (s *APIServer) getProxies(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
servers, err := auth.GetProxies()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return marshalServers(servers, version)
|
|
}
|
|
|
|
// deleteAllProxies deletes all proxies
|
|
func (s *APIServer) deleteAllProxies(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteAllProxies()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// deleteProxy deletes proxy
|
|
func (s *APIServer) deleteProxy(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
name := p.ByName("name")
|
|
if name == "" {
|
|
return nil, trace.BadParameter("missing proxy name")
|
|
}
|
|
err := auth.DeleteProxy(name)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// upsertAuthServer is called by remote Auth servers when they ping back into the auth service
|
|
func (s *APIServer) upsertAuthServer(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
return s.upsertServer(auth, types.RoleAuth, r, p)
|
|
}
|
|
|
|
// getAuthServers returns registered auth servers
|
|
func (s *APIServer) getAuthServers(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
servers, err := auth.GetAuthServers()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return marshalServers(servers, version)
|
|
}
|
|
|
|
func marshalServers(servers []types.Server, version string) (interface{}, error) {
|
|
items := make([]json.RawMessage, len(servers))
|
|
for i, server := range servers {
|
|
data, err := services.MarshalServer(server, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
type upsertReverseTunnelRawReq struct {
|
|
ReverseTunnel json.RawMessage `json:"reverse_tunnel"`
|
|
TTL time.Duration `json:"ttl"`
|
|
}
|
|
|
|
// upsertReverseTunnel is called by admin to create a reverse tunnel to remote proxy
|
|
func (s *APIServer) upsertReverseTunnel(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req upsertReverseTunnelRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
tun, err := services.UnmarshalReverseTunnel(req.ReverseTunnel)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := services.ValidateReverseTunnel(tun); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if req.TTL != 0 {
|
|
tun.SetExpiry(s.Now().UTC().Add(req.TTL))
|
|
}
|
|
if err := auth.UpsertReverseTunnel(tun); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// getReverseTunnels returns a list of reverse tunnels
|
|
func (s *APIServer) getReverseTunnels(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
reverseTunnels, err := auth.GetReverseTunnels(r.Context())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items := make([]json.RawMessage, len(reverseTunnels))
|
|
for i, tunnel := range reverseTunnels {
|
|
data, err := services.MarshalReverseTunnel(tunnel, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
// deleteReverseTunnel deletes reverse tunnel
|
|
func (s *APIServer) deleteReverseTunnel(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
domainName := p.ByName("domain")
|
|
err := auth.DeleteReverseTunnel(domainName)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message(fmt.Sprintf("reverse tunnel %v deleted", domainName)), nil
|
|
}
|
|
|
|
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(r.Context(), 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) deleteWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
user, sessionID := p.ByName("user"), p.ByName("sid")
|
|
err := auth.WebSessions().Delete(r.Context(), types.DeleteWebSessionRequest{
|
|
User: user,
|
|
SessionID: sessionID,
|
|
})
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message(fmt.Sprintf("session %q for user %q deleted", sessionID, user)), nil
|
|
}
|
|
|
|
func (s *APIServer) getWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
user, sid := p.ByName("user"), p.ByName("sid")
|
|
sess, err := auth.GetWebSessionInfo(r.Context(), user, sid)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
|
|
}
|
|
|
|
type WebSessionReq struct {
|
|
// User is the user name associated with the session id.
|
|
User string `json:"user"`
|
|
// PrevSessionID is the id of current session.
|
|
PrevSessionID string `json:"prev_session_id"`
|
|
// AccessRequestID is an optional field that holds the id of an approved access request.
|
|
AccessRequestID string `json:"access_request_id"`
|
|
// Switchback is a flag to indicate if user is wanting to switchback from an assumed role
|
|
// back to their default role.
|
|
Switchback bool `json:"switchback"`
|
|
// ReloadUser is a flag to indicate if user needs to be refetched from the backend
|
|
// to apply new user changes e.g. user traits were updated.
|
|
ReloadUser bool `json:"reload_user"`
|
|
}
|
|
|
|
func (s *APIServer) createWebSession(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req WebSessionReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if req.PrevSessionID != "" {
|
|
sess, err := auth.ExtendWebSession(r.Context(), req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return sess, nil
|
|
}
|
|
|
|
sess, err := auth.CreateWebSession(r.Context(), req.User)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
|
|
}
|
|
|
|
func (s *APIServer) authenticateWebUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req AuthenticateUserRequest
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
req.Username = p.ByName("user")
|
|
sess, err := auth.AuthenticateWebUser(r.Context(), req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return rawMessage(services.MarshalWebSession(sess, services.WithVersion(version)))
|
|
}
|
|
|
|
func (s *APIServer) authenticateSSHUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req AuthenticateSSHRequest
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
req.Username = p.ByName("user")
|
|
return auth.AuthenticateSSHUser(r.Context(), req)
|
|
}
|
|
|
|
type upsertUserRawReq struct {
|
|
User json.RawMessage `json:"user"`
|
|
}
|
|
|
|
func (s *APIServer) upsertUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req *upsertUserRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
user, err := services.UnmarshalUser(req.User)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if err := services.ValidateUserRoles(r.Context(), user, auth); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
err = auth.UpsertUser(user)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message(fmt.Sprintf("'%v' user upserted", user.GetName())), nil
|
|
}
|
|
|
|
func (s *APIServer) getUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
user, err := auth.GetUser(p.ByName("user"), false)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return rawMessage(services.MarshalUser(user, services.WithVersion(version), services.PreserveResourceID()))
|
|
}
|
|
|
|
func rawMessage(data []byte, err error) (interface{}, error) {
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
m := json.RawMessage(data)
|
|
return &m, nil
|
|
}
|
|
|
|
func (s *APIServer) getUsers(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
users, err := auth.GetUsers(false)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
out := make([]json.RawMessage, len(users))
|
|
for i, user := range users {
|
|
data, err := services.MarshalUser(user, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
out[i] = data
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// DELETE IN: 5.2 REST method is replaced by grpc method with context.
|
|
func (s *APIServer) deleteUser(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
user := p.ByName("user")
|
|
if err := auth.DeleteUser(r.Context(), user); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message(fmt.Sprintf("user %q deleted", user)), nil
|
|
}
|
|
|
|
type generateHostCertReq struct {
|
|
Key []byte `json:"key"`
|
|
HostID string `json:"hostname"`
|
|
NodeName string `json:"node_name"`
|
|
Principals []string `json:"principals"`
|
|
ClusterName string `json:"auth_domain"`
|
|
Roles types.SystemRoles `json:"roles"`
|
|
TTL time.Duration `json:"ttl"`
|
|
}
|
|
|
|
func (s *APIServer) generateHostCert(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
|
|
var req *generateHostCertReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if len(req.Roles) != 1 {
|
|
return nil, trace.BadParameter("exactly one system role is required")
|
|
}
|
|
|
|
cert, err := auth.GenerateHostCert(r.Context(), req.Key, req.HostID, req.NodeName, req.Principals, req.ClusterName, req.Roles[0], req.TTL)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return string(cert), nil
|
|
}
|
|
|
|
func (s *APIServer) registerUsingToken(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
|
|
var req types.RegisterUsingTokenRequest
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
// Pass along the remote address the request came from to the registration function.
|
|
req.RemoteAddr = r.RemoteAddr
|
|
|
|
certs, err := auth.RegisterUsingToken(r.Context(), &req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return certs, nil
|
|
}
|
|
|
|
func (s *APIServer) rotateCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req RotateRequest
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := auth.RotateCertAuthority(r.Context(), req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
type upsertCertAuthorityRawReq struct {
|
|
CA json.RawMessage `json:"ca"`
|
|
TTL time.Duration `json:"ttl"`
|
|
}
|
|
|
|
// upsertCertAuthority creates or updates a cert authority.
|
|
// Deprecated: Replaced by teleport.v1.trust.TrustService/UpsertCertAuthority
|
|
// DELETE IN 14.0.0
|
|
func (s *APIServer) upsertCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req *upsertCertAuthorityRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
ca, err := services.UnmarshalCertAuthority(req.CA)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if req.TTL != 0 {
|
|
ca.SetExpiry(s.Now().UTC().Add(req.TTL))
|
|
}
|
|
|
|
if err := auth.UpsertCertAuthority(r.Context(), ca); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
type rotateExternalCertAuthorityRawReq struct {
|
|
CA json.RawMessage `json:"ca"`
|
|
}
|
|
|
|
func (s *APIServer) rotateExternalCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req rotateExternalCertAuthorityRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
ca, err := services.UnmarshalCertAuthority(req.CA)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := auth.RotateExternalCertAuthority(r.Context(), ca); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// getCertAuthorities returns all cert authorities that match the provided type.
|
|
// Deprecated: Replaced by teleport.v1.trust.TrustService/GetCertAuthorities
|
|
// DELETE IN 14.0.0
|
|
func (s *APIServer) getCertAuthorities(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
loadKeys, _, err := httplib.ParseBool(r.URL.Query(), "load_keys")
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
certs, err := auth.GetCertAuthorities(r.Context(), types.CertAuthType(p.ByName("type")), loadKeys)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items := make([]json.RawMessage, len(certs))
|
|
for i, cert := range certs {
|
|
data, err := services.MarshalCertAuthority(cert, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
// getCertAuthority returns a single matching cert authority.
|
|
// Deprecated: Replaced by teleport.v1.trust.TrustService/GetCertAuthority
|
|
// DELETE IN 14.0.0
|
|
func (s *APIServer) getCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
loadKeys, _, err := httplib.ParseBool(r.URL.Query(), "load_keys")
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
id := types.CertAuthID{
|
|
Type: types.CertAuthType(p.ByName("type")),
|
|
DomainName: p.ByName("domain"),
|
|
}
|
|
ca, err := auth.GetCertAuthority(r.Context(), id, loadKeys)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return rawMessage(services.MarshalCertAuthority(ca, services.WithVersion(version), services.PreserveResourceID()))
|
|
}
|
|
|
|
// deleteCertAuthority removes the matching cert authority.
|
|
// Deprecated: Replaced by teleport.v1.trust.TrustService/DeleteCertAuthority.
|
|
// DELETE IN 14.0.0
|
|
func (s *APIServer) deleteCertAuthority(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
id := types.CertAuthID{
|
|
DomainName: p.ByName("domain"),
|
|
Type: types.CertAuthType(p.ByName("type")),
|
|
}
|
|
if err := auth.DeleteCertAuthority(r.Context(), id); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message(fmt.Sprintf("cert '%v' deleted", id)), 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 types.ExternalIdentity `json:"identity"`
|
|
// Web session will be generated by auth server if requested in OIDCAuthRequest
|
|
Session json.RawMessage `json:"session,omitempty"`
|
|
// Cert will be generated by certificate authority
|
|
Cert []byte `json:"cert,omitempty"`
|
|
// TLSCert is PEM encoded TLS certificate
|
|
TLSCert []byte `json:"tls_cert,omitempty"`
|
|
// Req is original oidc auth request
|
|
Req 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(r.Context(), req.Query)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
raw := githubAuthRawResponse{
|
|
Username: response.Username,
|
|
Identity: response.Identity,
|
|
Cert: response.Cert,
|
|
TLSCert: response.TLSCert,
|
|
Req: response.Req,
|
|
}
|
|
if response.Session != nil {
|
|
rawSession, err := services.MarshalWebSession(
|
|
response.Session, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
raw.Session = rawSession
|
|
}
|
|
raw.HostSigners = make([]json.RawMessage, len(response.HostSigners))
|
|
for i, ca := range response.HostSigners {
|
|
data, err := services.MarshalCertAuthority(
|
|
ca, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
raw.HostSigners[i] = data
|
|
}
|
|
return &raw, nil
|
|
}
|
|
|
|
// HTTP GET /:version/events?query
|
|
//
|
|
// Query fields:
|
|
//
|
|
// 'from' : time filter in RFC3339 format
|
|
// 'to' : time filter in RFC3339 format
|
|
// ... : other fields are passed directly to the audit backend
|
|
func (s *APIServer) searchEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var err error
|
|
to := time.Now().In(time.UTC)
|
|
from := to.AddDate(0, -1, 0) // one month ago
|
|
query := r.URL.Query()
|
|
// parse 'to' and 'from' params:
|
|
fromStr := query.Get("from")
|
|
if fromStr != "" {
|
|
from, err = time.Parse(time.RFC3339, fromStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("from")
|
|
}
|
|
}
|
|
toStr := query.Get("to")
|
|
if toStr != "" {
|
|
to, err = time.Parse(time.RFC3339, toStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("to")
|
|
}
|
|
}
|
|
var limit int
|
|
limitStr := query.Get("limit")
|
|
if limitStr != "" {
|
|
limit, err = strconv.Atoi(limitStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("failed to parse limit: %q", limit)
|
|
}
|
|
}
|
|
|
|
eventTypes := query[events.EventType]
|
|
eventsList, _, err := auth.SearchEvents(from, to, apidefaults.Namespace, eventTypes, limit, types.EventOrderDescending, "")
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return eventsList, nil
|
|
}
|
|
|
|
// searchSessionEvents only allows searching audit log for events related to session playback.
|
|
func (s *APIServer) searchSessionEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var err error
|
|
|
|
// default values for "to" and "from" fields
|
|
to := time.Now().In(time.UTC) // now
|
|
from := to.AddDate(0, -1, 0) // one month ago
|
|
|
|
// parse query for "to" and "from"
|
|
query := r.URL.Query()
|
|
fromStr := query.Get("from")
|
|
if fromStr != "" {
|
|
from, err = time.Parse(time.RFC3339, fromStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("from")
|
|
}
|
|
}
|
|
toStr := query.Get("to")
|
|
if toStr != "" {
|
|
to, err = time.Parse(time.RFC3339, toStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("to")
|
|
}
|
|
}
|
|
var limit int
|
|
limitStr := query.Get("limit")
|
|
if limitStr != "" {
|
|
limit, err = strconv.Atoi(limitStr)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("failed to parse limit: %q", limit)
|
|
}
|
|
}
|
|
// only pull back start and end events to build list of completed sessions
|
|
eventsList, _, err := auth.SearchSessionEvents(from, to, limit, types.EventOrderDescending, "", nil, "")
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return eventsList, nil
|
|
}
|
|
|
|
// HTTP GET /:version/sessions/:id/stream?offset=x&bytes=y
|
|
// Query parameters:
|
|
//
|
|
// "offset" : bytes from the beginning
|
|
// "bytes" : number of bytes to read (it won't return more than 512Kb)
|
|
func (s *APIServer) getSessionChunk(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
sid, err := session.ParseID(p.ByName("id"))
|
|
if err != nil {
|
|
return nil, trace.BadParameter("missing parameter id")
|
|
}
|
|
namespace := p.ByName("namespace")
|
|
if !types.IsValidNamespace(namespace) {
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
}
|
|
|
|
// "offset bytes" query param
|
|
offsetBytes, err := strconv.Atoi(r.URL.Query().Get("offset"))
|
|
if err != nil || offsetBytes < 0 {
|
|
offsetBytes = 0
|
|
}
|
|
// "max bytes" query param
|
|
max, err := strconv.Atoi(r.URL.Query().Get("bytes"))
|
|
if err != nil || offsetBytes < 0 {
|
|
offsetBytes = 0
|
|
}
|
|
log.Debugf("apiserver.GetSessionChunk(%v, %v, offset=%d)", namespace, *sid, offsetBytes)
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
buffer, err := auth.GetSessionChunk(namespace, *sid, offsetBytes, max)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if _, err = w.Write(buffer); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
return nil, nil
|
|
}
|
|
|
|
// HTTP GET /:version/sessions/:id/events?maxage=n
|
|
// Query:
|
|
//
|
|
// 'after' : cursor value to return events newer than N. Defaults to 0, (return all)
|
|
func (s *APIServer) getSessionEvents(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
sid, err := session.ParseID(p.ByName("id"))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
namespace := p.ByName("namespace")
|
|
if !types.IsValidNamespace(namespace) {
|
|
return nil, trace.BadParameter("invalid namespace %q", namespace)
|
|
}
|
|
afterN, err := strconv.Atoi(r.URL.Query().Get("after"))
|
|
if err != nil {
|
|
afterN = 0
|
|
}
|
|
|
|
return auth.GetSessionEvents(namespace, *sid, afterN)
|
|
}
|
|
|
|
type upsertNamespaceReq struct {
|
|
Namespace types.Namespace `json:"namespace"`
|
|
}
|
|
|
|
func (s *APIServer) upsertNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
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
|
|
}
|
|
|
|
func (s *APIServer) getNamespaces(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
namespaces, err := auth.GetNamespaces()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return namespaces, nil
|
|
}
|
|
|
|
func (s *APIServer) getNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
name := p.ByName("namespace")
|
|
if !types.IsValidNamespace(name) {
|
|
return nil, trace.BadParameter("invalid namespace %q", name)
|
|
}
|
|
|
|
namespace, err := auth.GetNamespace(name)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return namespace, nil
|
|
}
|
|
|
|
func (s *APIServer) deleteNamespace(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
name := p.ByName("namespace")
|
|
if !types.IsValidNamespace(name) {
|
|
return nil, trace.BadParameter("invalid namespace %q", name)
|
|
}
|
|
|
|
err := auth.DeleteNamespace(name)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
func (s *APIServer) getClusterName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
cn, err := auth.GetClusterName()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return rawMessage(services.MarshalClusterName(cn, services.WithVersion(version), services.PreserveResourceID()))
|
|
}
|
|
|
|
type setClusterNameReq struct {
|
|
ClusterName json.RawMessage `json:"cluster_name"`
|
|
}
|
|
|
|
func (s *APIServer) setClusterName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req setClusterNameReq
|
|
|
|
err := httplib.ReadJSON(r, &req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
cn, err := services.UnmarshalClusterName(req.ClusterName)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
err = auth.SetClusterName(cn)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return message(fmt.Sprintf("cluster name set: %+v", cn)), nil
|
|
}
|
|
|
|
func (s *APIServer) getStaticTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
st, err := auth.GetStaticTokens()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return rawMessage(services.MarshalStaticTokens(st, services.WithVersion(version), services.PreserveResourceID()))
|
|
}
|
|
|
|
func (s *APIServer) deleteStaticTokens(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteStaticTokens()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
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) {
|
|
var req setStaticTokensReq
|
|
|
|
err := httplib.ReadJSON(r, &req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
st, err := services.UnmarshalStaticTokens(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
|
|
}
|
|
|
|
type upsertTunnelConnectionRawReq struct {
|
|
TunnelConnection json.RawMessage `json:"tunnel_connection"`
|
|
}
|
|
|
|
// upsertTunnelConnection updates or inserts tunnel connection
|
|
func (s *APIServer) upsertTunnelConnection(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req upsertTunnelConnectionRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
conn, err := services.UnmarshalTunnelConnection(req.TunnelConnection)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := auth.UpsertTunnelConnection(conn); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// getTunnelConnections returns a list of tunnel connections from a cluster
|
|
func (s *APIServer) getTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
conns, err := auth.GetTunnelConnections(p.ByName("cluster"))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items := make([]json.RawMessage, len(conns))
|
|
for i, conn := range conns {
|
|
data, err := services.MarshalTunnelConnection(conn, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
// getAllTunnelConnections returns a list of tunnel connections from a cluster
|
|
func (s *APIServer) getAllTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
conns, err := auth.GetAllTunnelConnections()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items := make([]json.RawMessage, len(conns))
|
|
for i, conn := range conns {
|
|
data, err := services.MarshalTunnelConnection(conn, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// deleteTunnelConnections deletes all tunnel connections for cluster
|
|
func (s *APIServer) deleteTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteTunnelConnections(p.ByName("cluster"))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// deleteAllTunnelConnections deletes all tunnel connections
|
|
func (s *APIServer) deleteAllTunnelConnections(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteAllTunnelConnections()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
type createRemoteClusterRawReq struct {
|
|
// RemoteCluster is marshaled remote cluster resource
|
|
RemoteCluster json.RawMessage `json:"remote_cluster"`
|
|
}
|
|
|
|
// createRemoteCluster creates remote cluster
|
|
func (s *APIServer) createRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req createRemoteClusterRawReq
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
conn, err := services.UnmarshalRemoteCluster(req.RemoteCluster)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if err := auth.CreateRemoteCluster(conn); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// getRemoteClusters returns a list of remote clusters
|
|
func (s *APIServer) getRemoteClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
clusters, err := auth.GetRemoteClusters()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items := make([]json.RawMessage, len(clusters))
|
|
for i, cluster := range clusters {
|
|
data, err := services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
items[i] = data
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
// getRemoteCluster returns a remote cluster by name
|
|
func (s *APIServer) getRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
cluster, err := auth.GetRemoteCluster(p.ByName("cluster"))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return rawMessage(services.MarshalRemoteCluster(cluster, services.WithVersion(version), services.PreserveResourceID()))
|
|
}
|
|
|
|
// deleteRemoteCluster deletes remote cluster by name
|
|
func (s *APIServer) deleteRemoteCluster(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteRemoteCluster(r.Context(), p.ByName("cluster"))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
// deleteAllRemoteClusters deletes all remote clusters
|
|
func (s *APIServer) deleteAllRemoteClusters(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
err := auth.DeleteAllRemoteClusters()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return message("ok"), nil
|
|
}
|
|
|
|
func (s *APIServer) processKubeCSR(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
|
|
var req KubeCSR
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
re, err := auth.ProcessKubeCSR(req)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return re, nil
|
|
}
|
|
|
|
func message(msg string) map[string]interface{} {
|
|
return map[string]interface{}{"message": msg}
|
|
}
|