mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
Allow requesting a join token with IAM method from the web api (#11339)
Co-authored-by: Jim Bishopp <jamesbishopp@gmail.com> Co-authored-by: Nic Klaassen <nic@goteleport.com>
This commit is contained in:
co-authored by
Jim Bishopp
Nic Klaassen
parent
2eca7c70f4
commit
57cc2ed355
@@ -87,7 +87,7 @@ func NewProvisionTokenFromSpec(token string, expires time.Time, spec ProvisionTo
|
||||
}
|
||||
|
||||
// MustCreateProvisionToken returns a new valid provision token
|
||||
// or panics, used in testes
|
||||
// or panics, used in tests
|
||||
func MustCreateProvisionToken(token string, roles SystemRoles, expires time.Time) ProvisionToken {
|
||||
t, err := NewProvisionToken(token, roles, expires)
|
||||
if err != nil {
|
||||
|
||||
@@ -45,7 +45,7 @@ type Provisioner interface {
|
||||
}
|
||||
|
||||
// MustCreateProvisionToken returns a new valid provision token
|
||||
// or panics, used in testes
|
||||
// or panics, used in tests
|
||||
func MustCreateProvisionToken(token string, roles types.SystemRoles, expires time.Time) types.ProvisionToken {
|
||||
t, err := types.NewProvisionToken(token, roles, expires)
|
||||
if err != nil {
|
||||
|
||||
@@ -2008,9 +2008,11 @@ func (s *WebSuite) TestGetClusterDetails(c *C) {
|
||||
|
||||
func TestTokenGeneration(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
roles types.SystemRoles
|
||||
shouldErr bool
|
||||
name string
|
||||
roles types.SystemRoles
|
||||
shouldErr bool
|
||||
joinMethod types.JoinMethod
|
||||
allow []*types.TokenRule
|
||||
}{
|
||||
{
|
||||
name: "single node role",
|
||||
@@ -2037,6 +2039,19 @@ func TestTokenGeneration(t *testing.T) {
|
||||
roles: types.SystemRoles{},
|
||||
shouldErr: true,
|
||||
},
|
||||
{
|
||||
name: "cannot request token with IAM join method without allow field",
|
||||
roles: types.SystemRoles{types.RoleNode},
|
||||
joinMethod: types.JoinMethodIAM,
|
||||
shouldErr: true,
|
||||
},
|
||||
{
|
||||
name: "can request token with IAM join method",
|
||||
roles: types.SystemRoles{types.RoleNode},
|
||||
joinMethod: types.JoinMethodIAM,
|
||||
allow: []*types.TokenRule{{AWSAccount: "1234"}},
|
||||
shouldErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
@@ -2047,8 +2062,10 @@ func TestTokenGeneration(t *testing.T) {
|
||||
pack := proxy.authPack(t, "test-user@example.com")
|
||||
|
||||
endpoint := pack.clt.Endpoint("webapi", "token")
|
||||
re, err := pack.clt.PostJSON(context.Background(), endpoint, createTokenRequest{
|
||||
Roles: tc.roles,
|
||||
re, err := pack.clt.PostJSON(context.Background(), endpoint, types.ProvisionTokenSpecV2{
|
||||
Roles: tc.roles,
|
||||
JoinMethod: tc.joinMethod,
|
||||
Allow: tc.allow,
|
||||
})
|
||||
|
||||
if tc.shouldErr {
|
||||
@@ -2066,6 +2083,13 @@ func TestTokenGeneration(t *testing.T) {
|
||||
generatedToken, err := proxy.auth.Auth().GetToken(context.Background(), responseToken.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.roles, generatedToken.GetRoles())
|
||||
|
||||
expectedJoinMethod := tc.joinMethod
|
||||
if tc.joinMethod == "" {
|
||||
expectedJoinMethod = types.JoinMethodToken
|
||||
}
|
||||
// if no joinMethod is provided, expect token method
|
||||
require.Equal(t, expectedJoinMethod, generatedToken.GetJoinMethod())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+120
-20
@@ -21,9 +21,12 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -46,6 +49,8 @@ type nodeJoinToken struct {
|
||||
ID string `json:"id"`
|
||||
// Expiry is token expiration time.
|
||||
Expiry time.Time `json:"expiry,omitempty"`
|
||||
// Method is the join method that the token supports
|
||||
Method types.JoinMethod `json:"method"`
|
||||
}
|
||||
|
||||
// scriptSettings is used to hold values which are passed into the function that
|
||||
@@ -55,28 +60,75 @@ type scriptSettings struct {
|
||||
appInstallMode bool
|
||||
appName string
|
||||
appURI string
|
||||
}
|
||||
|
||||
// createTokenRequest is the expected request body of
|
||||
// the endpoint to create token
|
||||
type createTokenRequest struct {
|
||||
Roles types.SystemRoles `json:"roles"`
|
||||
joinMethod string
|
||||
}
|
||||
|
||||
func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
||||
var req createTokenRequest
|
||||
var req types.ProvisionTokenSpecV2
|
||||
if err := httplib.ReadJSON(r, &req); err != nil {
|
||||
log.WithError(err).Error("error reading body")
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
clt, err := ctx.GetClient()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error getting client")
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return createJoinToken(r.Context(), clt, req.Roles)
|
||||
var expires time.Time
|
||||
var tokenName string
|
||||
switch req.JoinMethod {
|
||||
case types.JoinMethodIAM:
|
||||
// to prevent generation of redundant IAM tokens
|
||||
// we generate a deterministic name for them
|
||||
tokenName, err = generateIAMTokenName(req.Allow)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
// if a token with this name is found and it has indeed the same rule set,
|
||||
// return it. Otherwise, go ahead and create it
|
||||
t, err := clt.GetToken(r.Context(), tokenName)
|
||||
if err != nil && !trace.IsNotFound(err) {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// check if the token found has the right rules
|
||||
if t.GetJoinMethod() != types.JoinMethodIAM || !isSameRuleSet(req.Allow, t.GetAllowRules()) {
|
||||
return nil, trace.BadParameter("failed to create token: token with name %q already exists and does not have the expected allow rules", tokenName)
|
||||
}
|
||||
|
||||
return &nodeJoinToken{
|
||||
ID: t.GetName(),
|
||||
Expiry: *t.GetMetadata().Expires,
|
||||
Method: t.GetJoinMethod(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IAM tokens should 'never' expire
|
||||
expires = time.Now().UTC().AddDate(1000, 0, 0)
|
||||
default:
|
||||
tokenName, err = utils.CryptoRandomHex(auth.TokenLenBytes)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
expires = time.Now().UTC().Add(defaults.NodeJoinTokenTTL)
|
||||
}
|
||||
|
||||
provisionToken, err := types.NewProvisionTokenFromSpec(tokenName, expires, req)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = clt.UpsertToken(r.Context(), provisionToken)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &nodeJoinToken{
|
||||
ID: tokenName,
|
||||
Expiry: expires,
|
||||
Method: provisionToken.GetJoinMethod(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *Handler) createNodeTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
||||
@@ -99,6 +151,7 @@ func (h *Handler) getNodeJoinScriptHandle(w http.ResponseWriter, r *http.Request
|
||||
settings := scriptSettings{
|
||||
token: params.ByName("token"),
|
||||
appInstallMode: false,
|
||||
joinMethod: r.URL.Query().Get("method"),
|
||||
}
|
||||
|
||||
script, err := getJoinScript(settings, h.GetProxyClient())
|
||||
@@ -176,17 +229,20 @@ func createJoinToken(ctx context.Context, m nodeAPIGetter, roles types.SystemRol
|
||||
}
|
||||
|
||||
func getJoinScript(settings scriptSettings, m nodeAPIGetter) (string, error) {
|
||||
// This token does not need to be validated against the backend because it's not used to
|
||||
// reveal any sensitive information. However, we still need to perform a simple input
|
||||
// validation check by verifying that the token was auto-generated.
|
||||
// Auto-generated tokens must be encoded and must have an expected length.
|
||||
decodedToken, err := hex.DecodeString(settings.token)
|
||||
if err != nil {
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
// Skip decoding validation for IAM tokens since they are generated with a different method
|
||||
if settings.joinMethod != string(types.JoinMethodIAM) {
|
||||
// This token does not need to be validated against the backend because it's not used to
|
||||
// reveal any sensitive information. However, we still need to perform a simple input
|
||||
// validation check by verifying that the token was auto-generated.
|
||||
// Auto-generated tokens must be encoded and must have an expected length.
|
||||
decodedToken, err := hex.DecodeString(settings.token)
|
||||
if err != nil {
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
|
||||
if len(decodedToken) != auth.TokenLenBytes {
|
||||
return "", trace.BadParameter("invalid token length")
|
||||
if len(decodedToken) != auth.TokenLenBytes {
|
||||
return "", trace.BadParameter("invalid token length")
|
||||
}
|
||||
}
|
||||
|
||||
// Get hostname and port from proxy server address.
|
||||
@@ -237,6 +293,7 @@ func getJoinScript(settings scriptSettings, m nodeAPIGetter) (string, error) {
|
||||
"appInstallMode": strconv.FormatBool(settings.appInstallMode),
|
||||
"appName": settings.appName,
|
||||
"appURI": settings.appURI,
|
||||
"joinMethod": settings.joinMethod,
|
||||
})
|
||||
if err != nil {
|
||||
return "", trace.Wrap(err)
|
||||
@@ -245,6 +302,49 @@ func getJoinScript(settings scriptSettings, m nodeAPIGetter) (string, error) {
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// generateIAMTokenName makes a deterministic name for a iam join token
|
||||
// based on its rule set
|
||||
func generateIAMTokenName(rules []*types.TokenRule) (string, error) {
|
||||
// sort the rules by (account ID, arn)
|
||||
// to make sure a set of rules will produce the same hash,
|
||||
// no matter the order they are in the slice
|
||||
orderedRules := make([]*types.TokenRule, len(rules))
|
||||
copy(orderedRules, rules)
|
||||
sortRules(orderedRules)
|
||||
|
||||
h := fnv.New32a()
|
||||
for _, r := range orderedRules {
|
||||
s := fmt.Sprintf("%s%s", r.AWSAccount, r.AWSARN)
|
||||
_, err := h.Write([]byte(s))
|
||||
if err != nil {
|
||||
return "", trace.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("teleport-ui-iam-%d", h.Sum32()), nil
|
||||
}
|
||||
|
||||
// sortRules sorts a slice of rules based on their AWS Account ID and ARN
|
||||
func sortRules(rules []*types.TokenRule) {
|
||||
sort.Slice(rules, func(i, j int) bool {
|
||||
iAcct, jAcct := rules[i].AWSAccount, rules[j].AWSAccount
|
||||
// if accountID is the same, sort based on arn
|
||||
if iAcct == jAcct {
|
||||
arn1, arn2 := rules[i].AWSARN, rules[j].AWSARN
|
||||
return arn1 < arn2
|
||||
}
|
||||
|
||||
return iAcct < jAcct
|
||||
})
|
||||
}
|
||||
|
||||
// isSameRuleSet check if r1 and r2 are the same rules, ignoring the order
|
||||
func isSameRuleSet(r1 []*types.TokenRule, r2 []*types.TokenRule) bool {
|
||||
sortRules(r1)
|
||||
sortRules(r2)
|
||||
return reflect.DeepEqual(r1, r2)
|
||||
}
|
||||
|
||||
type nodeAPIGetter interface {
|
||||
// GenerateToken creates a special provisioning token for a new SSH server
|
||||
// that is valid for ttl period seconds.
|
||||
|
||||
@@ -46,6 +46,237 @@ func TestCreateNodeJoinToken(t *testing.T) {
|
||||
require.Equal(t, "some-token-id", token.ID)
|
||||
}
|
||||
|
||||
func TestGenerateIAMTokenName(t *testing.T) {
|
||||
rule1 := types.TokenRule{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:1",
|
||||
}
|
||||
|
||||
rule1Name := "teleport-ui-iam-2218897454"
|
||||
|
||||
// make sure the hash algorithm don't change accidentally
|
||||
hash1, err := generateIAMTokenName([]*types.TokenRule{&rule1})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rule1Name, hash1)
|
||||
|
||||
rule2 := types.TokenRule{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
}
|
||||
|
||||
// make sure the order doesn't matter
|
||||
hash1, err = generateIAMTokenName([]*types.TokenRule{&rule1, &rule2})
|
||||
require.NoError(t, err)
|
||||
|
||||
hash2, err := generateIAMTokenName([]*types.TokenRule{&rule2, &rule1})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, hash1, hash2)
|
||||
|
||||
// generate different hashes for different rules
|
||||
hash1, err = generateIAMTokenName([]*types.TokenRule{&rule1})
|
||||
require.NoError(t, err)
|
||||
|
||||
hash2, err = generateIAMTokenName([]*types.TokenRule{&rule2})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotEqual(t, hash1, hash2)
|
||||
}
|
||||
|
||||
func TestSortRules(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
rules []*types.TokenRule
|
||||
expected []*types.TokenRule
|
||||
}{
|
||||
{
|
||||
name: "different account ID, no ARN",
|
||||
rules: []*types.TokenRule{
|
||||
{AWSAccount: "200000000000"},
|
||||
{AWSAccount: "100000000000"},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{AWSAccount: "100000000000"},
|
||||
{AWSAccount: "200000000000"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different account ID, no ARN, already ordered",
|
||||
rules: []*types.TokenRule{
|
||||
{AWSAccount: "100000000000"},
|
||||
{AWSAccount: "200000000000"},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{AWSAccount: "100000000000"},
|
||||
{AWSAccount: "200000000000"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different account ID, with ARN",
|
||||
rules: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different account ID, with ARN, already ordered",
|
||||
rules: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same account ID, different ARN, already ordered",
|
||||
rules: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same account ID, different ARN",
|
||||
rules: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple account ID and ARNs",
|
||||
rules: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000001",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
|
||||
{
|
||||
AWSAccount: "200000000001",
|
||||
AWSARN: "arn:aws:iam:z",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "300000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
},
|
||||
expected: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "100000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000000",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000001",
|
||||
AWSARN: "arn:aws:iam:b",
|
||||
},
|
||||
{
|
||||
AWSAccount: "200000000001",
|
||||
AWSARN: "arn:aws:iam:z",
|
||||
},
|
||||
{
|
||||
AWSAccount: "300000000000",
|
||||
AWSARN: "arn:aws:iam:a",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
sortRules(tc.rules)
|
||||
require.Equal(t, tc.expected, tc.rules)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNodeJoinScript(t *testing.T) {
|
||||
m := &mockedNodeAPIGetter{}
|
||||
m.mockGetProxyServers = func() ([]types.Server, error) {
|
||||
@@ -94,6 +325,17 @@ func TestGetNodeJoinScript(t *testing.T) {
|
||||
require.Contains(t, script, "test-host")
|
||||
require.Contains(t, script, "12345678")
|
||||
require.Contains(t, script, "sha256:")
|
||||
require.NotContains(t, script, "JOIN_METHOD=\"iam\"")
|
||||
|
||||
// Test iam method script
|
||||
iamToken := scriptSettings{
|
||||
token: "token length doesnt matter in this case",
|
||||
joinMethod: string(types.JoinMethodIAM),
|
||||
}
|
||||
|
||||
script, err = getJoinScript(iamToken, m)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, script, "JOIN_METHOD=\"iam\"")
|
||||
}
|
||||
|
||||
func TestGetAppJoinScript(t *testing.T) {
|
||||
@@ -272,6 +514,110 @@ func TestGetAppJoinScript(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSameRuleSet(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
r1 []*types.TokenRule
|
||||
r2 []*types.TokenRule
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "empty slice",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "simple identical rules",
|
||||
r1: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
},
|
||||
r2: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "different rules",
|
||||
r1: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
},
|
||||
r2: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "111111111111",
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "same rules in different order",
|
||||
r1: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
{
|
||||
AWSAccount: "222222222222",
|
||||
},
|
||||
{
|
||||
AWSAccount: "111111111111",
|
||||
AWSARN: "arn:*",
|
||||
},
|
||||
},
|
||||
r2: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "222222222222",
|
||||
},
|
||||
{
|
||||
AWSAccount: "111111111111",
|
||||
AWSARN: "arn:*",
|
||||
},
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "almost the same rules",
|
||||
r1: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
{
|
||||
AWSAccount: "222222222222",
|
||||
},
|
||||
{
|
||||
AWSAccount: "111111111111",
|
||||
AWSARN: "arn:*",
|
||||
},
|
||||
},
|
||||
r2: []*types.TokenRule{
|
||||
{
|
||||
AWSAccount: "123123123123",
|
||||
},
|
||||
{
|
||||
AWSAccount: "222222222222",
|
||||
},
|
||||
{
|
||||
AWSAccount: "111111111111",
|
||||
AWSARN: "arn:",
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require.Equal(t, tc.expected, isSameRuleSet(tc.r1, tc.r2))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockedNodeAPIGetter struct {
|
||||
mockGenerateToken func(ctx context.Context, req auth.GenerateTokenRequest) (string, error)
|
||||
mockGetProxyServers func() ([]types.Server, error)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Any changes made to this script must also be made to e/lib/web/scripts/install_node.go
|
||||
set -euo pipefail
|
||||
SCRIPT_NAME="teleport-installer"
|
||||
|
||||
@@ -40,6 +39,7 @@ TELEPORT_VERSION="{{.version}}"
|
||||
TARGET_HOSTNAME="{{.hostname}}"
|
||||
TARGET_PORT="{{.port}}"
|
||||
JOIN_TOKEN="{{.token}}"
|
||||
JOIN_METHOD="{{.joinMethod}}"
|
||||
CA_PIN_HASHES="{{.caPins}}"
|
||||
ARG_CA_PIN_HASHES=""
|
||||
APP_INSTALL_MODE="{{.appInstallMode}}"
|
||||
@@ -431,10 +431,11 @@ EOF
|
||||
install_teleport_node_config() {
|
||||
log "Writing Teleport node service config to ${TELEPORT_CONFIG_PATH}"
|
||||
CA_PINS_CONFIG=$(get_yaml_list "ca_pin" "${CA_PIN_HASHES}" " ")
|
||||
AUTH_CONFIG=$(get_node_auth_config)
|
||||
cat << EOF > ${TELEPORT_CONFIG_PATH}
|
||||
teleport:
|
||||
nodename: ${NODENAME}
|
||||
auth_token: ${JOIN_TOKEN}
|
||||
${AUTH_CONFIG}
|
||||
${CA_PINS_CONFIG}
|
||||
auth_servers:
|
||||
- ${TARGET_HOSTNAME}:${TARGET_PORT}
|
||||
@@ -449,6 +450,16 @@ proxy_service:
|
||||
enabled: no
|
||||
EOF
|
||||
}
|
||||
# get the auth section of a node config
|
||||
get_node_auth_config() {
|
||||
if [[ ${JOIN_METHOD} == "iam" ]]; then
|
||||
echo "join_params:
|
||||
token_name: ${JOIN_TOKEN}
|
||||
method: iam
|
||||
";
|
||||
else
|
||||
echo "auth_token: ${JOIN_TOKEN}"; fi
|
||||
}
|
||||
# checks whether the given host is running MacOS
|
||||
is_macos_host() { if [[ ${OSTYPE} == "darwin"* ]]; then return 0; else return 1; fi }
|
||||
# checks whether teleport is already running on the host
|
||||
|
||||
Reference in New Issue
Block a user