Files
teleport/lib/auth/clt.go
T
rosstimothy b0dc78610e Migrate upserting CAs to gRPC (#23140)
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
2023-03-16 14:46:43 +00:00

1775 lines
67 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 (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/gravitational/roundtrip"
"github.com/gravitational/trace"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/breaker"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/okta"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
apidefaults "github.com/gravitational/teleport/api/defaults"
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1"
pluginspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/plugins/v1"
samlidppb "github.com/gravitational/teleport/api/gen/proto/go/teleport/samlidp/v1"
tracehttp "github.com/gravitational/teleport/api/observability/tracing/http"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/utils"
)
const (
// CurrentVersion is a current API version
CurrentVersion = types.V2
// MissingNamespaceError indicates that the client failed to
// provide the namespace in the request.
MissingNamespaceError = "missing required parameter: namespace"
)
// Client is the Auth API client. It works by connecting to auth servers
// via gRPC and HTTP.
//
// When Teleport servers connect to auth API, they usually establish an SSH
// tunnel first, and then do HTTP-over-SSH. This client is wrapped by auth.TunClient
// in lib/auth/tun.go
//
// NOTE: This client is being deprecated in favor of the gRPC Client in
// teleport/api/client. This Client should only be used internally, or for
// functionality that hasn't been ported to the new client yet.
type Client struct {
// APIClient is used to make gRPC requests to the server
*APIClient
// HTTPClient is used to make http requests to the server
*HTTPClient
}
// Make sure Client implements all the necessary methods.
var _ ClientI = &Client{}
// NewClient creates a new API client with a connection to a Teleport server.
//
// The client will use the first credentials and the given dialer. If
// no dialer is given, the first address will be used. This address must
// be an auth server address.
//
// NOTE: This client is being deprecated in favor of the gRPC Client in
// teleport/api/client. This Client should only be used internally, or for
// functionality that hasn't been ported to the new client yet.
func NewClient(cfg client.Config, params ...roundtrip.ClientParam) (*Client, error) {
cfg.DialInBackground = true
if err := cfg.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
apiClient, err := client.New(cfg.Context, cfg)
if err != nil {
return nil, trace.Wrap(err)
}
// apiClient configures the tls.Config, so we clone it and reuse it for http.
tlsConfig := apiClient.Config().Clone()
httpClient, err := NewHTTPClient(cfg, tlsConfig, params...)
if err != nil {
return nil, trace.Wrap(err)
}
return &Client{
APIClient: apiClient,
HTTPClient: httpClient,
}, nil
}
// APIClient is aliased here so that it can be embedded in Client.
type APIClient = client.Client
// HTTPClient is a teleport HTTP API client.
type HTTPClient struct {
roundtrip.Client
// transport defines the methods by which the client can reach the server.
transport *http.Transport
// TLS holds the TLS config for the http client.
tls *tls.Config
}
// NewHTTPClient creates a new HTTP client with TLS authentication and the given dialer.
func NewHTTPClient(cfg client.Config, tls *tls.Config, params ...roundtrip.ClientParam) (*HTTPClient, error) {
if err := cfg.CheckAndSetDefaults(); err != nil {
return nil, err
}
dialer := cfg.Dialer
if dialer == nil {
if len(cfg.Addrs) == 0 {
return nil, trace.BadParameter("no addresses to dial")
}
contextDialer := client.NewDialer(cfg.Context, cfg.KeepAlivePeriod, cfg.DialTimeout, client.WithTLSConfig(tls))
dialer = client.ContextDialerFunc(func(ctx context.Context, network, _ string) (conn net.Conn, err error) {
for _, addr := range cfg.Addrs {
conn, err = contextDialer.DialContext(ctx, network, addr)
if err == nil {
return conn, nil
}
}
// not wrapping on purpose to preserve the original error
return nil, err
})
}
// Set the next protocol. This is needed due to the Auth Server using a
// multiplexer for protocol detection. Unless next protocol is specified
// it will attempt to upgrade to HTTP2 and at that point there is no way
// to distinguish between HTTP2/JSON or GPRC.
tls.NextProtos = []string{teleport.HTTPNextProtoTLS}
// Configure ALPN SNI direct dial TLS routing information used by ALPN SNI proxy in order to
// dial auth service without using SSH tunnels.
tls = client.ConfigureALPN(tls, cfg.ALPNSNIAuthDialClusterName)
transport := &http.Transport{
// notice that below roundtrip.Client is passed
// teleport.APIDomain as an address for the API server, this is
// to make sure client verifies the DNS name of the API server and
// custom DialContext overrides this DNS name to the real address.
// In addition this dialer tries multiple addresses if provided
DialContext: dialer.DialContext,
ResponseHeaderTimeout: apidefaults.DefaultIOTimeout,
TLSClientConfig: tls,
// Increase the size of the connection pool. This substantially improves the
// performance of Teleport under load as it reduces the number of TLS
// handshakes performed.
MaxIdleConns: defaults.HTTPMaxIdleConns,
MaxIdleConnsPerHost: defaults.HTTPMaxIdleConnsPerHost,
// Limit the total number of connections to the Auth Server. Some hosts allow a low
// number of connections per process (ulimit) to a host. This is a problem for
// enhanced session recording auditing which emits so many events to the
// Audit Log (using the Auth Client) that the connection pool often does not
// have a free connection to return, so just opens a new one. This quickly
// leads to hitting the OS limit and the client returning out of file
// descriptors error.
MaxConnsPerHost: defaults.HTTPMaxConnsPerHost,
// IdleConnTimeout defines the maximum amount of time before idle connections
// are closed. Leaving this unset will lead to connections open forever and
// will cause memory leaks in a long running process.
IdleConnTimeout: defaults.HTTPIdleTimeout,
}
cb, err := breaker.New(cfg.CircuitBreakerConfig)
if err != nil {
return nil, trace.Wrap(err)
}
clientParams := append(
[]roundtrip.ClientParam{
roundtrip.HTTPClient(&http.Client{
Timeout: defaults.HTTPRequestTimeout,
Transport: tracehttp.NewTransport(breaker.NewRoundTripper(cb, transport)),
}),
roundtrip.SanitizerEnabled(true),
},
params...,
)
// Since the client uses a custom dialer and SNI is used for TLS handshake, the address
// used here is arbitrary as it just needs to be set to pass http request validation.
httpClient, err := roundtrip.NewClient("https://"+constants.APIDomain, CurrentVersion, clientParams...)
if err != nil {
return nil, trace.Wrap(err)
}
return &HTTPClient{
Client: *httpClient,
transport: transport,
tls: tls,
}, nil
}
// Close closes the HTTP client connection to the auth server.
func (c *HTTPClient) Close() {
c.transport.CloseIdleConnections()
}
// TLSConfig returns the HTTP client's TLS config.
func (c *HTTPClient) TLSConfig() *tls.Config {
return c.tls
}
// GetTransport returns the HTTP client's transport.
func (c *HTTPClient) GetTransport() *http.Transport {
return c.transport
}
// ClientTimeout sets idle and dial timeouts of the HTTP transport
// used by the client.
func ClientTimeout(timeout time.Duration) roundtrip.ClientParam {
return func(c *roundtrip.Client) error {
transport, ok := (c.HTTPClient().Transport).(*http.Transport)
if !ok {
return nil
}
transport.IdleConnTimeout = timeout
transport.ResponseHeaderTimeout = timeout
return nil
}
}
// PostJSON is a generic method that issues http POST request to the server
func (c *Client) PostJSON(ctx context.Context, endpoint string, val interface{}) (*roundtrip.Response, error) {
return httplib.ConvertResponse(c.Client.PostJSON(ctx, endpoint, val))
}
// PutJSON is a generic method that issues http PUT request to the server
func (c *Client) PutJSON(ctx context.Context, endpoint string, val interface{}) (*roundtrip.Response, error) {
return httplib.ConvertResponse(c.Client.PutJSON(ctx, endpoint, val))
}
// PostForm is a generic method that issues http POST request to the server
func (c *Client) PostForm(ctx context.Context, endpoint string, vals url.Values, files ...roundtrip.File) (*roundtrip.Response, error) {
return httplib.ConvertResponse(c.Client.PostForm(ctx, endpoint, vals, files...))
}
// Get issues http GET request to the server
func (c *Client) Get(ctx context.Context, u string, params url.Values) (*roundtrip.Response, error) {
return httplib.ConvertResponse(c.Client.Get(ctx, u, params))
}
// Delete issues http Delete Request to the server
func (c *Client) Delete(ctx context.Context, u string) (*roundtrip.Response, error) {
return httplib.ConvertResponse(c.Client.Delete(ctx, u))
}
// ProcessKubeCSR processes CSR request against Kubernetes CA, returns
// signed certificate if successful.
func (c *Client) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) {
if err := req.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
out, err := c.PostJSON(context.TODO(), c.Endpoint("kube", "csr"), req)
if err != nil {
return nil, trace.Wrap(err)
}
var re KubeCSRResponse
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
return nil, trace.Wrap(err)
}
return &re, nil
}
func (c *Client) Close() error {
c.HTTPClient.Close()
return c.APIClient.Close()
}
// CreateCertAuthority not implemented: can only be called locally.
func (c *Client) CreateCertAuthority(ctx context.Context, ca types.CertAuthority) error {
return trace.NotImplemented(notImplementedMessage)
}
// RotateCertAuthority starts or restarts certificate authority rotation process.
func (c *Client) RotateCertAuthority(ctx context.Context, req RotateRequest) error {
_, err := c.PostJSON(ctx, c.Endpoint("authorities", string(req.Type), "rotate"), req)
return trace.Wrap(err)
}
// RotateExternalCertAuthority rotates external certificate authority,
// this method is used to update only public keys and certificates of the
// the certificate authorities of trusted clusters.
func (c *Client) RotateExternalCertAuthority(ctx context.Context, ca types.CertAuthority) error {
if err := services.ValidateCertAuthority(ca); err != nil {
return trace.Wrap(err)
}
data, err := services.MarshalCertAuthority(ca)
if err != nil {
return trace.Wrap(err)
}
_, err = c.PostJSON(ctx, c.Endpoint("authorities", string(ca.GetType()), "rotate", "external"),
&rotateExternalCertAuthorityRawReq{CA: data})
return trace.Wrap(err)
}
// UpsertCertAuthority updates or inserts new cert authority
func (c *Client) UpsertCertAuthority(ctx context.Context, ca types.CertAuthority) error {
if err := services.ValidateCertAuthority(ca); err != nil {
return trace.Wrap(err)
}
_, err := c.APIClient.UpsertCertAuthority(ctx, ca)
switch {
case err == nil:
return nil
// Fallback to HTTP API
// DELETE IN 14.0.0
case trace.IsNotImplemented(err):
data, err := services.MarshalCertAuthority(ca)
if err != nil {
return trace.Wrap(err)
}
_, err = c.PostJSON(ctx, c.Endpoint("authorities", string(ca.GetType())),
&upsertCertAuthorityRawReq{CA: data})
return trace.Wrap(err)
default:
return trace.Wrap(err)
}
}
// CompareAndSwapCertAuthority updates existing cert authority if the existing cert authority
// value matches the value stored in the backend.
func (c *Client) CompareAndSwapCertAuthority(new, existing types.CertAuthority) error {
return trace.BadParameter("this function is not supported on the client")
}
// GetCertAuthorities returns a list of certificate authorities
func (c *Client) GetCertAuthorities(ctx context.Context, caType types.CertAuthType, loadKeys bool) ([]types.CertAuthority, error) {
if err := caType.Check(); err != nil {
return nil, trace.Wrap(err)
}
cas, err := c.APIClient.GetCertAuthorities(ctx, caType, loadKeys)
switch {
case err == nil:
return cas, nil
// Fallback to HTTP API
// DELETE IN 14.0.0
case trace.IsNotImplemented(err):
resp, err := c.Get(ctx, c.Endpoint("authorities", string(caType)), url.Values{
"load_keys": []string{fmt.Sprintf("%t", loadKeys)},
})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(resp.Bytes(), &items); err != nil {
return nil, err
}
cas := make([]types.CertAuthority, 0, len(items))
for _, raw := range items {
ca, err := services.UnmarshalCertAuthority(raw)
if err != nil {
return nil, trace.Wrap(err)
}
cas = append(cas, ca)
}
return cas, nil
default:
return nil, trace.Wrap(err)
}
}
// GetCertAuthority returns certificate authority by given id. Parameter loadSigningKeys
// controls if signing keys are loaded
func (c *Client) GetCertAuthority(ctx context.Context, id types.CertAuthID, loadSigningKeys bool) (types.CertAuthority, error) {
if err := id.Check(); err != nil {
return nil, trace.Wrap(err)
}
ca, err := c.APIClient.GetCertAuthority(ctx, id, loadSigningKeys)
switch {
case err == nil:
return ca, nil
// Fallback to HTTP API
// DELETE IN 14.0.0
case trace.IsNotImplemented(err):
out, err := c.Get(ctx, c.Endpoint("authorities", string(id.Type), id.DomainName), url.Values{
"load_keys": []string{fmt.Sprintf("%t", loadSigningKeys)},
})
if err != nil {
return nil, trace.Wrap(err)
}
ca, err := services.UnmarshalCertAuthority(out.Bytes())
return ca, trace.Wrap(err)
default:
return nil, trace.Wrap(err)
}
}
// DeleteCertAuthority deletes cert authority by ID
func (c *Client) DeleteCertAuthority(ctx context.Context, id types.CertAuthID) error {
if err := id.Check(); err != nil {
return trace.Wrap(err)
}
err := c.APIClient.DeleteCertAuthority(ctx, id)
switch {
case err == nil:
return nil
// Fallback to HTTP API
// DELETE IN 14.0.0
case trace.IsNotImplemented(err):
_, err := c.Delete(ctx, c.Endpoint("authorities", string(id.Type), id.DomainName))
return trace.Wrap(err)
default:
return trace.Wrap(err)
}
}
// ActivateCertAuthority not implemented: can only be called locally.
func (c *Client) ActivateCertAuthority(id types.CertAuthID) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeactivateCertAuthority not implemented: can only be called locally.
func (c *Client) DeactivateCertAuthority(id types.CertAuthID) error {
return trace.NotImplemented(notImplementedMessage)
}
// UpdateUserCARoleMap not implemented: can only be called locally.
func (c *Client) UpdateUserCARoleMap(ctx context.Context, name string, roleMap types.RoleMap, activated bool) error {
return trace.NotImplemented(notImplementedMessage)
}
// RegisterUsingToken calls the auth service API to register a new node using a registration token
// which was previously issued via CreateToken/UpsertToken.
func (c *Client) RegisterUsingToken(ctx context.Context, req *types.RegisterUsingTokenRequest) (*proto.Certs, error) {
if err := req.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
out, err := c.PostJSON(ctx, c.Endpoint("tokens", "register"), req)
if err != nil {
return nil, trace.Wrap(err)
}
var certs proto.Certs
if err := json.Unmarshal(out.Bytes(), &certs); err != nil {
return nil, trace.Wrap(err)
}
return &certs, nil
}
// DELETE IN: 5.1.0
//
// This logic has been moved to KeepAliveServer.
//
// KeepAliveNode updates node keep alive information.
func (c *Client) KeepAliveNode(ctx context.Context, keepAlive types.KeepAlive) error {
return trace.BadParameter("not implemented, use StreamKeepAlives instead")
}
// KeepAliveServer not implemented: can only be called locally.
func (c *Client) KeepAliveServer(ctx context.Context, keepAlive types.KeepAlive) error {
return trace.BadParameter("not implemented, use StreamKeepAlives instead")
}
// UpsertReverseTunnel is used by admins to create a new reverse tunnel
// to the remote proxy to bypass firewall restrictions
func (c *Client) UpsertReverseTunnel(tunnel types.ReverseTunnel) error {
data, err := services.MarshalReverseTunnel(tunnel)
if err != nil {
return trace.Wrap(err)
}
args := &upsertReverseTunnelRawReq{
ReverseTunnel: data,
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("reversetunnels"), args)
return trace.Wrap(err)
}
// GetReverseTunnel not implemented: can only be called locally.
func (c *Client) GetReverseTunnel(name string, opts ...services.MarshalOption) (types.ReverseTunnel, error) {
return nil, trace.NotImplemented(notImplementedMessage)
}
// GetReverseTunnels returns the list of created reverse tunnels
func (c *Client) GetReverseTunnels(ctx context.Context, opts ...services.MarshalOption) ([]types.ReverseTunnel, error) {
out, err := c.Get(ctx, c.Endpoint("reversetunnels"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
tunnels := make([]types.ReverseTunnel, len(items))
for i, raw := range items {
tunnel, err := services.UnmarshalReverseTunnel(raw)
if err != nil {
return nil, trace.Wrap(err)
}
tunnels[i] = tunnel
}
return tunnels, nil
}
// DeleteReverseTunnel deletes reverse tunnel by domain name
func (c *Client) DeleteReverseTunnel(domainName string) error {
// this is to avoid confusing error in case if domain empty for example
// HTTP route will fail producing generic not found error
// instead we catch the error here
if strings.TrimSpace(domainName) == "" {
return trace.BadParameter("empty domain name")
}
_, err := c.Delete(context.TODO(), c.Endpoint("reversetunnels", domainName))
return trace.Wrap(err)
}
// UpsertTunnelConnection upserts tunnel connection
func (c *Client) UpsertTunnelConnection(conn types.TunnelConnection) error {
data, err := services.MarshalTunnelConnection(conn)
if err != nil {
return trace.Wrap(err)
}
args := &upsertTunnelConnectionRawReq{
TunnelConnection: data,
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("tunnelconnections"), args)
return trace.Wrap(err)
}
// GetTunnelConnections returns tunnel connections for a given cluster
func (c *Client) GetTunnelConnections(clusterName string, opts ...services.MarshalOption) ([]types.TunnelConnection, error) {
if clusterName == "" {
return nil, trace.BadParameter("missing cluster name parameter")
}
out, err := c.Get(context.TODO(), c.Endpoint("tunnelconnections", clusterName), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
conns := make([]types.TunnelConnection, len(items))
for i, raw := range items {
conn, err := services.UnmarshalTunnelConnection(raw)
if err != nil {
return nil, trace.Wrap(err)
}
conns[i] = conn
}
return conns, nil
}
// GetAllTunnelConnections returns all tunnel connections
func (c *Client) GetAllTunnelConnections(opts ...services.MarshalOption) ([]types.TunnelConnection, error) {
out, err := c.Get(context.TODO(), c.Endpoint("tunnelconnections"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
conns := make([]types.TunnelConnection, len(items))
for i, raw := range items {
conn, err := services.UnmarshalTunnelConnection(raw)
if err != nil {
return nil, trace.Wrap(err)
}
conns[i] = conn
}
return conns, nil
}
// DeleteTunnelConnection deletes tunnel connection by name
func (c *Client) DeleteTunnelConnection(clusterName string, connName string) error {
if clusterName == "" {
return trace.BadParameter("missing parameter cluster name")
}
if connName == "" {
return trace.BadParameter("missing parameter connection name")
}
_, err := c.Delete(context.TODO(), c.Endpoint("tunnelconnections", clusterName, connName))
return trace.Wrap(err)
}
// DeleteTunnelConnections deletes all tunnel connections for cluster
func (c *Client) DeleteTunnelConnections(clusterName string) error {
if clusterName == "" {
return trace.BadParameter("missing parameter cluster name")
}
_, err := c.Delete(context.TODO(), c.Endpoint("tunnelconnections", clusterName))
return trace.Wrap(err)
}
// DeleteAllTokens not implemented: can only be called locally.
func (c *Client) DeleteAllTokens() error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllTunnelConnections deletes all tunnel connections
func (c *Client) DeleteAllTunnelConnections() error {
_, err := c.Delete(context.TODO(), c.Endpoint("tunnelconnections"))
return trace.Wrap(err)
}
// AddUserLoginAttempt logs user login attempt
func (c *Client) AddUserLoginAttempt(user string, attempt services.LoginAttempt, ttl time.Duration) error {
panic("not implemented")
}
// GetUserLoginAttempts returns user login attempts
func (c *Client) GetUserLoginAttempts(user string) ([]services.LoginAttempt, error) {
panic("not implemented")
}
// GetRemoteClusters returns a list of remote clusters
func (c *Client) GetRemoteClusters(opts ...services.MarshalOption) ([]types.RemoteCluster, error) {
out, err := c.Get(context.TODO(), c.Endpoint("remoteclusters"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
conns := make([]types.RemoteCluster, len(items))
for i, raw := range items {
conn, err := services.UnmarshalRemoteCluster(raw)
if err != nil {
return nil, trace.Wrap(err)
}
conns[i] = conn
}
return conns, nil
}
// GetRemoteCluster returns a remote cluster by name
func (c *Client) GetRemoteCluster(clusterName string) (types.RemoteCluster, error) {
if clusterName == "" {
return nil, trace.BadParameter("missing cluster name")
}
out, err := c.Get(context.TODO(), c.Endpoint("remoteclusters", clusterName), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalRemoteCluster(out.Bytes())
}
// DeleteRemoteCluster deletes remote cluster by name
func (c *Client) DeleteRemoteCluster(ctx context.Context, clusterName string) error {
if clusterName == "" {
return trace.BadParameter("missing parameter cluster name")
}
_, err := c.Delete(ctx, c.Endpoint("remoteclusters", clusterName))
return trace.Wrap(err)
}
// DeleteAllRemoteClusters deletes all remote clusters
func (c *Client) DeleteAllRemoteClusters() error {
_, err := c.Delete(context.TODO(), c.Endpoint("remoteclusters"))
return trace.Wrap(err)
}
// CreateRemoteCluster creates remote cluster resource
func (c *Client) CreateRemoteCluster(rc types.RemoteCluster) error {
data, err := services.MarshalRemoteCluster(rc)
if err != nil {
return trace.Wrap(err)
}
args := &createRemoteClusterRawReq{
RemoteCluster: data,
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("remoteclusters"), args)
return trace.Wrap(err)
}
// UpsertAuthServer is used by auth servers to report their presence
// to other auth servers in form of hearbeat expiring after ttl period.
func (c *Client) UpsertAuthServer(s types.Server) error {
data, err := services.MarshalServer(s)
if err != nil {
return trace.Wrap(err)
}
args := &upsertServerRawReq{
Server: data,
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("authservers"), args)
return trace.Wrap(err)
}
// GetAuthServers returns the list of auth servers registered in the cluster.
func (c *Client) GetAuthServers() ([]types.Server, error) {
out, err := c.Get(context.TODO(), c.Endpoint("authservers"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
re := make([]types.Server, len(items))
for i, raw := range items {
server, err := services.UnmarshalServer(raw, types.KindAuthServer)
if err != nil {
return nil, trace.Wrap(err)
}
re[i] = server
}
return re, nil
}
// DeleteAllAuthServers not implemented: can only be called locally.
func (c *Client) DeleteAllAuthServers() error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAuthServer not implemented: can only be called locally.
func (c *Client) DeleteAuthServer(name string) error {
return trace.NotImplemented(notImplementedMessage)
}
// UpsertProxy is used by proxies to report their presence
// to other auth servers in form of heartbeat expiring after ttl period.
func (c *Client) UpsertProxy(s types.Server) error {
data, err := services.MarshalServer(s)
if err != nil {
return trace.Wrap(err)
}
args := &upsertServerRawReq{
Server: data,
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("proxies"), args)
return trace.Wrap(err)
}
// GetProxies returns the list of auth servers registered in the cluster.
func (c *Client) GetProxies() ([]types.Server, error) {
out, err := c.Get(context.TODO(), c.Endpoint("proxies"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var items []json.RawMessage
if err := json.Unmarshal(out.Bytes(), &items); err != nil {
return nil, trace.Wrap(err)
}
re := make([]types.Server, len(items))
for i, raw := range items {
server, err := services.UnmarshalServer(raw, types.KindProxy)
if err != nil {
return nil, trace.Wrap(err)
}
re[i] = server
}
return re, nil
}
// DeleteAllProxies deletes all proxies
func (c *Client) DeleteAllProxies() error {
_, err := c.Delete(context.TODO(), c.Endpoint("proxies"))
if err != nil {
return trace.Wrap(err)
}
return nil
}
// DeleteProxy deletes proxy by name
func (c *Client) DeleteProxy(name string) error {
if name == "" {
return trace.BadParameter("missing parameter name")
}
_, err := c.Delete(context.TODO(), c.Endpoint("proxies", name))
if err != nil {
return trace.Wrap(err)
}
return nil
}
// UpsertUser user updates user entry.
func (c *Client) UpsertUser(user types.User) error {
data, err := services.MarshalUser(user)
if err != nil {
return trace.Wrap(err)
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("users"), &upsertUserRawReq{User: data})
return trace.Wrap(err)
}
// CompareAndSwapUser not implemented: can only be called locally
func (c *Client) CompareAndSwapUser(ctx context.Context, new, expected types.User) error {
return trace.NotImplemented(notImplementedMessage)
}
// ExtendWebSession creates a new web session for a user based on another
// valid web session
func (c *Client) ExtendWebSession(ctx context.Context, req WebSessionReq) (types.WebSession, error) {
out, err := c.PostJSON(ctx, c.Endpoint("users", req.User, "web", "sessions"), req)
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalWebSession(out.Bytes())
}
// CreateWebSession creates a new web session for a user
func (c *Client) CreateWebSession(ctx context.Context, user string) (types.WebSession, error) {
out, err := c.PostJSON(
ctx,
c.Endpoint("users", user, "web", "sessions"),
WebSessionReq{User: user},
)
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalWebSession(out.Bytes())
}
// AuthenticateWebUser authenticates web user, creates and returns web session
// in case if authentication is successful
func (c *Client) AuthenticateWebUser(ctx context.Context, req AuthenticateUserRequest) (types.WebSession, error) {
out, err := c.PostJSON(
ctx,
c.Endpoint("users", req.Username, "web", "authenticate"),
req,
)
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalWebSession(out.Bytes())
}
// AuthenticateSSHUser authenticates SSH console user, creates and returns a pair of signed TLS and SSH
// short lived certificates as a result
func (c *Client) AuthenticateSSHUser(ctx context.Context, req AuthenticateSSHRequest) (*SSHLoginResponse, error) {
out, err := c.PostJSON(
ctx,
c.Endpoint("users", req.Username, "ssh", "authenticate"),
req,
)
if err != nil {
return nil, trace.Wrap(err)
}
var re SSHLoginResponse
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
return nil, trace.Wrap(err)
}
return &re, nil
}
// GetWebSessionInfo checks if a web sesion is valid, returns session id in case if
// it is valid, or error otherwise.
func (c *Client) GetWebSessionInfo(ctx context.Context, user, sessionID string) (types.WebSession, error) {
out, err := c.Get(
ctx,
c.Endpoint("users", user, "web", "sessions", sessionID), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalWebSession(out.Bytes())
}
// DeleteWebSession deletes the web session specified with sid for the given user
func (c *Client) DeleteWebSession(ctx context.Context, user string, sid string) error {
_, err := c.Delete(ctx, c.Endpoint("users", user, "web", "sessions", sid))
return trace.Wrap(err)
}
// GenerateHostCert takes the public key in the Open SSH “authorized_keys“
// plain text format, signs it using Host Certificate Authority private key and returns the
// resulting certificate.
func (c *Client) GenerateHostCert(
ctx context.Context, key []byte, hostID, nodeName string, principals []string, clusterName string, role types.SystemRole, ttl time.Duration,
) ([]byte, error) {
out, err := c.PostJSON(ctx, c.Endpoint("ca", "host", "certs"),
generateHostCertReq{
Key: key,
HostID: hostID,
NodeName: nodeName,
Principals: principals,
ClusterName: clusterName,
Roles: types.SystemRoles{role},
TTL: ttl,
})
if err != nil {
return nil, trace.Wrap(err)
}
var cert string
if err := json.Unmarshal(out.Bytes(), &cert); err != nil {
return nil, err
}
return []byte(cert), nil
}
// ValidateOIDCAuthCallback validates OIDC auth callback returned from redirect
func (c *Client) ValidateOIDCAuthCallback(ctx context.Context, q url.Values) (*OIDCAuthResponse, error) {
out, err := c.PostJSON(ctx, c.Endpoint("oidc", "requests", "validate"), ValidateOIDCAuthCallbackReq{
Query: q,
})
if err != nil {
return nil, trace.Wrap(err)
}
var rawResponse *OIDCAuthRawResponse
if err := json.Unmarshal(out.Bytes(), &rawResponse); err != nil {
return nil, trace.Wrap(err)
}
response := OIDCAuthResponse{
Username: rawResponse.Username,
Identity: rawResponse.Identity,
Cert: rawResponse.Cert,
Req: rawResponse.Req,
TLSCert: rawResponse.TLSCert,
}
if len(rawResponse.Session) != 0 {
session, err := services.UnmarshalWebSession(rawResponse.Session)
if err != nil {
return nil, trace.Wrap(err)
}
response.Session = session
}
response.HostSigners = make([]types.CertAuthority, len(rawResponse.HostSigners))
for i, raw := range rawResponse.HostSigners {
ca, err := services.UnmarshalCertAuthority(raw)
if err != nil {
return nil, trace.Wrap(err)
}
response.HostSigners[i] = ca
}
return &response, nil
}
// ValidateSAMLResponse validates response returned by SAML identity provider
func (c *Client) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) {
out, err := c.PostJSON(ctx, c.Endpoint("saml", "requests", "validate"), ValidateSAMLResponseReq{
Response: re,
ConnectorID: connectorID,
})
if err != nil {
return nil, trace.Wrap(err)
}
var rawResponse *SAMLAuthRawResponse
if err := json.Unmarshal(out.Bytes(), &rawResponse); err != nil {
return nil, trace.Wrap(err)
}
response := SAMLAuthResponse{
Username: rawResponse.Username,
Identity: rawResponse.Identity,
Cert: rawResponse.Cert,
Req: rawResponse.Req,
TLSCert: rawResponse.TLSCert,
}
if len(rawResponse.Session) != 0 {
session, err := services.UnmarshalWebSession(rawResponse.Session)
if err != nil {
return nil, trace.Wrap(err)
}
response.Session = session
}
response.HostSigners = make([]types.CertAuthority, len(rawResponse.HostSigners))
for i, raw := range rawResponse.HostSigners {
ca, err := services.UnmarshalCertAuthority(raw)
if err != nil {
return nil, trace.Wrap(err)
}
response.HostSigners[i] = ca
}
return &response, nil
}
// ValidateGithubAuthCallback validates Github auth callback returned from redirect
func (c *Client) ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error) {
out, err := c.PostJSON(ctx, c.Endpoint("github", "requests", "validate"),
validateGithubAuthCallbackReq{Query: q})
if err != nil {
return nil, trace.Wrap(err)
}
var rawResponse githubAuthRawResponse
if err := json.Unmarshal(out.Bytes(), &rawResponse); err != nil {
return nil, trace.Wrap(err)
}
response := GithubAuthResponse{
Username: rawResponse.Username,
Identity: rawResponse.Identity,
Cert: rawResponse.Cert,
Req: rawResponse.Req,
TLSCert: rawResponse.TLSCert,
}
if len(rawResponse.Session) != 0 {
session, err := services.UnmarshalWebSession(
rawResponse.Session)
if err != nil {
return nil, trace.Wrap(err)
}
response.Session = session
}
response.HostSigners = make([]types.CertAuthority, len(rawResponse.HostSigners))
for i, raw := range rawResponse.HostSigners {
ca, err := services.UnmarshalCertAuthority(raw)
if err != nil {
return nil, trace.Wrap(err)
}
response.HostSigners[i] = ca
}
return &response, nil
}
// GetSessionChunk allows clients to receive a byte array (chunk) from a recorded
// session stream, starting from 'offset', up to 'max' in length. The upper bound
// of 'max' is set to events.MaxChunkBytes
func (c *Client) GetSessionChunk(namespace string, sid session.ID, offsetBytes, maxBytes int) ([]byte, error) {
if namespace == "" {
return nil, trace.BadParameter(MissingNamespaceError)
}
response, err := c.Get(context.TODO(), c.Endpoint("namespaces", namespace, "sessions", string(sid), "stream"), url.Values{
"offset": []string{strconv.Itoa(offsetBytes)},
"bytes": []string{strconv.Itoa(maxBytes)},
})
if err != nil {
log.Error(err)
return nil, trace.Wrap(err)
}
return response.Bytes(), nil
}
// Returns events that happen during a session sorted by time
// (oldest first).
//
// afterN allows to filter by "newer than N" value where N is the cursor ID
// of previously returned bunch (good for polling for latest)
func (c *Client) GetSessionEvents(namespace string, sid session.ID, afterN int) (retval []events.EventFields, err error) {
if namespace == "" {
return nil, trace.BadParameter(MissingNamespaceError)
}
query := make(url.Values)
if afterN > 0 {
query.Set("after", strconv.Itoa(afterN))
}
response, err := c.Get(context.TODO(), c.Endpoint("namespaces", namespace, "sessions", string(sid), "events"), query)
if err != nil {
return nil, trace.Wrap(err)
}
retval = make([]events.EventFields, 0)
if err := json.Unmarshal(response.Bytes(), &retval); err != nil {
return nil, trace.Wrap(err)
}
return retval, nil
}
// StreamSessionEvents streams all events from a given session recording. An error is returned on the first
// channel if one is encountered. Otherwise the event channel is closed when the stream ends.
// The event channel is not closed on error to prevent race conditions in downstream select statements.
func (c *Client) StreamSessionEvents(ctx context.Context, sessionID session.ID, startIndex int64) (chan apievents.AuditEvent, chan error) {
return c.APIClient.StreamSessionEvents(ctx, string(sessionID), startIndex)
}
// SearchEvents allows searching for audit events with pagination support.
func (c *Client) SearchEvents(fromUTC, toUTC time.Time, namespace string, eventTypes []string, limit int, order types.EventOrder, startKey string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchEvents(context.TODO(), fromUTC, toUTC, namespace, eventTypes, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
return events, lastKey, nil
}
// SearchSessionEvents returns session related events to find completed sessions.
func (c *Client) SearchSessionEvents(fromUTC, toUTC time.Time, limit int, order types.EventOrder, startKey string, cond *types.WhereExpr, sessionID string) ([]apievents.AuditEvent, string, error) {
events, lastKey, err := c.APIClient.SearchSessionEvents(context.TODO(), fromUTC, toUTC, limit, order, startKey)
if err != nil {
return nil, "", trace.Wrap(err)
}
return events, lastKey, nil
}
// GetNamespaces returns a list of namespaces
func (c *Client) GetNamespaces() ([]types.Namespace, error) {
out, err := c.Get(context.TODO(), c.Endpoint("namespaces"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var re []types.Namespace
if err := utils.FastUnmarshal(out.Bytes(), &re); err != nil {
return nil, trace.Wrap(err)
}
return re, nil
}
// GetNamespace returns namespace by name
func (c *Client) GetNamespace(name string) (*types.Namespace, error) {
if name == "" {
return nil, trace.BadParameter("missing namespace name")
}
out, err := c.Get(context.TODO(), c.Endpoint("namespaces", name), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalNamespace(out.Bytes())
}
// UpsertNamespace upserts namespace
func (c *Client) UpsertNamespace(ns types.Namespace) error {
_, err := c.PostJSON(context.TODO(), c.Endpoint("namespaces"), upsertNamespaceReq{Namespace: ns})
return trace.Wrap(err)
}
// DeleteNamespace deletes namespace by name
func (c *Client) DeleteNamespace(name string) error {
_, err := c.Delete(context.TODO(), c.Endpoint("namespaces", name))
return trace.Wrap(err)
}
// CreateRole not implemented: can only be called locally.
func (c *Client) CreateRole(ctx context.Context, role types.Role) error {
return trace.NotImplemented(notImplementedMessage)
}
// GetClusterName returns a cluster name
func (c *Client) GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error) {
out, err := c.Get(context.TODO(), c.Endpoint("configuration", "name"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
cn, err := services.UnmarshalClusterName(out.Bytes())
if err != nil {
return nil, trace.Wrap(err)
}
return cn, err
}
// SetClusterName sets cluster name once, will
// return Already Exists error if the name is already set
func (c *Client) SetClusterName(cn types.ClusterName) error {
data, err := services.MarshalClusterName(cn)
if err != nil {
return trace.Wrap(err)
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("configuration", "name"), &setClusterNameReq{ClusterName: data})
if err != nil {
return trace.Wrap(err)
}
return nil
}
// UpsertClusterName not implemented: can only be called locally.
func (c *Client) UpsertClusterName(cn types.ClusterName) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteStaticTokens deletes static tokens
func (c *Client) DeleteStaticTokens() error {
_, err := c.Delete(context.TODO(), c.Endpoint("configuration", "static_tokens"))
return trace.Wrap(err)
}
// GetStaticTokens returns a list of static register tokens
func (c *Client) GetStaticTokens() (types.StaticTokens, error) {
out, err := c.Get(context.TODO(), c.Endpoint("configuration", "static_tokens"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
st, err := services.UnmarshalStaticTokens(out.Bytes())
if err != nil {
return nil, trace.Wrap(err)
}
return st, err
}
// SetStaticTokens sets a list of static register tokens
func (c *Client) SetStaticTokens(st types.StaticTokens) error {
data, err := services.MarshalStaticTokens(st)
if err != nil {
return trace.Wrap(err)
}
_, err = c.PostJSON(context.TODO(), c.Endpoint("configuration", "static_tokens"), &setStaticTokensReq{StaticTokens: data})
if err != nil {
return trace.Wrap(err)
}
return nil
}
// DeleteClusterName not implemented: can only be called locally.
func (c *Client) DeleteClusterName() error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllCertAuthorities not implemented: can only be called locally.
func (c *Client) DeleteAllCertAuthorities(caType types.CertAuthType) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllReverseTunnels not implemented: can only be called locally.
func (c *Client) DeleteAllReverseTunnels() error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllCertNamespaces not implemented: can only be called locally.
func (c *Client) DeleteAllNamespaces() error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllRoles not implemented: can only be called locally.
func (c *Client) DeleteAllRoles() error {
return trace.NotImplemented(notImplementedMessage)
}
// ListWindowsDesktops not implemented: can only be called locally.
func (c *Client) ListWindowsDesktops(ctx context.Context, req types.ListWindowsDesktopsRequest) (*types.ListWindowsDesktopsResponse, error) {
return nil, trace.NotImplemented(notImplementedMessage)
}
// ListWindowsDesktopServices not implemented: can only be called locally.
func (c *Client) ListWindowsDesktopServices(ctx context.Context, req types.ListWindowsDesktopServicesRequest) (*types.ListWindowsDesktopServicesResponse, error) {
return nil, trace.NotImplemented(notImplementedMessage)
}
// DeleteAllUsers not implemented: can only be called locally.
func (c *Client) DeleteAllUsers() error {
return trace.NotImplemented(notImplementedMessage)
}
func (c *Client) ValidateTrustedCluster(ctx context.Context, validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
validateRequestRaw, err := validateRequest.ToRaw()
if err != nil {
return nil, trace.Wrap(err)
}
out, err := c.PostJSON(ctx, c.Endpoint("trustedclusters", "validate"), validateRequestRaw)
if err != nil {
return nil, trace.Wrap(err)
}
var validateResponseRaw ValidateTrustedClusterResponseRaw
err = json.Unmarshal(out.Bytes(), &validateResponseRaw)
if err != nil {
return nil, trace.Wrap(err)
}
validateResponse, err := validateResponseRaw.ToNative()
if err != nil {
return nil, trace.Wrap(err)
}
return validateResponse, nil
}
// CreateResetPasswordToken creates reset password token
func (c *Client) CreateResetPasswordToken(ctx context.Context, req CreateUserTokenRequest) (types.UserToken, error) {
return c.APIClient.CreateResetPasswordToken(ctx, &proto.CreateResetPasswordTokenRequest{
Name: req.Name,
TTL: proto.Duration(req.TTL),
Type: req.Type,
})
}
// CreateBot creates a bot and associated resources.
func (c *Client) CreateBot(ctx context.Context, req *proto.CreateBotRequest) (*proto.CreateBotResponse, error) {
return c.APIClient.CreateBot(ctx, req)
}
// DeleteBot deletes a certificate renewal bot and associated resources.
func (c *Client) DeleteBot(ctx context.Context, botName string) error {
return c.APIClient.DeleteBot(ctx, botName)
}
// GetBotUsers fetches all bot users.
func (c *Client) GetBotUsers(ctx context.Context) ([]types.User, error) {
return c.APIClient.GetBotUsers(ctx)
}
// GetDatabaseServers returns all registered database proxy servers.
func (c *Client) GetDatabaseServers(ctx context.Context, namespace string, opts ...services.MarshalOption) ([]types.DatabaseServer, error) {
return c.APIClient.GetDatabaseServers(ctx, namespace)
}
// UpsertAppSession not implemented: can only be called locally.
func (c *Client) UpsertAppSession(ctx context.Context, session types.WebSession) error {
return trace.NotImplemented(notImplementedMessage)
}
// UpsertSnowflakeSession not implemented: can only be called locally.
func (c *Client) UpsertSnowflakeSession(_ context.Context, _ types.WebSession) error {
return trace.NotImplemented(notImplementedMessage)
}
// UpsertSAMLIdPSession not implemented: can only be called locally.
func (c *Client) UpsertSAMLIdPSession(_ context.Context, _ types.WebSession) error {
return trace.NotImplemented(notImplementedMessage)
}
// ResumeAuditStream resumes existing audit stream.
func (c *Client) ResumeAuditStream(ctx context.Context, sid session.ID, uploadID string) (apievents.Stream, error) {
return c.APIClient.ResumeAuditStream(ctx, string(sid), uploadID)
}
// CreateAuditStream creates new audit stream.
func (c *Client) CreateAuditStream(ctx context.Context, sid session.ID) (apievents.Stream, error) {
return c.APIClient.CreateAuditStream(ctx, string(sid))
}
// GetClusterAuditConfig gets cluster audit configuration.
func (c *Client) GetClusterAuditConfig(ctx context.Context, opts ...services.MarshalOption) (types.ClusterAuditConfig, error) {
return c.APIClient.GetClusterAuditConfig(ctx)
}
// GetClusterNetworkingConfig gets cluster networking configuration.
func (c *Client) GetClusterNetworkingConfig(ctx context.Context, opts ...services.MarshalOption) (types.ClusterNetworkingConfig, error) {
return c.APIClient.GetClusterNetworkingConfig(ctx)
}
// GetSessionRecordingConfig gets session recording configuration.
func (c *Client) GetSessionRecordingConfig(ctx context.Context, opts ...services.MarshalOption) (types.SessionRecordingConfig, error) {
return c.APIClient.GetSessionRecordingConfig(ctx)
}
// GenerateCertAuthorityCRL generates an empty CRL for a CA.
func (c *Client) GenerateCertAuthorityCRL(ctx context.Context, caType types.CertAuthType) ([]byte, error) {
resp, err := c.APIClient.GenerateCertAuthorityCRL(ctx, &proto.CertAuthorityRequest{Type: caType})
if err != nil {
return nil, trace.Wrap(err)
}
return resp.CRL, nil
}
// DeleteClusterNetworkingConfig not implemented: can only be called locally.
func (c *Client) DeleteClusterNetworkingConfig(ctx context.Context) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteSessionRecordingConfig not implemented: can only be called locally.
func (c *Client) DeleteSessionRecordingConfig(ctx context.Context) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAuthPreference not implemented: can only be called locally.
func (c *Client) DeleteAuthPreference(context.Context) error {
return trace.NotImplemented(notImplementedMessage)
}
// SetClusterAuditConfig not implemented: can only be called locally.
func (c *Client) SetClusterAuditConfig(ctx context.Context, auditConfig types.ClusterAuditConfig) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteClusterAuditConfig not implemented: can only be called locally.
func (c *Client) DeleteClusterAuditConfig(ctx context.Context) error {
return trace.NotImplemented(notImplementedMessage)
}
// DeleteAllLocks not implemented: can only be called locally.
func (c *Client) DeleteAllLocks(context.Context) error {
return trace.NotImplemented(notImplementedMessage)
}
func (c *Client) UpdatePresence(ctx context.Context, sessionID, user string) error {
return trace.NotImplemented(notImplementedMessage)
}
func (c *Client) GetLicense(ctx context.Context) (string, error) {
return c.APIClient.GetLicense(ctx)
}
func (c *Client) ListReleases(ctx context.Context) ([]*types.Release, error) {
return c.APIClient.ListReleases(ctx, &proto.ListReleasesRequest{})
}
// WebService implements features used by Web UI clients
type WebService interface {
// GetWebSessionInfo checks if a web session is valid, returns session id in case if
// it is valid, or error otherwise.
GetWebSessionInfo(ctx context.Context, user, sessionID string) (types.WebSession, error)
// ExtendWebSession creates a new web session for a user based on another
// valid web session
ExtendWebSession(ctx context.Context, req WebSessionReq) (types.WebSession, error)
// CreateWebSession creates a new web session for a user
CreateWebSession(ctx context.Context, user string) (types.WebSession, error)
// AppSession defines application session features.
services.AppSession
// SnowflakeSession defines Snowflake session features.
services.SnowflakeSession
}
// IdentityService manages identities and users
type IdentityService interface {
// UpsertOIDCConnector updates or creates OIDC connector
UpsertOIDCConnector(ctx context.Context, connector types.OIDCConnector) error
// GetOIDCConnector returns OIDC connector information by id
GetOIDCConnector(ctx context.Context, id string, withSecrets bool) (types.OIDCConnector, error)
// GetOIDCConnectors gets OIDC connectors list
GetOIDCConnectors(ctx context.Context, withSecrets bool) ([]types.OIDCConnector, error)
// DeleteOIDCConnector deletes OIDC connector by ID
DeleteOIDCConnector(ctx context.Context, connectorID string) error
// CreateOIDCAuthRequest creates OIDCAuthRequest
CreateOIDCAuthRequest(ctx context.Context, req types.OIDCAuthRequest) (*types.OIDCAuthRequest, error)
// GetOIDCAuthRequest returns OIDC auth request if found
GetOIDCAuthRequest(ctx context.Context, id string) (*types.OIDCAuthRequest, error)
// ValidateOIDCAuthCallback validates OIDC auth callback returned from redirect
ValidateOIDCAuthCallback(ctx context.Context, q url.Values) (*OIDCAuthResponse, error)
// UpsertSAMLConnector updates or creates SAML connector
UpsertSAMLConnector(ctx context.Context, connector types.SAMLConnector) error
// GetSAMLConnector returns SAML connector information by id
GetSAMLConnector(ctx context.Context, id string, withSecrets bool) (types.SAMLConnector, error)
// GetSAMLConnectors gets SAML connectors list
GetSAMLConnectors(ctx context.Context, withSecrets bool) ([]types.SAMLConnector, error)
// DeleteSAMLConnector deletes SAML connector by ID
DeleteSAMLConnector(ctx context.Context, connectorID string) error
// CreateSAMLAuthRequest creates SAML AuthnRequest
CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error)
// ValidateSAMLResponse validates SAML auth response
ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error)
// GetSAMLAuthRequest returns SAML auth request if found
GetSAMLAuthRequest(ctx context.Context, authRequestID string) (*types.SAMLAuthRequest, error)
// UpsertGithubConnector creates or updates a Github connector
UpsertGithubConnector(ctx context.Context, connector types.GithubConnector) error
// GetGithubConnectors returns all configured Github connectors
GetGithubConnectors(ctx context.Context, withSecrets bool) ([]types.GithubConnector, error)
// GetGithubConnector returns the specified Github connector
GetGithubConnector(ctx context.Context, id string, withSecrets bool) (types.GithubConnector, error)
// DeleteGithubConnector deletes the specified Github connector
DeleteGithubConnector(ctx context.Context, id string) error
// CreateGithubAuthRequest creates a new request for Github OAuth2 flow
CreateGithubAuthRequest(ctx context.Context, req types.GithubAuthRequest) (*types.GithubAuthRequest, error)
// GetGithubAuthRequest returns Github auth request if found
GetGithubAuthRequest(ctx context.Context, id string) (*types.GithubAuthRequest, error)
// ValidateGithubAuthCallback validates Github auth callback
ValidateGithubAuthCallback(ctx context.Context, q url.Values) (*GithubAuthResponse, error)
// GetSSODiagnosticInfo returns SSO diagnostic info records.
GetSSODiagnosticInfo(ctx context.Context, authKind string, authRequestID string) (*types.SSODiagnosticInfo, error)
// GetUser returns user by name
GetUser(name string, withSecrets bool) (types.User, error)
// GetCurrentUser returns current user as seen by the server.
// Useful especially in the context of remote clusters which perform role and trait mapping.
GetCurrentUser(ctx context.Context) (types.User, error)
// GetCurrentUserRoles returns current user's roles.
GetCurrentUserRoles(ctx context.Context) ([]types.Role, error)
// CreateUser inserts a new entry in a backend.
CreateUser(ctx context.Context, user types.User) error
// UpdateUser updates an existing user in a backend.
UpdateUser(ctx context.Context, user types.User) error
// UpsertUser user updates or inserts user entry
UpsertUser(user types.User) error
// CompareAndSwapUser updates an existing user in a backend, but fails if
// the user in the backend does not match the expected value.
CompareAndSwapUser(ctx context.Context, new, expected types.User) error
// DeleteUser deletes an existng user in a backend by username.
DeleteUser(ctx context.Context, user string) error
// GetUsers returns a list of usernames registered in the system
GetUsers(withSecrets bool) ([]types.User, error)
// ChangePassword changes user password
ChangePassword(ctx context.Context, req *proto.ChangePasswordRequest) error
// GenerateHostCert takes the public key in the Open SSH ``authorized_keys``
// plain text format, signs it using Host Certificate Authority private key and returns the
// resulting certificate.
GenerateHostCert(ctx context.Context, key []byte, hostID, nodeName string, principals []string, clusterName string, role types.SystemRole, ttl time.Duration) ([]byte, error)
// GenerateUserCerts takes the public key in the OpenSSH `authorized_keys` plain
// text format, signs it using User Certificate Authority signing key and
// returns the resulting certificates.
GenerateUserCerts(ctx context.Context, req proto.UserCertsRequest) (*proto.Certs, error)
// GenerateUserSingleUseCerts is like GenerateUserCerts but issues a
// certificate for a single session
// (https://github.com/gravitational/teleport/blob/3a1cf9111c2698aede2056513337f32bfc16f1f1/rfd/0014-session-2FA.md#sessions).
GenerateUserSingleUseCerts(ctx context.Context) (proto.AuthService_GenerateUserSingleUseCertsClient, error)
// IsMFARequired is a request to check whether MFA is required to
// access the Target.
IsMFARequired(ctx context.Context, req *proto.IsMFARequiredRequest) (*proto.IsMFARequiredResponse, error)
// DeleteAllUsers deletes all users
DeleteAllUsers() error
// CreateResetPasswordToken creates a new user reset token
CreateResetPasswordToken(ctx context.Context, req CreateUserTokenRequest) (types.UserToken, error)
// CreateBot creates a new certificate renewal bot and associated resources.
CreateBot(ctx context.Context, req *proto.CreateBotRequest) (*proto.CreateBotResponse, error)
// DeleteBot removes a certificate renewal bot and associated resources.
DeleteBot(ctx context.Context, botName string) error
// GetBotUsers gets all bot users.
GetBotUsers(ctx context.Context) ([]types.User, error)
// ChangeUserAuthentication allows a user with a reset or invite token to change their password and if enabled also adds a new mfa device.
// Upon success, creates new web session and creates new set of recovery codes (if user meets requirements).
ChangeUserAuthentication(ctx context.Context, req *proto.ChangeUserAuthenticationRequest) (*proto.ChangeUserAuthenticationResponse, error)
// GetResetPasswordToken returns a reset password token.
GetResetPasswordToken(ctx context.Context, username string) (types.UserToken, error)
// GetMFADevices fetches all MFA devices registered for the calling user.
GetMFADevices(ctx context.Context, in *proto.GetMFADevicesRequest) (*proto.GetMFADevicesResponse, error)
// AddMFADevice adds a new MFA device for the calling user.
AddMFADevice(ctx context.Context) (proto.AuthService_AddMFADeviceClient, error)
// DeleteMFADevice deletes a MFA device for the calling user.
DeleteMFADevice(ctx context.Context) (proto.AuthService_DeleteMFADeviceClient, error)
// AddMFADeviceSync adds a new MFA device (nonstream).
AddMFADeviceSync(ctx context.Context, req *proto.AddMFADeviceSyncRequest) (*proto.AddMFADeviceSyncResponse, error)
// DeleteMFADeviceSync deletes a users MFA device (nonstream).
DeleteMFADeviceSync(ctx context.Context, req *proto.DeleteMFADeviceSyncRequest) error
// CreateAuthenticateChallenge creates and returns MFA challenges for a users registered MFA devices.
CreateAuthenticateChallenge(ctx context.Context, req *proto.CreateAuthenticateChallengeRequest) (*proto.MFAAuthenticateChallenge, error)
// CreateRegisterChallenge creates and returns MFA register challenge for a new MFA device.
CreateRegisterChallenge(ctx context.Context, req *proto.CreateRegisterChallengeRequest) (*proto.MFARegisterChallenge, error)
// MaintainSessionPresence establishes a channel used to continuously verify the presence for a session.
MaintainSessionPresence(ctx context.Context) (proto.AuthService_MaintainSessionPresenceClient, error)
// StartAccountRecovery creates a recovery start token for a user who successfully verified their username and their recovery code.
// This token is used as part of a URL that will be emailed to the user (not done in this request).
// Represents step 1 of the account recovery process.
StartAccountRecovery(ctx context.Context, req *proto.StartAccountRecoveryRequest) (types.UserToken, error)
// VerifyAccountRecovery creates a recovery approved token after successful verification of users password or second factor
// (authn depending on what user needed to recover). This token will allow users to perform protected actions while not logged in.
// Represents step 2 of the account recovery process after RPC StartAccountRecovery.
VerifyAccountRecovery(ctx context.Context, req *proto.VerifyAccountRecoveryRequest) (types.UserToken, error)
// CompleteAccountRecovery sets a new password or adds a new mfa device,
// allowing user to regain access to their account using the new credentials.
// Represents the last step in the account recovery process after RPC's StartAccountRecovery and VerifyAccountRecovery.
CompleteAccountRecovery(ctx context.Context, req *proto.CompleteAccountRecoveryRequest) error
// CreateAccountRecoveryCodes creates new set of recovery codes for a user, replacing and invalidating any previously owned codes.
CreateAccountRecoveryCodes(ctx context.Context, req *proto.CreateAccountRecoveryCodesRequest) (*proto.RecoveryCodes, error)
// GetAccountRecoveryToken returns a user token resource after verifying the token in
// request is not expired and is of the correct recovery type.
GetAccountRecoveryToken(ctx context.Context, req *proto.GetAccountRecoveryTokenRequest) (types.UserToken, error)
// GetAccountRecoveryCodes returns the user in context their recovery codes resource without any secrets.
GetAccountRecoveryCodes(ctx context.Context, req *proto.GetAccountRecoveryCodesRequest) (*proto.RecoveryCodes, error)
// CreatePrivilegeToken creates a privilege token for the logged in user who has successfully re-authenticated with their second factor.
// A privilege token allows users to perform privileged action eg: add/delete their MFA device.
CreatePrivilegeToken(ctx context.Context, req *proto.CreatePrivilegeTokenRequest) (*types.UserTokenV3, error)
// UpdateHeadlessAuthenticationState updates a headless authentication state.
UpdateHeadlessAuthenticationState(ctx context.Context, id string, state types.HeadlessAuthenticationState, mfaResponse *proto.MFAAuthenticateResponse) error
// GetHeadlessAuthentication retrieves a headless authentication by id.
GetHeadlessAuthentication(ctx context.Context, id string) (*types.HeadlessAuthentication, error)
}
// ProvisioningService is a service in control
// of adding new nodes, auth servers and proxies to the cluster
type ProvisioningService interface {
// GetTokens returns a list of active invitation tokens for nodes and users
GetTokens(ctx context.Context) (tokens []types.ProvisionToken, err error)
// GetToken returns provisioning token
GetToken(ctx context.Context, token string) (types.ProvisionToken, error)
// DeleteToken deletes a given provisioning token on the auth server (CA). It
// could be a reset password token or a machine token
DeleteToken(ctx context.Context, token string) error
// DeleteAllTokens deletes all provisioning tokens
DeleteAllTokens() error
// UpsertToken adds provisioning tokens for the auth server
UpsertToken(ctx context.Context, token types.ProvisionToken) error
// CreateToken creates a new provision token for the auth server
CreateToken(ctx context.Context, token types.ProvisionToken) error
// RegisterUsingToken calls the auth service API to register a new node via registration token
// which has been previously issued via GenerateToken
RegisterUsingToken(ctx context.Context, req *types.RegisterUsingTokenRequest) (*proto.Certs, error)
}
// ClientI is a client to Auth service
type ClientI interface {
IdentityService
ProvisioningService
services.Trust
events.AuditLogSessionStreamer
events.Streamer
apievents.Emitter
services.Presence
services.Access
services.DynamicAccess
services.DynamicAccessOracle
services.Restrictions
services.Apps
services.Databases
services.DatabaseServices
services.Kubernetes
services.WindowsDesktops
services.SAMLIdPServiceProviders
services.UserGroups
WebService
services.Status
services.ClusterConfiguration
services.SessionTrackerService
services.ConnectionsDiagnostic
services.SAMLIdPSession
types.Events
types.WebSessionsGetter
types.WebTokensGetter
// DevicesClient returns a Device Trust client.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
// still get a client when calling this method, but all RPCs will return
// "not implemented" errors (as per the default gRPC behavior).
DevicesClient() devicepb.DeviceTrustServiceClient
// LoginRuleClient returns a client to the Login Rule gRPC service.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
// still get a client when calling this method, but all RPCs will return
// "not implemented" errors (as per the default gRPC behavior).
LoginRuleClient() loginrulepb.LoginRuleServiceClient
// NewKeepAliver returns a new instance of keep aliver
NewKeepAliver(ctx context.Context) (types.KeepAliver, error)
// RotateCertAuthority starts or restarts certificate authority rotation process.
RotateCertAuthority(ctx context.Context, req RotateRequest) error
// RotateExternalCertAuthority rotates external certificate authority,
// this method is used to update only public keys and certificates of the
// the certificate authorities of trusted clusters.
RotateExternalCertAuthority(ctx context.Context, ca types.CertAuthority) error
// ValidateTrustedCluster validates trusted cluster token with
// main cluster, in case if validation is successful, main cluster
// adds remote cluster
ValidateTrustedCluster(context.Context, *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error)
// GetDomainName returns auth server cluster name
GetDomainName(ctx context.Context) (string, error)
// GetClusterCACert returns the PEM-encoded TLS certs for the local cluster.
// If the cluster has multiple TLS certs, they will all be concatenated.
GetClusterCACert(ctx context.Context) (*proto.GetClusterCACertResponse, error)
// GenerateHostCerts generates new host certificates (signed
// by the host certificate authority) for a node
GenerateHostCerts(context.Context, *proto.HostCertsRequest) (*proto.Certs, error)
// GenerateOpenSSHCert signs a SSH certificate with OpenSSH CA that
// can be used to connect to Agentless nodes.
GenerateOpenSSHCert(ctx context.Context, req *proto.OpenSSHCertRequest) (*proto.OpenSSHCert, error)
// AuthenticateWebUser authenticates web user, creates and returns web session
// in case if authentication is successful
AuthenticateWebUser(ctx context.Context, req AuthenticateUserRequest) (types.WebSession, error)
// AuthenticateSSHUser authenticates SSH console user, creates and returns a pair of signed TLS and SSH
// short-lived certificates as a result
AuthenticateSSHUser(ctx context.Context, req AuthenticateSSHRequest) (*SSHLoginResponse, error)
// ProcessKubeCSR processes CSR request against Kubernetes CA, returns
// signed certificate if successful.
ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error)
// Ping gets basic info about the auth server.
Ping(ctx context.Context) (proto.PingResponse, error)
// CreateAppSession creates an application web session. Application web
// sessions represent a browser session the client holds.
CreateAppSession(context.Context, types.CreateAppSessionRequest) (types.WebSession, error)
// CreateSnowflakeSession creates a Snowflake web session. Snowflake web
// sessions represent Database Access Snowflake session the client holds.
CreateSnowflakeSession(context.Context, types.CreateSnowflakeSessionRequest) (types.WebSession, error)
// CreateSAMLIdPSession creates a SAML IdP. SAML IdP sessions represent
// sessions created by the SAML identity provider.
CreateSAMLIdPSession(context.Context, types.CreateSAMLIdPSessionRequest) (types.WebSession, error)
// GenerateDatabaseCert generates client certificate used by a database
// service to authenticate with the database instance.
GenerateDatabaseCert(context.Context, *proto.DatabaseCertRequest) (*proto.DatabaseCertResponse, error)
// GetWebSession queries the existing web session described with req.
// Implements ReadAccessPoint.
GetWebSession(ctx context.Context, req types.GetWebSessionRequest) (types.WebSession, error)
// GetWebToken queries the existing web token described with req.
// Implements ReadAccessPoint.
GetWebToken(ctx context.Context, req types.GetWebTokenRequest) (types.WebToken, error)
// ResetAuthPreference resets cluster auth preference to defaults.
ResetAuthPreference(ctx context.Context) error
// ResetClusterNetworkingConfig resets cluster networking configuration to defaults.
ResetClusterNetworkingConfig(ctx context.Context) error
// ResetSessionRecordingConfig resets session recording configuration to defaults.
ResetSessionRecordingConfig(ctx context.Context) error
// GenerateWindowsDesktopCert generates client smartcard certificate used
// by an RDP client to authenticate with Windows.
GenerateWindowsDesktopCert(context.Context, *proto.WindowsDesktopCertRequest) (*proto.WindowsDesktopCertResponse, error)
// GenerateCertAuthorityCRL generates an empty CRL for a CA.
GenerateCertAuthorityCRL(context.Context, types.CertAuthType) ([]byte, error)
// GetInventoryStatus gets basic status info about instance inventory.
GetInventoryStatus(ctx context.Context, req proto.InventoryStatusRequest) (proto.InventoryStatusSummary, error)
// PingInventory attempts to trigger a downstream ping against a connected instance.
PingInventory(ctx context.Context, req proto.InventoryPingRequest) (proto.InventoryPingResponse, error)
// SubmitUsageEvent submits an external usage event.
SubmitUsageEvent(ctx context.Context, req *proto.SubmitUsageEventRequest) error
// GetLicense returns the license used to start Teleport Enterprise
GetLicense(ctx context.Context) (string, error)
// ListReleases returns a list of Teleport Enterprise releases
ListReleases(ctx context.Context) ([]*types.Release, error)
// PluginsClient returns a Plugins client.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
// still get a plugins client when calling this method, but all RPCs will return
// "not implemented" errors (as per the default gRPC behavior).
PluginsClient() pluginspb.PluginServiceClient
// SAMLIdPClient returns a SAML IdP client.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
// still get a SAML IdP client when calling this method, but all RPCs will return
// "not implemented" errors (as per the default gRPC behavior).
SAMLIdPClient() samlidppb.SAMLIdPServiceClient
// OktaClient returns an Okta client.
// Clients connecting to non-Enterprise clusters, or older Teleport versions,
// still get an Okta client when calling this method, but all RPCs will return
// "not implemented" errors (as per the default gRPC behavior).
OktaClient() *okta.Client
}