mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
d1c6b0618e
Mostly duplicated imports and redundant types in struct literals.
749 lines
23 KiB
Go
749 lines
23 KiB
Go
/*
|
|
Copyright 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/x509"
|
|
"encoding/pem"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gravitational/teleport/api/defaults"
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/auth/native"
|
|
"github.com/gravitational/trace"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
|
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
|
"github.com/jonboulle/clockwork"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type ec2Instance struct {
|
|
iid []byte
|
|
account string
|
|
region string
|
|
instanceID string
|
|
pendingTime time.Time
|
|
}
|
|
|
|
var (
|
|
instance1 = ec2Instance{
|
|
iid: []byte(`MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIB
|
|
23sKICAiYWNjb3VudElkIiA6ICIyNzg1NzYyMjA0NTMiLAogICJhcmNoaXRlY3R1cmUiIDogIng4
|
|
Nl82NCIsCiAgImF2YWlsYWJpbGl0eVpvbmUiIDogInVzLXdlc3QtMmEiLAogICJiaWxsaW5nUHJv
|
|
ZHVjdHMiIDogbnVsbCwKICAiZGV2cGF5UHJvZHVjdENvZGVzIiA6IG51bGwsCiAgIm1hcmtldHBs
|
|
YWNlUHJvZHVjdENvZGVzIiA6IG51bGwsCiAgImltYWdlSWQiIDogImFtaS0wZmE5ZTFmNjQxNDJj
|
|
ZGUxNyIsCiAgImluc3RhbmNlSWQiIDogImktMDc4NTE3Y2E4YTcwYTFkZGUiLAogICJpbnN0YW5j
|
|
ZVR5cGUiIDogInQyLm1lZGl1bSIsCiAgImtlcm5lbElkIiA6IG51bGwsCiAgInBlbmRpbmdUaW1l
|
|
IiA6ICIyMDIxLTA5LTAzVDIxOjI1OjQ0WiIsCiAgInByaXZhdGVJcCIgOiAiMTAuMC4wLjIwOSIs
|
|
CiAgInJhbWRpc2tJZCIgOiBudWxsLAogICJyZWdpb24iIDogInVzLXdlc3QtMiIsCiAgInZlcnNp
|
|
b24iIDogIjIwMTctMDktMzAiCn0AAAAAAAAxggIvMIICKwIBATBpMFwxCzAJBgNVBAYTAlVTMRkw
|
|
FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6
|
|
b24gV2ViIFNlcnZpY2VzIExMQwIJALZL3lrQCSTMMA0GCWCGSAFlAwQCAQUAoIGYMBgGCSqGSIb3
|
|
DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMDkwMzIxMjU0N1owLQYJKoZIhvcN
|
|
AQk0MSAwHjANBglghkgBZQMEAgEFAKENBgkqhkiG9w0BAQsFADAvBgkqhkiG9w0BCQQxIgQgCH2d
|
|
JiKmdx9uhxlm8ObWAvFOhqJb7k79+DW/T3ezwVUwDQYJKoZIhvcNAQELBQAEggEANWautigs/qZ6
|
|
w8g5/EfWsAFj8kHgUD+xqsQ1HDrBUx3IQ498NMBZ78379B8RBfuzeVjbaf+yugov0fYrDbGvSRRw
|
|
myy49TfZ9gdlpWQXzwSg3OPMDNToRoKw00/LQjSxcTCaPP4vMDEIjYMUqZ3i4uWYJJJ0Lb7fDMDk
|
|
Anu7yHolVfbnvIAuZe8lGpc7ofCSBG5wulm+/pqzO25YPMH1cLEvOadE+3N2GxK6gRTLJoE98rsm
|
|
LDp6OuU/b2QfaxU0ec6OogdtSJto/URI0/ygHmNAzBis470A29yh5nVwm6AkY4krjPsK7uiBIRhs
|
|
lr5x0X6+ggQfF2BKAJ/BRcAHNgAAAAAAAA==`),
|
|
account: "278576220453",
|
|
region: "us-west-2",
|
|
instanceID: "i-078517ca8a70a1dde",
|
|
pendingTime: time.Date(2021, time.September, 3, 21, 25, 44, 0, time.UTC),
|
|
}
|
|
instance2 = ec2Instance{
|
|
iid: []byte(`MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwGggCSABIIB
|
|
3XsKICAiYWNjb3VudElkIiA6ICI4ODM0NzQ2NjI4ODgiLAogICJhcmNoaXRlY3R1cmUiIDogIng4
|
|
Nl82NCIsCiAgImF2YWlsYWJpbGl0eVpvbmUiIDogInVzLXdlc3QtMWMiLAogICJiaWxsaW5nUHJv
|
|
ZHVjdHMiIDogbnVsbCwKICAiZGV2cGF5UHJvZHVjdENvZGVzIiA6IG51bGwsCiAgIm1hcmtldHBs
|
|
YWNlUHJvZHVjdENvZGVzIiA6IG51bGwsCiAgImltYWdlSWQiIDogImFtaS0wY2UzYzU1YTMxZDI5
|
|
MDQwZSIsCiAgImluc3RhbmNlSWQiIDogImktMDFiOTQwYzQ1ZmQxMWZlNzQiLAogICJpbnN0YW5j
|
|
ZVR5cGUiIDogInQyLm1pY3JvIiwKICAia2VybmVsSWQiIDogbnVsbCwKICAicGVuZGluZ1RpbWUi
|
|
IDogIjIwMjEtMDktMTFUMDA6MTQ6MThaIiwKICAicHJpdmF0ZUlwIiA6ICIxNzIuMzEuMTIuMjUx
|
|
IiwKICAicmFtZGlza0lkIiA6IG51bGwsCiAgInJlZ2lvbiIgOiAidXMtd2VzdC0xIiwKICAidmVy
|
|
c2lvbiIgOiAiMjAxNy0wOS0zMCIKfQAAAAAAADGCAi8wggIrAgEBMGkwXDELMAkGA1UEBhMCVVMx
|
|
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0Ft
|
|
YXpvbiBXZWIgU2VydmljZXMgTExDAgkA00+QilzIS0gwDQYJYIZIAWUDBAIBBQCggZgwGAYJKoZI
|
|
hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwOTExMDAxNDIyWjAtBgkqhkiG
|
|
9w0BCTQxIDAeMA0GCWCGSAFlAwQCAQUAoQ0GCSqGSIb3DQEBCwUAMC8GCSqGSIb3DQEJBDEiBCDS
|
|
1gNvxbYnEL6plVu8X/QmKPJFJwIJfi+2hIVjyKAOtjANBgkqhkiG9w0BAQsFAASCAQABKmghATg8
|
|
VXkdiIGcTIPfKrc2v/zEIdLUAi+Ew5lrGUVjnNqrP9irGK4d9sVtcu/8UKp9RDoeJOQ6I/pRcwvT
|
|
PJVHlhGnLyybr5ZVqkxiC09GASNnPe12dzCKkKD2rvW6mGR91cxpM94Xqi5UA/ZRqiXwpHo3LECN
|
|
Gu38Hpdv6sBgD/av2Ohd+vEH2zvYVkp7ZfnFuDLWRSBQZgmKwVKVdOjrMmP32vb3vzhMBuOj+jbQ
|
|
RwEmYIkRaEGNbrZgatjMJYmTWuLG26zws3avOK6kL6u38DV3wJPVB/G0Ira5MvC/ojGya+DrVngW
|
|
VUP+3jgenPrd7OyCWPSwOoOBMhSlAAAAAAAA`),
|
|
account: "883474662888",
|
|
region: "us-west-1",
|
|
instanceID: "i-01b940c45fd11fe74",
|
|
pendingTime: time.Date(2021, time.September, 11, 0, 14, 18, 0, time.UTC),
|
|
}
|
|
)
|
|
|
|
type ec2ClientNoInstance struct{}
|
|
type ec2ClientNotRunning struct{}
|
|
type ec2ClientRunning struct{}
|
|
|
|
func (c ec2ClientNoInstance) DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
|
|
return &ec2.DescribeInstancesOutput{}, nil
|
|
}
|
|
|
|
func (c ec2ClientNotRunning) DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
|
|
return &ec2.DescribeInstancesOutput{
|
|
Reservations: []ec2types.Reservation{
|
|
{
|
|
Instances: []ec2types.Instance{
|
|
{
|
|
InstanceId: ¶ms.InstanceIds[0],
|
|
State: &ec2types.InstanceState{
|
|
Name: ec2types.InstanceStateNameTerminated,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (c ec2ClientRunning) DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
|
|
return &ec2.DescribeInstancesOutput{
|
|
Reservations: []ec2types.Reservation{
|
|
{
|
|
Instances: []ec2types.Instance{
|
|
{
|
|
InstanceId: ¶ms.InstanceIds[0],
|
|
State: &ec2types.InstanceState{
|
|
Name: ec2types.InstanceStateNameRunning,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func TestAuth_RegisterUsingToken_EC2(t *testing.T) {
|
|
ctx := context.Background()
|
|
p, err := newTestPack(ctx, t.TempDir())
|
|
require.NoError(t, err)
|
|
a := p.a
|
|
|
|
// upsert a node to test duplicates
|
|
node := &types.ServerV2{
|
|
Kind: types.KindNode,
|
|
Version: types.V2,
|
|
Metadata: types.Metadata{
|
|
Name: instance2.account + "-" + instance2.instanceID,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
}
|
|
_, err = a.UpsertNode(ctx, node)
|
|
require.NoError(t, err)
|
|
|
|
sshPrivateKey, sshPublicKey, err := native.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
tlsPublicKey, err := PrivateKeyToPublicKeyTLS(sshPrivateKey)
|
|
require.NoError(t, err)
|
|
|
|
isNil := func(err error) bool {
|
|
return err == nil
|
|
}
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
tokenSpec types.ProvisionTokenSpecV2
|
|
ec2Client ec2Client
|
|
request types.RegisterUsingTokenRequest
|
|
expectError func(error) bool
|
|
clock clockwork.Clock
|
|
}{
|
|
{
|
|
desc: "basic passing case",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: isNil,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "pass with multiple rules",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance2.account,
|
|
AWSRegions: []string{instance2.region},
|
|
},
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: isNil,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "pass with multiple regions",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{"us-east-1", instance1.region, "us-east-2"},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: isNil,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "pass with no regions",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: isNil,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "wrong account",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: "bad account",
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "wrong region",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{"bad region"},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "bad HostID",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: "bad host id",
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "no identity document",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "bad identity document",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: []byte("bad document"),
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "instance already joined",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance2.account,
|
|
AWSRegions: []string{instance2.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance2.account + "-" + instance2.instanceID,
|
|
EC2IdentityDocument: instance2.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance2.pendingTime),
|
|
},
|
|
{
|
|
desc: "instance already joined, fake ID",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance2.account,
|
|
AWSRegions: []string{instance2.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: "fake id",
|
|
EC2IdentityDocument: instance2.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance2.pendingTime),
|
|
},
|
|
{
|
|
desc: "instance not running",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientNotRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "instance not exists",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientNoInstance{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime),
|
|
},
|
|
{
|
|
desc: "TTL expired",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime.Add(5*time.Minute + time.Second)),
|
|
},
|
|
{
|
|
desc: "custom TTL pass",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
AWSIIDTTL: types.Duration(10 * time.Minute),
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: isNil,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime.Add(9 * time.Minute)),
|
|
},
|
|
{
|
|
desc: "custom TTL fail",
|
|
tokenSpec: types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{types.RoleNode},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
AWSIIDTTL: types.Duration(10 * time.Minute),
|
|
},
|
|
ec2Client: ec2ClientRunning{},
|
|
request: types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: types.RoleNode,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
},
|
|
expectError: trace.IsAccessDenied,
|
|
clock: clockwork.NewFakeClockAt(instance1.pendingTime.Add(11 * time.Minute)),
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
clock := tc.clock
|
|
if clock == nil {
|
|
clock = clockwork.NewRealClock()
|
|
}
|
|
a.clock = clock
|
|
|
|
token, err := types.NewProvisionTokenFromSpec(
|
|
"test_token",
|
|
time.Now().Add(time.Minute),
|
|
tc.tokenSpec)
|
|
require.NoError(t, err)
|
|
|
|
err = a.UpsertToken(context.Background(), token)
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.WithValue(context.Background(), ec2ClientKey{}, tc.ec2Client)
|
|
|
|
// set common request values here to avoid setting them in every
|
|
// testcase
|
|
tc.request.PublicSSHKey = sshPublicKey
|
|
tc.request.PublicTLSKey = tlsPublicKey
|
|
|
|
_, err = a.RegisterUsingToken(ctx, &tc.request)
|
|
require.True(t, tc.expectError(err))
|
|
|
|
err = a.DeleteToken(context.Background(), token.GetName())
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestAWSCerts asserts that all certificates parse
|
|
func TestAWSCerts(t *testing.T) {
|
|
for _, certBytes := range awsRSA2048CertBytes {
|
|
certPEM, _ := pem.Decode(certBytes)
|
|
_, err := x509.ParseCertificate(certPEM.Bytes)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
// TestHostUniqueCheck tests the uniqueness check used by checkEC2JoinRequest
|
|
func TestHostUniqueCheck(t *testing.T) {
|
|
ctx := context.Background()
|
|
p, err := newTestPack(ctx, t.TempDir())
|
|
require.NoError(t, err)
|
|
a := p.a
|
|
|
|
a.clock = clockwork.NewFakeClockAt(instance1.pendingTime)
|
|
|
|
token, err := types.NewProvisionTokenFromSpec(
|
|
"test_token",
|
|
time.Now().Add(time.Minute),
|
|
types.ProvisionTokenSpecV2{
|
|
Roles: []types.SystemRole{
|
|
types.RoleNode,
|
|
types.RoleProxy,
|
|
types.RoleKube,
|
|
types.RoleDatabase,
|
|
types.RoleApp,
|
|
},
|
|
Allow: []*types.TokenRule{
|
|
{
|
|
AWSAccount: instance1.account,
|
|
AWSRegions: []string{instance1.region},
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = a.UpsertToken(context.Background(), token)
|
|
require.NoError(t, err)
|
|
|
|
sshPrivateKey, sshPublicKey, err := native.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
tlsPublicKey, err := PrivateKeyToPublicKeyTLS(sshPrivateKey)
|
|
require.NoError(t, err)
|
|
|
|
testCases := []struct {
|
|
role types.SystemRole
|
|
upserter func(name string)
|
|
}{
|
|
{
|
|
role: types.RoleNode,
|
|
upserter: func(name string) {
|
|
node := &types.ServerV2{
|
|
Kind: types.KindNode,
|
|
Version: types.V2,
|
|
Metadata: types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
}
|
|
_, err := a.UpsertNode(context.Background(), node)
|
|
require.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
role: types.RoleProxy,
|
|
upserter: func(name string) {
|
|
proxy := &types.ServerV2{
|
|
Kind: types.KindProxy,
|
|
Version: types.V2,
|
|
Metadata: types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
}
|
|
err := a.UpsertProxy(proxy)
|
|
require.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
role: types.RoleKube,
|
|
upserter: func(name string) {
|
|
|
|
kube, err := types.NewKubernetesServerV3(
|
|
types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
types.KubernetesServerSpecV3{
|
|
HostID: name,
|
|
Hostname: "test-kuge",
|
|
Cluster: &types.KubernetesClusterV3{
|
|
Metadata: types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = a.UpsertKubernetesServer(context.Background(), kube)
|
|
require.NoError(t, err)
|
|
|
|
},
|
|
},
|
|
{
|
|
role: types.RoleDatabase,
|
|
upserter: func(name string) {
|
|
db, err := types.NewDatabaseServerV3(
|
|
types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
types.DatabaseServerSpecV3{
|
|
HostID: name,
|
|
Hostname: "test-db",
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = a.UpsertDatabaseServer(context.Background(), db)
|
|
require.NoError(t, err)
|
|
},
|
|
},
|
|
{
|
|
role: types.RoleApp,
|
|
upserter: func(name string) {
|
|
app, err := types.NewAppV3(
|
|
types.Metadata{
|
|
Name: "test-app",
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
types.AppSpecV3{
|
|
URI: "https://app.localhost",
|
|
})
|
|
require.NoError(t, err)
|
|
appServer, err := types.NewAppServerV3(
|
|
types.Metadata{
|
|
Name: name,
|
|
Namespace: defaults.Namespace,
|
|
},
|
|
types.AppServerSpecV3{
|
|
HostID: name,
|
|
App: app,
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = a.UpsertApplicationServer(context.Background(), appServer)
|
|
require.NoError(t, err)
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx = context.WithValue(ctx, ec2ClientKey{}, ec2ClientRunning{})
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(string(tc.role), func(t *testing.T) {
|
|
request := types.RegisterUsingTokenRequest{
|
|
Token: "test_token",
|
|
NodeName: "node_name",
|
|
Role: tc.role,
|
|
HostID: instance1.account + "-" + instance1.instanceID,
|
|
EC2IdentityDocument: instance1.iid,
|
|
PublicSSHKey: sshPublicKey,
|
|
PublicTLSKey: tlsPublicKey,
|
|
}
|
|
|
|
// request works with no existing host
|
|
_, err = a.RegisterUsingToken(ctx, &request)
|
|
require.NoError(t, err)
|
|
|
|
// add the server
|
|
name := instance1.account + "-" + instance1.instanceID
|
|
tc.upserter(name)
|
|
|
|
// request should fail
|
|
_, err = a.RegisterUsingToken(ctx, &request)
|
|
expectedErr := &trace.AccessDeniedError{}
|
|
require.ErrorAs(t, err, &expectedErr)
|
|
})
|
|
}
|
|
|
|
}
|