mirror of
https://github.com/gravitational/teleport.git
synced 2026-08-29 05:27:37 +08:00
1b2785b41d
* MWI: Add lock targets for join token name and bot instance ID This adds two new lock targets meant to help lock specific bot instances without affecting all bots sharing a single user: - Bot Instance ID: Targets a bot instance UUID, which has been assigned automatically to unique bot instances for some time - Join token name: Targets the join token through which the bot joined Bot instance ID locks are most useful for traditional token-joined bots, since tokens are single use and bots have no way to onboard again without human intervention if their old certs (and old bot instance) expire. Join token locks are useful for bots using delegated join methods. They are particularly useful for bound keypair joining, where there is a direct 1:1 relationship between a "bot instance" and a token, even though that bot ID will change each time a recovery takes place. Note that this does not currently set the join token for nodes even though that would theoretically be possible. We could consider supporting node locking in the future if there's demand. * Set join token cert request field for non-renewable bot identities * Fix ASN ID and pass through join token name in impersonated certs * Tweak docstrings and add missing references for lib/decision * Clarify docstrings Clarifies various docstrings and makes sure they mention `token` joined bots cannot be targeted. * Fix failing tests
126 lines
4.0 KiB
Go
126 lines
4.0 KiB
Go
/*
|
|
* Teleport
|
|
* Copyright (C) 2023 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_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/services"
|
|
"github.com/gravitational/teleport/lib/sshca"
|
|
"github.com/gravitational/teleport/lib/tlsca"
|
|
)
|
|
|
|
func TestLockTargetsFromTLSIdentity(t *testing.T) {
|
|
t.Run("all locks", func(t *testing.T) {
|
|
identity := tlsca.Identity{
|
|
Username: "llama",
|
|
Groups: []string{"access", "editor"},
|
|
MFAVerified: "mfa-device-id",
|
|
ActiveRequests: []string{"access-request-1", "access-request-2"},
|
|
DeviceExtensions: tlsca.DeviceExtensions{
|
|
DeviceID: "trusted-device-id",
|
|
},
|
|
JoinToken: "example",
|
|
BotInstanceID: "a-b-c-d",
|
|
}
|
|
|
|
// Test.
|
|
got := services.LockTargetsFromTLSIdentity(identity)
|
|
|
|
want := []types.LockTarget{
|
|
{User: identity.Username},
|
|
{MFADevice: identity.MFAVerified},
|
|
{Device: identity.DeviceExtensions.DeviceID},
|
|
{JoinToken: "example"},
|
|
{BotInstanceID: "a-b-c-d"},
|
|
}
|
|
// Insert roles at the start to match `got`s order.
|
|
// The test itself doesn't care about the order, it's just easier to test
|
|
// this way.
|
|
want = append(services.RolesToLockTargets(identity.Groups), want...)
|
|
for _, request := range identity.ActiveRequests {
|
|
want = append(want, types.LockTarget{AccessRequest: request})
|
|
}
|
|
if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
|
|
t.Errorf("LockTargetsFromTLSIdentity mismatch (-want +got)\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSSHAccessLockTargets(t *testing.T) {
|
|
t.Run("all locks", func(t *testing.T) {
|
|
const clusterName = "mycluster"
|
|
const serverID = "myserver"
|
|
const mfaDevice = "my-mfa-device-1"
|
|
const trustedDevice = "my-trusted-device-1"
|
|
const osLogin = "camel"
|
|
const teleportUser = "llama"
|
|
const joinToken = "some-join-token"
|
|
const botInstanceID = "a-b-c-d"
|
|
mappedRoles := []string{"access", "editor"}
|
|
unmappedRoles := []string{"unmapped-role-1", "unmapped-role-2", "access"}
|
|
accessRequests := []string{"access-request-1", "access-request-2"}
|
|
|
|
unmappedIdentity := &sshca.Identity{
|
|
Username: teleportUser,
|
|
MFAVerified: mfaDevice,
|
|
DeviceID: trustedDevice,
|
|
Roles: unmappedRoles,
|
|
ActiveRequests: accessRequests,
|
|
JoinToken: joinToken,
|
|
BotInstanceID: botInstanceID,
|
|
}
|
|
|
|
accessInfo := &services.AccessInfo{
|
|
Username: "llama",
|
|
Roles: mappedRoles,
|
|
}
|
|
|
|
got := services.SSHAccessLockTargets(clusterName, serverID, osLogin, accessInfo, unmappedIdentity)
|
|
want := []types.LockTarget{
|
|
{User: teleportUser},
|
|
{ServerID: serverID},
|
|
{ServerID: serverID + "." + clusterName},
|
|
{MFADevice: mfaDevice},
|
|
{Device: trustedDevice},
|
|
{JoinToken: joinToken},
|
|
{BotInstanceID: botInstanceID},
|
|
}
|
|
for _, role := range mappedRoles {
|
|
want = append(want, types.LockTarget{Role: role})
|
|
}
|
|
for _, role := range unmappedRoles[:len(unmappedRoles)-1] /* skip duplicate role */ {
|
|
want = append(want, types.LockTarget{Role: role})
|
|
}
|
|
for _, request := range accessRequests {
|
|
want = append(want, types.LockTarget{AccessRequest: request})
|
|
}
|
|
|
|
want = append(want, types.LockTarget{Login: osLogin})
|
|
|
|
if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
|
|
t.Errorf("SSHAccessLockTargets mismatch (-want +got)\n%s", diff)
|
|
}
|
|
})
|
|
}
|