mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
96 lines
3.7 KiB
Go
96 lines
3.7 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
|
"github.com/aws/aws-sdk-go/service/iam"
|
|
"github.com/aws/aws-sdk-go/service/sts"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
// assumedRoleTrustPolicy lets any principal assume the role, so the test
|
|
// isolates policy attachment as the sole variable under test.
|
|
assumedRoleTrustPolicy = `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"sts:AssumeRole"}]}`
|
|
|
|
// A single-action grant, so the positive case proves a specific managed
|
|
// policy was resolved and evaluated - not a blanket allow.
|
|
assumedRoleDescribeRegionsPolicy = `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"ec2:DescribeRegions","Resource":"*"}]}`
|
|
)
|
|
|
|
// requireAWSErrorCode fails t unless err is an awserr.Error carrying code.
|
|
// Shared by every allow/deny authz assertion in this tier.
|
|
func requireAWSErrorCode(t *testing.T, err error, code string) {
|
|
t.Helper()
|
|
require.Error(t, err)
|
|
var awsErr awserr.Error
|
|
require.ErrorAs(t, err, &awsErr, "expected awserr.Error, got %T: %v", err, err)
|
|
assert.Equal(t, code, awsErr.Code())
|
|
}
|
|
|
|
// requireAWSErrorCodeStatus additionally pins the HTTP status, so a code that
|
|
// AWS documents as a client or server error keeps its documented status class.
|
|
func requireAWSErrorCodeStatus(t *testing.T, err error, code string, status int) {
|
|
t.Helper()
|
|
requireAWSErrorCode(t, err, code)
|
|
var reqErr awserr.RequestFailure
|
|
require.ErrorAs(t, err, &reqErr, "expected awserr.RequestFailure, got %T: %v", err, err)
|
|
assert.Equal(t, status, reqErr.StatusCode())
|
|
}
|
|
|
|
// TestAssumedRoleControlPlaneEnforcement proves the gateway evaluates
|
|
// assumed-role sessions against managed policies: a zero-policy role is
|
|
// denied a guarded action, and the same live session is allowed once a
|
|
// policy is attached to the role (policies resolved per request).
|
|
// ec2:DescribeRegions is dispatched entirely inside the gateway
|
|
// (gateway/ec2.go ec2LocalActions) so this exercises only gateway-side authz,
|
|
// with no NATS stub involved.
|
|
func TestAssumedRoleControlPlaneEnforcement(t *testing.T) {
|
|
gw := StartGateway(t)
|
|
|
|
roleOut, err := gw.IAMClient(t).CreateRole(&iam.CreateRoleInput{
|
|
RoleName: aws.String("are-role"),
|
|
AssumeRolePolicyDocument: aws.String(assumedRoleTrustPolicy),
|
|
})
|
|
require.NoError(t, err, "create-role")
|
|
|
|
assumeOut, err := gw.STSClient(t).AssumeRole(&sts.AssumeRoleInput{
|
|
RoleArn: roleOut.Role.Arn,
|
|
RoleSessionName: aws.String("are-session"),
|
|
})
|
|
require.NoError(t, err, "assume-role")
|
|
creds := assumeOut.Credentials
|
|
require.NotNil(t, creds, "AssumeRole returned nil Credentials")
|
|
|
|
sessionCli := gw.ClientsWithSessionCreds(t,
|
|
aws.StringValue(creds.AccessKeyId),
|
|
aws.StringValue(creds.SecretAccessKey),
|
|
aws.StringValue(creds.SessionToken))
|
|
|
|
// Zero-policy role: the session authenticates, then default-denies.
|
|
_, err = sessionCli.EC2.DescribeRegions(&ec2.DescribeRegionsInput{})
|
|
requireAWSErrorCode(t, err, "UnauthorizedOperation")
|
|
|
|
policyOut, err := gw.IAMClient(t).CreatePolicy(&iam.CreatePolicyInput{
|
|
PolicyName: aws.String("are-ec2-describe-regions"),
|
|
PolicyDocument: aws.String(assumedRoleDescribeRegionsPolicy),
|
|
})
|
|
require.NoError(t, err, "create-policy")
|
|
|
|
_, err = gw.IAMClient(t).AttachRolePolicy(&iam.AttachRolePolicyInput{
|
|
RoleName: roleOut.Role.RoleName,
|
|
PolicyArn: policyOut.Policy.Arn,
|
|
})
|
|
require.NoError(t, err, "attach-role-policy")
|
|
|
|
// Same live session credentials are now allowed - the role grants it.
|
|
_, err = sessionCli.EC2.DescribeRegions(&ec2.DescribeRegionsInput{})
|
|
require.NoError(t, err, "describe-regions must be allowed once the role grants it")
|
|
}
|