Files
teleport/lib/auth/trustedcluster.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

807 lines
26 KiB
Go

/*
Copyright 2017 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"
"encoding/json"
"net/http"
"net/url"
"strings"
"time"
"github.com/google/go-cmp/cmp"
"github.com/gravitational/roundtrip"
"github.com/gravitational/trace"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
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"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
// UpsertTrustedCluster creates or toggles a Trusted Cluster relationship.
func (a *Server) UpsertTrustedCluster(ctx context.Context, trustedCluster types.TrustedCluster) (newTrustedCluster types.TrustedCluster, returnErr error) {
// It is recommended to omit trusted cluster name because the trusted cluster name
// is updated to the roots cluster name during the handshake with the root cluster.
var existingCluster types.TrustedCluster
if trustedCluster.GetName() != "" {
var err error
existingCluster, err = a.GetTrustedCluster(ctx, trustedCluster.GetName())
if err != nil && !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
}
}
enable := trustedCluster.GetEnabled()
// If the trusted cluster already exists in the backend, make sure it's a
// valid state change client is trying to make.
if existingCluster != nil {
if err := existingCluster.CanChangeStateTo(trustedCluster); err != nil {
return nil, trace.Wrap(err)
}
}
logger := log.WithField("trusted_cluster", trustedCluster.GetName())
// change state
if err := a.checkLocalRoles(ctx, trustedCluster.GetRoleMap()); err != nil {
return nil, trace.Wrap(err)
}
// Update role map
if existingCluster != nil && !cmp.Equal(existingCluster.GetRoleMap(), trustedCluster.GetRoleMap()) {
if err := a.UpdateUserCARoleMap(ctx, existingCluster.GetName(), trustedCluster.GetRoleMap(),
existingCluster.GetEnabled()); err != nil {
return nil, trace.Wrap(err)
}
// Reset previous UserCA role map if this func fails later on
defer func() {
if returnErr != nil {
if err := a.UpdateUserCARoleMap(ctx, trustedCluster.GetName(), existingCluster.GetRoleMap(),
trustedCluster.GetEnabled()); err != nil {
returnErr = trace.NewAggregate(err, returnErr)
}
}
}()
}
// Create or update state
switch {
case existingCluster != nil && enable == true:
if existingCluster.GetEnabled() {
break
}
log.Debugf("Enabling existing Trusted Cluster relationship.")
if err := a.activateCertAuthority(trustedCluster); err != nil {
if trace.IsNotFound(err) {
return nil, trace.BadParameter("enable only supported for Trusted Clusters created with Teleport 2.3 and above")
}
return nil, trace.Wrap(err)
}
if err := a.createReverseTunnel(trustedCluster); err != nil {
return nil, trace.Wrap(err)
}
case existingCluster != nil && enable == false:
if !existingCluster.GetEnabled() {
break
}
log.Debugf("Disabling existing Trusted Cluster relationship.")
if err := a.deactivateCertAuthority(trustedCluster); err != nil {
if trace.IsNotFound(err) {
return nil, trace.BadParameter("enable only supported for Trusted Clusters created with Teleport 2.3 and above")
}
return nil, trace.Wrap(err)
}
if err := a.DeleteReverseTunnel(trustedCluster.GetName()); err != nil {
return nil, trace.Wrap(err)
}
case existingCluster == nil && enable == true:
logger.Info("Creating enabled Trusted Cluster relationship.")
remoteCAs, err := a.establishTrust(ctx, trustedCluster)
if err != nil {
return nil, trace.Wrap(err)
}
// Force name of the trusted cluster resource
// to be equal to the name of the remote cluster it is connecting to.
trustedCluster.SetName(remoteCAs[0].GetClusterName())
if err := a.addCertAuthorities(ctx, trustedCluster, remoteCAs); err != nil {
return nil, trace.Wrap(err)
}
if err := a.createReverseTunnel(trustedCluster); err != nil {
return nil, trace.Wrap(err)
}
case existingCluster == nil && enable == false:
logger.Info("Creating disabled Trusted Cluster relationship.")
remoteCAs, err := a.establishTrust(ctx, trustedCluster)
if err != nil {
return nil, trace.Wrap(err)
}
// Force name to the name of the trusted cluster.
trustedCluster.SetName(remoteCAs[0].GetClusterName())
if err := a.addCertAuthorities(ctx, trustedCluster, remoteCAs); err != nil {
return nil, trace.Wrap(err)
}
if err := a.deactivateCertAuthority(trustedCluster); err != nil {
return nil, trace.Wrap(err)
}
}
tc, err := a.Services.UpsertTrustedCluster(ctx, trustedCluster)
if err != nil {
return nil, trace.Wrap(err)
}
if err := a.emitter.EmitAuditEvent(ctx, &apievents.TrustedClusterCreate{
Metadata: apievents.Metadata{
Type: events.TrustedClusterCreateEvent,
Code: events.TrustedClusterCreateCode,
},
UserMetadata: authz.ClientUserMetadata(ctx),
ResourceMetadata: apievents.ResourceMetadata{
Name: trustedCluster.GetName(),
},
}); err != nil {
logger.WithError(err).Warn("Failed to emit trusted cluster create event.")
}
return tc, nil
}
func (a *Server) checkLocalRoles(ctx context.Context, roleMap types.RoleMap) error {
for _, mapping := range roleMap {
for _, localRole := range mapping.Local {
// expansion means dynamic mapping is in place,
// so local role is undefined
if utils.ContainsExpansion(localRole) {
continue
}
_, err := a.GetRole(ctx, localRole)
if err != nil {
if trace.IsNotFound(err) {
return trace.NotFound("a role %q referenced in a mapping %v:%v is not defined", localRole, mapping.Remote, mapping.Local)
}
return trace.Wrap(err)
}
}
}
return nil
}
// DeleteTrustedCluster removes types.CertAuthority, services.ReverseTunnel,
// and services.TrustedCluster resources.
func (a *Server) DeleteTrustedCluster(ctx context.Context, name string) error {
cn, err := a.GetClusterName()
if err != nil {
return trace.Wrap(err)
}
// This check ensures users are not deleting their root/own cluster.
if cn.GetClusterName() == name {
return trace.BadParameter("trusted cluster %q is the name of this root cluster and cannot be removed.", name)
}
// Remove all CAs
for _, caType := range []types.CertAuthType{types.HostCA, types.UserCA, types.DatabaseCA, types.OpenSSHCA} {
if err := a.DeleteCertAuthority(ctx, types.CertAuthID{Type: caType, DomainName: name}); err != nil {
if !trace.IsNotFound(err) {
return trace.Wrap(err)
}
}
}
if err := a.DeleteReverseTunnel(name); err != nil {
if !trace.IsNotFound(err) {
return trace.Wrap(err)
}
}
if err := a.Services.DeleteTrustedCluster(ctx, name); err != nil {
return trace.Wrap(err)
}
if err := a.emitter.EmitAuditEvent(ctx, &apievents.TrustedClusterDelete{
Metadata: apievents.Metadata{
Type: events.TrustedClusterDeleteEvent,
Code: events.TrustedClusterDeleteCode,
},
UserMetadata: authz.ClientUserMetadata(ctx),
ResourceMetadata: apievents.ResourceMetadata{
Name: name,
},
}); err != nil {
log.WithError(err).Warn("Failed to emit trusted cluster delete event.")
}
return nil
}
func (a *Server) establishTrust(ctx context.Context, trustedCluster types.TrustedCluster) ([]types.CertAuthority, error) {
var localCertAuthorities []types.CertAuthority
domainName, err := a.GetDomainName()
if err != nil {
return nil, trace.Wrap(err)
}
// get a list of certificate authorities for this auth server
allLocalCAs, err := a.GetCertAuthorities(ctx, types.HostCA, false)
if err != nil {
return nil, trace.Wrap(err)
}
for _, lca := range allLocalCAs {
if lca.GetClusterName() == domainName {
localCertAuthorities = append(localCertAuthorities, lca)
}
}
// create a request to validate a trusted cluster (token and local certificate authorities)
validateRequest := ValidateTrustedClusterRequest{
Token: trustedCluster.GetToken(),
CAs: localCertAuthorities,
TeleportVersion: teleport.Version,
}
// log the local certificate authorities that we are sending
log.Infof("Sending validate request; token=%s, CAs=%v", backend.MaskKeyName(validateRequest.Token), validateRequest.CAs)
// send the request to the remote auth server via the proxy
validateResponse, err := a.sendValidateRequestToProxy(trustedCluster.GetProxyAddress(), &validateRequest)
if err != nil {
log.Error(err)
if strings.Contains(err.Error(), "x509") {
return nil, trace.AccessDenied("the trusted cluster uses misconfigured HTTP/TLS certificate.")
}
return nil, trace.Wrap(err)
}
// log the remote certificate authorities we are adding
log.Infof("Received validate response; CAs=%v", validateResponse.CAs)
for _, ca := range validateResponse.CAs {
for _, keyPair := range ca.GetActiveKeys().TLS {
cert, err := tlsca.ParseCertificatePEM(keyPair.Cert)
if err != nil {
return nil, trace.Wrap(err)
}
remoteClusterName, err := tlsca.ClusterName(cert.Subject)
if err != nil {
return nil, trace.Wrap(err)
}
if remoteClusterName == domainName {
return nil, trace.BadParameter("remote cluster name can not be the same as local cluster name")
}
// TODO(klizhentas) in 2.5.0 prohibit adding trusted cluster resource name
// different from cluster name (we had no way of checking this before x509,
// because SSH CA was a public key, not a cert with metadata)
}
}
return validateResponse.CAs, nil
}
func (a *Server) addCertAuthorities(ctx context.Context, trustedCluster types.TrustedCluster, remoteCAs []types.CertAuthority) error {
// the remote auth server has verified our token. add the
// remote certificate authority to our backend
for _, remoteCertAuthority := range remoteCAs {
// change the name of the remote ca to the name of the trusted cluster
remoteCertAuthority.SetName(trustedCluster.GetName())
// wipe out roles sent from the remote cluster and set roles from the trusted cluster
remoteCertAuthority.SetRoles(nil)
if remoteCertAuthority.GetType() == types.UserCA {
for _, r := range trustedCluster.GetRoles() {
remoteCertAuthority.AddRole(r)
}
remoteCertAuthority.SetRoleMap(trustedCluster.GetRoleMap())
}
// we use create here instead of upsert to prevent people from wiping out
// their own ca if it has the same name as the remote ca
err := a.CreateCertAuthority(ctx, remoteCertAuthority)
if err != nil {
return trace.Wrap(err)
}
}
return nil
}
// DeleteRemoteCluster deletes remote cluster resource, all certificate authorities
// associated with it
func (a *Server) DeleteRemoteCluster(ctx context.Context, clusterName string) error {
// To make sure remote cluster exists - to protect against random
// clusterName requests (e.g. when clusterName is set to local cluster name)
_, err := a.GetRemoteCluster(clusterName)
if err != nil {
return trace.Wrap(err)
}
// delete cert authorities associated with the cluster
err = a.DeleteCertAuthority(ctx, types.CertAuthID{
Type: types.HostCA,
DomainName: clusterName,
})
if err != nil {
// this method could have succeeded on the first call,
// but then if the remote cluster resource could not be deleted
// it would be impossible to delete the cluster after then
if !trace.IsNotFound(err) {
return trace.Wrap(err)
}
}
// there should be no User CA in trusted clusters on the main cluster side
// per standard automation but clean up just in case
err = a.DeleteCertAuthority(ctx, types.CertAuthID{
Type: types.UserCA,
DomainName: clusterName,
})
if err != nil {
if !trace.IsNotFound(err) {
return trace.Wrap(err)
}
}
return a.Services.DeleteRemoteCluster(ctx, clusterName)
}
// GetRemoteCluster returns remote cluster by name
func (a *Server) GetRemoteCluster(clusterName string) (types.RemoteCluster, error) {
// To make sure remote cluster exists - to protect against random
// clusterName requests (e.g. when clusterName is set to local cluster name)
remoteCluster, err := a.Services.GetRemoteCluster(clusterName)
return remoteCluster, trace.Wrap(err)
}
// updateRemoteClusterStatus determines current connection status of remoteCluster and writes it to the backend
// if there are changes. Returns true if backend was updated or false if update wasn't necessary.
func (a *Server) updateRemoteClusterStatus(ctx context.Context, netConfig types.ClusterNetworkingConfig, remoteCluster types.RemoteCluster) (updated bool, err error) {
keepAliveCountMax := netConfig.GetKeepAliveCountMax()
keepAliveInterval := netConfig.GetKeepAliveInterval()
// fetch tunnel connections for the cluster to update runtime status
connections, err := a.GetTunnelConnections(remoteCluster.GetName())
if err != nil {
return false, trace.Wrap(err)
}
lastConn, err := services.LatestTunnelConnection(connections)
if err != nil {
if !trace.IsNotFound(err) {
return false, trace.Wrap(err)
}
// No tunnel connections are known, mark the cluster offline (if it
// wasn't already).
if remoteCluster.GetConnectionStatus() != teleport.RemoteClusterStatusOffline {
remoteCluster.SetConnectionStatus(teleport.RemoteClusterStatusOffline)
if err := a.UpdateRemoteCluster(ctx, remoteCluster); err != nil {
// if the cluster was concurrently updated, ignore the update. either
// the update was consistent with our view of the world, in which case
// retrying would be pointless, or the update was not consistent, in which
// case we should prioritize presenting our view in an internally-consistent
// manner rather than competing with another task.
if !trace.IsCompareFailed(err) {
return false, trace.Wrap(err)
}
}
return true, nil
}
return false, nil
}
offlineThreshold := time.Duration(keepAliveCountMax) * keepAliveInterval
tunnelStatus := services.TunnelConnectionStatus(a.clock, lastConn, offlineThreshold)
// Update remoteCluster based on lastConn. If anything changed, update it
// in the backend too.
prevConnectionStatus := remoteCluster.GetConnectionStatus()
prevLastHeartbeat := remoteCluster.GetLastHeartbeat()
remoteCluster.SetConnectionStatus(tunnelStatus)
// Only bump LastHeartbeat if it's newer.
if lastConn.GetLastHeartbeat().After(prevLastHeartbeat) {
remoteCluster.SetLastHeartbeat(lastConn.GetLastHeartbeat().UTC())
}
if prevConnectionStatus != remoteCluster.GetConnectionStatus() || !prevLastHeartbeat.Equal(remoteCluster.GetLastHeartbeat()) {
if err := a.UpdateRemoteCluster(ctx, remoteCluster); err != nil {
// if the cluster was concurrently updated, ignore the update. either
// the update was consistent with our view of the world, in which case
// retrying would be pointless, or the update was not consistent, in which
// case we should prioritize presenting our view in an internally-consistent
// manner rather than competing with another task.
if !trace.IsCompareFailed(err) {
return false, trace.Wrap(err)
}
}
return true, nil
}
return false, nil
}
// GetRemoteClusters returns remote clusters with updated statuses
func (a *Server) GetRemoteClusters(opts ...services.MarshalOption) ([]types.RemoteCluster, error) {
// To make sure remote cluster exists - to protect against random
// clusterName requests (e.g. when clusterName is set to local cluster name)
remoteClusters, err := a.Services.GetRemoteClusters(opts...)
return remoteClusters, trace.Wrap(err)
}
func (a *Server) validateTrustedCluster(ctx context.Context, validateRequest *ValidateTrustedClusterRequest) (resp *ValidateTrustedClusterResponse, err error) {
defer func() {
if err != nil {
log.WithError(err).Info("Trusted cluster validation failed")
}
}()
log.Debugf("Received validate request: token=%s, CAs=%v", backend.MaskKeyName(validateRequest.Token), validateRequest.CAs)
domainName, err := a.GetDomainName()
if err != nil {
return nil, trace.Wrap(err)
}
// validate that we generated the token
tokenLabels, err := a.validateTrustedClusterToken(ctx, validateRequest.Token)
if err != nil {
return nil, trace.Wrap(err)
}
if len(validateRequest.CAs) != 1 {
return nil, trace.AccessDenied("expected exactly one certificate authority, received %v", len(validateRequest.CAs))
}
remoteCA := validateRequest.CAs[0]
err = remoteCA.CheckAndSetDefaults()
if err != nil {
return nil, trace.Wrap(err)
}
if remoteCA.GetType() != types.HostCA {
return nil, trace.AccessDenied("expected host certificate authority, received CA with type %q", remoteCA.GetType())
}
// a host CA shouldn't have a rolemap or roles in the first place
remoteCA.SetRoleMap(nil)
remoteCA.SetRoles(nil)
remoteClusterName := remoteCA.GetName()
if remoteClusterName == domainName {
return nil, trace.AccessDenied("remote cluster has same name as this cluster: %v", domainName)
}
_, err = a.GetTrustedCluster(ctx, remoteClusterName)
if err == nil {
return nil, trace.AccessDenied("remote cluster has same name as trusted cluster: %v", remoteClusterName)
} else if !trace.IsNotFound(err) {
return nil, trace.Wrap(err)
}
remoteCluster, err := types.NewRemoteCluster(remoteClusterName)
if err != nil {
return nil, trace.Wrap(err)
}
if len(tokenLabels) != 0 {
meta := remoteCluster.GetMetadata()
meta.Labels = utils.CopyStringsMap(tokenLabels)
remoteCluster.SetMetadata(meta)
}
remoteCluster.SetConnectionStatus(teleport.RemoteClusterStatusOffline)
err = a.CreateRemoteCluster(remoteCluster)
if err != nil {
if !trace.IsAlreadyExists(err) {
return nil, trace.Wrap(err)
}
}
err = a.UpsertCertAuthority(ctx, remoteCA)
if err != nil {
return nil, trace.Wrap(err)
}
// export local cluster certificate authority and return it to the cluster
validateResponse := ValidateTrustedClusterResponse{}
validateResponse.CAs, err = getLeafClusterCAs(ctx, a, domainName, validateRequest)
if err != nil {
return nil, trace.Wrap(err)
}
// log the local certificate authorities we are sending
log.Debugf("Sending validate response: CAs=%v", validateResponse.CAs)
return &validateResponse, nil
}
// getLeafClusterCAs returns a slice with Cert Authorities that should be returned in response to ValidateTrustedClusterRequest.
func getLeafClusterCAs(ctx context.Context, srv *Server, domainName string, validateRequest *ValidateTrustedClusterRequest) ([]types.CertAuthority, error) {
certTypes, err := getCATypesForLeaf(validateRequest)
if err != nil {
return nil, trace.Wrap(err)
}
caCerts := make([]types.CertAuthority, 0, len(certTypes))
for _, caType := range certTypes {
certAuthority, err := srv.GetCertAuthority(
ctx,
types.CertAuthID{Type: caType, DomainName: domainName},
false)
if err != nil {
return nil, trace.Wrap(err)
}
caCerts = append(caCerts, certAuthority)
}
return caCerts, nil
}
// getCATypesForLeaf returns the list of CA certificates that should be sync in response to ValidateTrustedClusterRequest.
func getCATypesForLeaf(validateRequest *ValidateTrustedClusterRequest) ([]types.CertAuthType, error) {
var (
err error
databaseCASupported bool
openSSHCASupported bool
)
if validateRequest.TeleportVersion != "" {
// (*ValidateTrustedClusterRequest).TeleportVersion was added in Teleport 10.0. If the request comes from an older
// cluster this field will be empty.
databaseCASupported, err = utils.MinVerWithoutPreRelease(validateRequest.TeleportVersion, constants.DatabaseCAMinVersion)
if err != nil {
return nil, trace.Wrap(err, "failed to parse Teleport version: %q", validateRequest.TeleportVersion)
}
openSSHCASupported, err = utils.MinVerWithoutPreRelease(validateRequest.TeleportVersion, constants.OpenSSHCAMinVersion)
if err != nil {
return nil, trace.Wrap(err, "failed to parse Teleport version: %q", validateRequest.TeleportVersion)
}
}
certTypes := []types.CertAuthType{types.HostCA, types.UserCA}
if databaseCASupported {
// Database CA was introduced in Teleport 10.0. Do not send it to older clusters
// as they don't understand it.
certTypes = append(certTypes, types.DatabaseCA)
}
if openSSHCASupported {
// OpenSSH CA was introduced in Teleport 12.0. Do not send it to older clusters
// as they don't understand it.
certTypes = append(certTypes, types.OpenSSHCA)
}
return certTypes, nil
}
func (a *Server) validateTrustedClusterToken(ctx context.Context, tokenName string) (map[string]string, error) {
provisionToken, err := a.ValidateToken(ctx, tokenName)
if err != nil {
return nil, trace.AccessDenied("the remote server denied access: invalid cluster token")
}
if !provisionToken.GetRoles().Include(types.RoleTrustedCluster) {
return nil, trace.AccessDenied("role does not match")
}
return provisionToken.GetMetadata().Labels, nil
}
func (a *Server) sendValidateRequestToProxy(host string, validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
proxyAddr := url.URL{
Scheme: "https",
Host: host,
}
opts := []roundtrip.ClientParam{
roundtrip.SanitizerEnabled(true),
}
if lib.IsInsecureDevMode() {
log.Warn("The setting insecureSkipVerify is used to communicate with proxy. Make sure you intend to run Teleport in insecure mode!")
// Get the default transport, this allows picking up proxy from the
// environment.
defaultTransport, ok := http.DefaultTransport.(*http.Transport)
if !ok {
return nil, trace.BadParameter("invalid transport type %T", http.DefaultTransport)
}
// Clone the transport to not modify the global instance.
tr := defaultTransport.Clone()
// Disable certificate checking while in debug mode.
tlsConfig := utils.TLSConfig(a.cipherSuites)
tlsConfig.InsecureSkipVerify = true
tr.TLSClientConfig = tlsConfig
insecureWebClient := &http.Client{
Transport: tracehttp.NewTransport(tr),
}
opts = append(opts, roundtrip.HTTPClient(insecureWebClient))
}
clt, err := roundtrip.NewClient(proxyAddr.String(), teleport.WebAPIVersion, opts...)
if err != nil {
return nil, trace.Wrap(err)
}
validateRequestRaw, err := validateRequest.ToRaw()
if err != nil {
return nil, trace.Wrap(err)
}
out, err := httplib.ConvertResponse(clt.PostJSON(context.TODO(), clt.Endpoint("webapi", "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
}
type ValidateTrustedClusterRequest struct {
Token string `json:"token"`
CAs []types.CertAuthority `json:"certificate_authorities"`
TeleportVersion string `json:"teleport_version"`
}
func (v *ValidateTrustedClusterRequest) ToRaw() (*ValidateTrustedClusterRequestRaw, error) {
cas := [][]byte{}
for _, certAuthority := range v.CAs {
data, err := services.MarshalCertAuthority(certAuthority)
if err != nil {
return nil, trace.Wrap(err)
}
cas = append(cas, data)
}
return &ValidateTrustedClusterRequestRaw{
Token: v.Token,
CAs: cas,
TeleportVersion: v.TeleportVersion,
}, nil
}
type ValidateTrustedClusterRequestRaw struct {
Token string `json:"token"`
CAs [][]byte `json:"certificate_authorities"`
TeleportVersion string `json:"teleport_version"`
}
func (v *ValidateTrustedClusterRequestRaw) ToNative() (*ValidateTrustedClusterRequest, error) {
cas := []types.CertAuthority{}
for _, rawCertAuthority := range v.CAs {
certAuthority, err := services.UnmarshalCertAuthority(rawCertAuthority)
if err != nil {
return nil, trace.Wrap(err)
}
cas = append(cas, certAuthority)
}
return &ValidateTrustedClusterRequest{
Token: v.Token,
CAs: cas,
TeleportVersion: v.TeleportVersion,
}, nil
}
type ValidateTrustedClusterResponse struct {
CAs []types.CertAuthority `json:"certificate_authorities"`
}
func (v *ValidateTrustedClusterResponse) ToRaw() (*ValidateTrustedClusterResponseRaw, error) {
cas := [][]byte{}
for _, certAuthority := range v.CAs {
data, err := services.MarshalCertAuthority(certAuthority)
if err != nil {
return nil, trace.Wrap(err)
}
cas = append(cas, data)
}
return &ValidateTrustedClusterResponseRaw{
CAs: cas,
}, nil
}
type ValidateTrustedClusterResponseRaw struct {
CAs [][]byte `json:"certificate_authorities"`
}
func (v *ValidateTrustedClusterResponseRaw) ToNative() (*ValidateTrustedClusterResponse, error) {
cas := []types.CertAuthority{}
for _, rawCertAuthority := range v.CAs {
certAuthority, err := services.UnmarshalCertAuthority(rawCertAuthority)
if err != nil {
return nil, trace.Wrap(err)
}
cas = append(cas, certAuthority)
}
return &ValidateTrustedClusterResponse{
CAs: cas,
}, nil
}
// activateCertAuthority will activate both the user and host certificate
// authority given in the services.TrustedCluster resource.
func (a *Server) activateCertAuthority(t types.TrustedCluster) error {
err := a.ActivateCertAuthority(types.CertAuthID{Type: types.UserCA, DomainName: t.GetName()})
if err != nil {
return trace.Wrap(err)
}
return trace.Wrap(a.ActivateCertAuthority(types.CertAuthID{Type: types.HostCA, DomainName: t.GetName()}))
}
// deactivateCertAuthority will deactivate both the user and host certificate
// authority given in the services.TrustedCluster resource.
func (a *Server) deactivateCertAuthority(t types.TrustedCluster) error {
err := a.DeactivateCertAuthority(types.CertAuthID{Type: types.UserCA, DomainName: t.GetName()})
if err != nil {
return trace.Wrap(err)
}
return trace.Wrap(a.DeactivateCertAuthority(types.CertAuthID{Type: types.HostCA, DomainName: t.GetName()}))
}
// createReverseTunnel will create a services.ReverseTunnel givenin the
// services.TrustedCluster resource.
func (a *Server) createReverseTunnel(t types.TrustedCluster) error {
reverseTunnel, err := types.NewReverseTunnel(
t.GetName(),
[]string{t.GetReverseTunnelAddress()},
)
if err != nil {
return trace.Wrap(err)
}
return trace.Wrap(a.UpsertReverseTunnel(reverseTunnel))
}