mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-29 17:58:15 +08:00
* Add `Beam` resource protobuf definition * Add beams backend service * Fix test and lint failures * Delete all beam alias leases on `DeleteAllBeams` * Apply PR feedback * Restructure beam storage service Following comments on the PR and a Slack conversation with Edorado, this commit restructures the storage service interface to reflect how it'll actually be used. For example, we now write the beam and its supporting resources in one atomic operation, rather than lots of little fallible writes. It also incorporates other feedback such as adding a field to track the provision state of the actual beam VM. * Use `types.GetExpiry` helper to avoid protobuf epoch issues * Fix linting errors * Store `DelegationSession` with beam resources Also remove duplicate `itemFromWorkloadIdentity` definition * Remove cross-service dependency Following @espadolini's comment, the `BeamService` is no longer responsible for writing the actual node, app, token, etc. records to the backend. Instead, those services expose `AppendPut*Actions` and `AppendDelete*Actions` methods and it's the caller's responsibility to call `Backend.AtomicWrite` with them. * Move `Append*Actions` methods to "internal" service interfaces * Fix test errors in tests * Store users without secrets * Rename user methods for clarity about what's included or not * Fix accidental cache usage * Fix broken reference from master * Do not set an expiry on beam alias In case the alias gets reassigned before the original beam is cleaned up
139 lines
5.0 KiB
Go
139 lines
5.0 KiB
Go
// Teleport
|
|
// Copyright (C) 2026 Gravitational, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/gravitational/trace"
|
|
|
|
delegationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/delegation/v1"
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/backend"
|
|
)
|
|
|
|
// DelegationSessions is an interface over the DelegationSessions service. This
|
|
// interface may also be implemented by a client to allow remote and local
|
|
// consumers to access the resource in a similar way.
|
|
type DelegationSessions interface {
|
|
// CreateDelegationSession creates a new delegation session.
|
|
CreateDelegationSession(ctx context.Context, session *delegationv1.DelegationSession) (*delegationv1.DelegationSession, error)
|
|
|
|
// GetDelegationSession reads a delegation session using its ID.
|
|
GetDelegationSession(ctx context.Context, id string) (*delegationv1.DelegationSession, error)
|
|
|
|
// DeleteDelegationSession deletes a delegation session using its ID.
|
|
DeleteDelegationSession(ctx context.Context, id string) error
|
|
|
|
// AppendPutDelegationSessionActions adds conditional actions to an atomic
|
|
// write to create or update a DelegationSession.
|
|
AppendPutDelegationSessionActions(
|
|
actions []backend.ConditionalAction,
|
|
session *delegationv1.DelegationSession,
|
|
condition backend.Condition,
|
|
) ([]backend.ConditionalAction, error)
|
|
|
|
// AppendDeleteDelegationSessionActions adds conditional actions to an atomic
|
|
// write to delete a DelegationSession.
|
|
AppendDeleteDelegationSessionActions(
|
|
actions []backend.ConditionalAction,
|
|
id string,
|
|
condition backend.Condition,
|
|
) ([]backend.ConditionalAction, error)
|
|
}
|
|
|
|
// ValidateDelegationSession validates a DelegationSession object.
|
|
func ValidateDelegationSession(p *delegationv1.DelegationSession) error {
|
|
switch {
|
|
case p == nil:
|
|
return trace.BadParameter("must not be nil")
|
|
case p.GetKind() != types.KindDelegationSession:
|
|
return trace.BadParameter("kind: must be %s", types.KindDelegationSession)
|
|
case p.GetVersion() != types.V1:
|
|
return trace.BadParameter("version: must be %s", types.V1)
|
|
case p.GetMetadata().GetName() == "":
|
|
return trace.BadParameter("metadata.name: is required")
|
|
case p.GetMetadata().GetExpires() == nil:
|
|
return trace.BadParameter("metadata.expires: is required")
|
|
case p.GetSpec().GetUser() == "":
|
|
return trace.BadParameter("spec.user: is required")
|
|
}
|
|
|
|
if len(p.GetSpec().GetResources()) == 0 {
|
|
return trace.BadParameter("spec.resources: at least one resource is required")
|
|
}
|
|
|
|
var hasWildcard, hasExplicit bool
|
|
for idx, spec := range p.GetSpec().GetResources() {
|
|
if err := ValidateDelegationResourceSpec(spec); err != nil {
|
|
return trace.BadParameter("spec.resources[%d]: invalid resource spec: %v", idx, err)
|
|
}
|
|
if spec.GetKind() == types.Wildcard {
|
|
hasWildcard = true
|
|
} else {
|
|
hasExplicit = true
|
|
}
|
|
if hasWildcard && hasExplicit {
|
|
return trace.BadParameter("spec.resources: wildcard is mutually exclusive with explicit resources")
|
|
}
|
|
}
|
|
|
|
if len(p.GetSpec().GetAuthorizedUsers()) == 0 {
|
|
return trace.BadParameter("spec.authorized_users: at least one user is required")
|
|
}
|
|
|
|
for idx, user := range p.GetSpec().GetAuthorizedUsers() {
|
|
if user.GetKind() != types.KindBot {
|
|
return trace.BadParameter("spec.authorized_users[%d].kind: must be %s", idx, types.KindBot)
|
|
}
|
|
if user.GetBotName() == "" {
|
|
return trace.BadParameter("spec.authorized_users[%d].bot_name: is required", idx)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateDelegationResourceSpec validates a DelegationResourceSpec object.
|
|
func ValidateDelegationResourceSpec(s *delegationv1.DelegationResourceSpec) error {
|
|
if s.GetName() == "" {
|
|
return trace.BadParameter("name is required")
|
|
}
|
|
|
|
// TODO(boxofrad): implement support for constraints.
|
|
if s.GetConstraints() != nil {
|
|
return trace.BadParameter("constraints are not yet supported")
|
|
}
|
|
|
|
switch s.GetKind() {
|
|
case types.KindApp, types.KindDatabase, types.KindNode, types.KindKubernetesCluster, types.KindWindowsDesktop, types.KindGitServer, types.Wildcard:
|
|
case "":
|
|
return trace.BadParameter("kind is required")
|
|
default:
|
|
return trace.BadParameter("invalid kind: %q", s.GetKind())
|
|
}
|
|
|
|
switch {
|
|
case s.GetKind() == types.Wildcard && s.GetName() != types.Wildcard:
|
|
return trace.BadParameter("name must also be '*'")
|
|
case s.GetKind() != types.Wildcard && s.GetName() == types.Wildcard:
|
|
return trace.BadParameter("kind must also be '*'")
|
|
}
|
|
|
|
return nil
|
|
}
|