diff --git a/modules/managed-cloud/README.md b/modules/managed-cloud/README.md index 38c2d22..8d21c1e 100644 --- a/modules/managed-cloud/README.md +++ b/modules/managed-cloud/README.md @@ -1,3 +1,37 @@ +# StreamNative Managed Cloud +This Terraform module creates the resources necessary for StreamNative's vendor access into your AWS environment. + +There are three main resources it creates: + +- [Permission Boundary Policy](https://github.com/streamnative/terraform-aws-cloud/blob/master/modules/managed-cloud/files/permission_boundary_iam_policy.json.tpl): This permission boundary defines the scope of exactly what is possible for StreamNative to do within your AWS account. It is self enforcing with strict requirements, to ensure that points of vulnerability (such as privledge escalation) are locked down and not possible. + +- Management role: This AWS IAM role is used for the day to day management of resources strictly owned by StreamNative. It is limited in its ability to create, modify, and delete resources within AWS. + +- Bootstrap role (temporary/optional): This AWS IAM role is typically only needed for initial provisioning or deprovisioning. It has the ability to create and delete (within the limits of the permission boundary) EC2, EKS, IAM, DynamoDB, Route53, and KMS resources. + +## Usage + +The module only requires two inputs: + +- `region`: The AWS region where your StreamNative Managed environment is running (this is needed to restrict access to manage certain AWS resources to a particular region) +- `streamnative_vendor_access_role_arn`: The ARN for the support role given to you by StreamNative. This is specific to you as a customer, and is the identity we will use when assuming the designated IAM roles in your account. It has a trust relationship specific to the AWS account you've designated for StreamNative's access. + +Assuming you are authenticated and authorized to the correct AWS environment, create a `main.tf` file containing the following: + +```hcl +module "sn_managed_cloud" { + source = "streamnative/cloud/aws//modules/managed-cloud" + + region = + streamnative_vendor_access_role_arn = +} +``` + +And then run `terraform init && terraform apply` accordingly. + +## CloudFormation (optional) +If you do not use Terraform or prefer a more AWS native approach to deploying these resources, the [`cloudformation`](https://github.com/streamnative/terraform-aws-cloud/tree/master/modules/managed-cloud/cloudformation) directory contains a stack template file you can use. It creates the same resources mentioned above, just upload the stack and provide the necessary `VendorSupportRoleArn` parameter. + ## Requirements | Name | Version | @@ -10,7 +44,6 @@ | Name | Version | |------|---------| | [aws](#provider\_aws) | >= 3.61.0 | -| [template](#provider\_template) | n/a | ## Modules @@ -28,10 +61,8 @@ No modules. | [aws_iam_role_policy_attachment.bootstrap_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.management_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.streamnative_control_plane_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.streamnative_vendor_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [template_file.bootstrap_role](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) | data source | -| [template_file.management_role](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) | data source | -| [template_file.permission_boundary](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) | data source | ## Inputs @@ -39,6 +70,8 @@ No modules. |------|-------------|------|---------|:--------:| | [create\_bootstrap\_role](#input\_create\_bootstrap\_role) | Whether or not to create the bootstrap role, which is used by StreamNative for the initial deployment of the StreamNative Cloud | `string` | `true` | no | | [region](#input\_region) | The AWS region where your instance of StreamNative Cloud is deployed, i.e. "us-west-2" | `string` | n/a | yes | +| [streamnative\_control\_plane\_role\_arn](#input\_streamnative\_control\_plane\_role\_arn) | The ARN of the role that is used by StreamNative for Control Plane operations | `string` | `"arn:aws:iam::311022431024:role/cloud-manager"` | no | +| [streamnative\_google\_account\_id](#input\_streamnative\_google\_account\_id) | The Google Cloud service account ID used by StreamNative for Control Plane operations | `string` | `"108050666045451143798"` | no | | [streamnative\_vendor\_access\_role\_arn](#input\_streamnative\_vendor\_access\_role\_arn) | The arn for the IAM principle (role) provided by StreamNative. This role is used exclusively by StreamNative (with strict permissions) for vendor access into your AWS account | `string` | n/a | yes | | [tags](#input\_tags) | Extra tags to apply to the resources created by this module. | `map(string)` | `{}` | no | diff --git a/modules/managed-cloud/cloudformation/managed_cloud.yaml b/modules/managed-cloud/cloudformation/managed_cloud.yaml new file mode 100644 index 0000000..f33aa35 --- /dev/null +++ b/modules/managed-cloud/cloudformation/managed_cloud.yaml @@ -0,0 +1,639 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Creates IAM resources used for StreamNative's vendor access" + +Parameters: + VendorSupportRoleArn: + Description: "The IAM Role ARN used by StreamNative for vendor access into your AWS account. This is provided to you by StreamNative." + Type: String + Default: "arn:aws:iam::311022431024:role/cloud-manager" + ControlPlaneSAID: + Description: "The SA ID used by the StreamNative Cloud Control Plane" + Type: String + Default: "108050666045451143798" + +Resources: + BootstrapRole: + Type: "AWS::IAM::Role" + Properties: + Path: "/StreamNative/" + RoleName: "StreamNativeCloudBootstrapRole" + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: AllowStreamNativeVendorAccess + Effect: Allow + Principal: + AWS: + - !Ref VendorSupportRoleArn + Action: sts:AssumeRole + MaxSessionDuration: 3600 + PermissionsBoundary: !Ref PermissionBoundaryPolicy + ManagedPolicyArns: + - !Ref BootstrapPolicy + Description: "This role is used to bootstrap the StreamNative Cloud within the AWS account. It is limited in scope to the attached policy and also the permission boundary." + Tags: + - + Key: "Vendor" + Value: "StreamNative" + + ManagementRole: + Type: "AWS::IAM::Role" + Properties: + Path: "/StreamNative/" + RoleName: "StreamNativeCloudManagementRole" + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: AllowStreamNativeVendorAccess + Effect: Allow + Principal: + AWS: + - !Ref VendorSupportRoleArn + Action: sts:AssumeRole + - Sid: AllowStreamNativeControlPlaneAccess + Effect: Allow + Principal: + Federated: accounts.google.com + Action: sts:AssumeRoleWithWebIdentity + Condition: + StringEquals: + accounts.google.com:aud: !Ref ControlPlaneSAID + MaxSessionDuration: 3600 + PermissionsBoundary: !Ref PermissionBoundaryPolicy + ManagedPolicyArns: + - !Ref ManagementPolicy + Description: "This role is used by StreamNative for the day to day management of the StreamNative Cloud deployment." + Tags: + - + Key: "Vendor" + Value: "StreamNative" + + PermissionBoundaryPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: "StreamNativeCloudPermissionBoundary" + Path: "/StreamNative/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowedServices", + "Effect": "Allow", + "Action": [ + "acm:*", + "autoscaling:*", + "cognito-idp:*", + "dynamodb:*", + "ec2:*", + "ecr:*", + "eks:*", + "elasticloadbalancing:*", + "iam:GetInstanceProfile", + "iam:GetOpenIDConnectProvider", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:GetRole", + "iam:GetServerCertificate", + "iam:ListAttachedRolePolicies", + "iam:ListEntitiesForPolicy", + "iam:ListInstanceProfile*", + "iam:ListOpenIDConnectProvider*", + "iam:ListPolicies", + "iam:ListPolicyTags", + "iam:ListPolicyVersions", + "iam:ListRole*", + "iam:ListServerCertificates", + "kms:*", + "logs:*", + "route53:*", + "s3:*", + "shield:*", + "sts:*", + "waf-regional:*", + "wafv2:*" + ], + "Resource": "*" + }, + { + "Sid": "IamRestrictions", + "Effect": "Allow", + "Action": [ + "iam:AddRoleToInstanceProfile", + "iam:AttachRolePolicy", + "iam:CreateOpenIDConnectProvider", + "iam:CreatePolicy", + "iam:CreatePolicyVersion", + "iam:CreateRole", + "iam:CreateServiceLinkedRole", + "iam:DeleteInstanceProfile", + "iam:DeleteOpenIDConnectProvider", + "iam:DeletePolicy", + "iam:DeletePolicyVersion", + "iam:DeleteRole", + "iam:DeleteServiceLinkedRole", + "iam:DetachRolePolicy", + "iam:PassRole", + "iam:PutRolePermissionsBoundary", + "iam:RemoveRoleFromInstanceProfile", + "iam:SetDefaultPolicyVersion", + "iam:TagInstanceProfile", + "iam:TagOpenIDConnectProvider", + "iam:TagPolicy", + "iam:TagRole", + "iam:UpdateAssumeRolePolicy", + "iam:UpdateOpenIDConnectProviderThumbprint", + "iam:UpdateRole", + "iam:UpdateRoleDescription" + ], + "Resource": [ + "arn:aws:iam::aws:policy/*", + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:oidc-provider/*", + "arn:aws:iam::${AWS::AccountId}:instance-profile/*", + "arn:aws:iam::${AWS::AccountId}:server-certificate/*" + ] + }, + { + "Sid": "RestrictPassRoleToEKS", + "Effect": "Allow", + "Action": [ + "iam:PassRole" + ], + "Resource": "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "Condition": { + "StringEquals": { + "iam:PassedToService": "eks.amazonaws.com" + } + } + }, + { + "Sid": "AllowedIAMManagedPolicies", + "Effect": "Deny", + "Action": [ + "iam:AttachRolePolicy" + ], + "Resource": "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "Condition": { + "ArnNotLike": { + "iam:PolicyARN": [ + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-AWSLoadBalancerControllerPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-CertManagerPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-ClusterAutoscalerPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-CsiPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-ExternalDnsPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-ExternalSecretsPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-TieredStoragePolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-VaultPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-VeleroBackupPolicy", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-elb-sl-role-*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*-deny-log-group*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/StreamNativeCloudPermissionBoundary", + "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", + "arn:aws:iam::aws:policy/AmazonEKSServicePolicy", + "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController", + "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", + "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", + "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + ] + } + } + }, + { + "Sid": "RequirePermissionBoundaryForIamRoles", + "Effect": "Allow", + "Action": [ + "iam:CreateRole" + ], + "Resource": "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "Condition": { + "StringEqualsIgnoreCase": { + "aws:ResourceTag/Vendor": "StreamNative", + "iam:PermissionsBoundary": "arn:aws:iam:::policy/StreamNative/StreamNativeCloudPermissionBoundary" + } + } + }, + { + "Sid": "RestrictChangesToVendorAccess", + "Effect": "Deny", + "Action": [ + "iam:Attach", + "iam:Create*", + "iam:Delete*", + "iam:Put", + "iam:Tag*", + "iam:Untag*", + "iam:Update*", + "iam:Set*" + ], + "Resource": [ + "arn:aws:iam:::policy/StreamNative/StreamNativeCloudPermissionBoundary", + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/StreamNativeBootstrapRole", + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/StreamNativeManagementRole" + ] + } + ] + } + + BootstrapPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: "StreamNativeCloudBootstrapPolicy" + Path: "/StreamNative/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "UnrestrictedServiceAccess", + "Effect": "Allow", + "Action": [ + "acm:ListCertificates", + "acm:ListTagsForCertificate", + "autoscaling:Describe*", + "dynamodb:ListBackups", + "dynamodb:ListGlobalTables", + "dynamodb:ListTables", + "dynamodb:ListTagsOfResource", + "ec2:AuthorizeSecurityGroup*", + "ec2:Describe*", + "ec2:Get*", + "ec2:RevokeSecurityGroup*", + "ec2:RunInstances", + "eks:Describe*", + "eks:List*", + "iam:AttachRolePolicy", + "iam:GetInstanceProfile", + "iam:GetOpenIDConnectProvider", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:GetRole", + "iam:ListAttachedRolePolicies", + "iam:ListEntitiesForPolicy", + "iam:ListInstanceProfile*", + "iam:ListOpenIDConnectProvider*", + "iam:ListPolicies", + "iam:ListPolicyTags", + "iam:ListPolicyVersions", + "iam:ListRole*", + "kms:CreateAlias", + "kms:CreateKey", + "kms:DeleteAlias", + "kms:DescribeKey", + "kms:GetKeyPolicy", + "kms:GetKeyRotationStatus", + "kms:ListAliases", + "kms:ListResourceTags", + "kms:ScheduleKeyDeletion", + "kms:TagResource", + "logs:CreateLogGroup", + "logs:DescribeLogGroups", + "logs:ListTagsLogGroup", + "route53:GetHostedZone", + "route53:ListHostedZones", + "s3:ListAllMyBuckets", + "s3:ListBucket" + ], + "Resource": "*" + }, + { + "Sid": "ResourceBasedRestictions", + "Effect": "Allow", + "Action": [ + "eks:DeleteNodeGroup", + "iam:CreatePolicy", + "iam:CreatePolicyVersion", + "iam:DeletePolicy", + "iam:DeletePolicyVersion" + ], + "Resource": [ + "arn:aws:eks:${AWS::Region}:${AWS::AccountId}:nodegroup/*/snc-*-pool*/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*" + ] + }, + { + "Sid": "RequireAWSRequestTag", + "Effect": "Allow", + "Action": [ + "acm:AddTagsToCertificate", + "acm:ImportCertificate", + "acm:RemoveTagsFromCertificate", + "acm:RequestCertificate", + "autoscaling:CreateAutoScalingGroup", + "autoscaling:CreateLaunchConfiguration", + "autoscaling:CreateOrUpdateTags", + "autoscaling:DetachInstances", + "autoscaling:SetDesiredCapacity", + "autoscaling:UpdateAutoScalingGroup", + "autoscaling:SuspendProcesses", + "ec2:AllocateAddress", + "ec2:CreateDhcpOptions", + "ec2:CreateEgressOnlyInternetGateway", + "ec2:CreateInternetGateway", + "ec2:CreateLaunchTemplate", + "ec2:CreateNatGateway", + "ec2:CreateNetworkInterface", + "ec2:CreateRouteTable", + "ec2:CreateSecurityGroup", + "ec2:CreateSubnet", + "ec2:CreateVolume", + "ec2:CreateVpc", + "ec2:CreateVpcEndpoint", + "ec2:CreateTags", + "eks:Create*", + "eks:RegisterCluster", + "eks:TagResource" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "aws:RequestTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "RequireAWSResourceTag", + "Effect": "Allow", + "Action": [ + "acm:DeleteCertificate", + "acm:DescribeCertificate", + "acm:ExportCertificate", + "acm:GetCertificate", + "acm:ImportCertificate", + "acm:RemoveTagsFromCertificate", + "acm:ResendValidationEmail", + "autoscaling:AttachInstances", + "autoscaling:CreateOrUpdateTags", + "autoscaling:Delete*", + "ec2:AssignPrivateIpAddresses", + "ec2:Associate*", + "ec2:AttachInternetGateway", + "ec2:CreateLaunchTemplateVersion", + "ec2:CreateNatGateway", + "ec2:CreateNetworkInterface", + "ec2:CreateRoute", + "ec2:CreateRouteTable", + "ec2:CreateSecurityGroup", + "ec2:CreateSubnet", + "ec2:CreateTags", + "ec2:CreateVpcEndpoint", + "ec2:Delete*", + "ec2:Detach*", + "ec2:Disassociate*", + "ec2:Modify*", + "ec2:Release*", + "ec2:Revoke*", + "ec2:RunInstances", + "ec2:Update*", + "eks:DeleteAddon", + "eks:DeleteCluster", + "eks:DeleteFargateProfile", + "eks:DeregisterCluster", + "eks:DisassociateIdentityProviderConfig", + "eks:U*", + "logs:DeleteLogGroup", + "logs:PutRetentionPolicy" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "aws:ResourceTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "RestrictS3Access", + "Effect": "Allow", + "Action":[ + "s3:CreateBucket", + "s3:DeleteBucket", + "s3:GetAccelerateConfiguration", + "s3:GetAccessPointPolicy", + "s3:GetAccountPublicAccessBlock", + "s3:GetAnalyticsConfiguration", + "s3:GetBucket*", + "s3:GetBucketLocation", + "s3:GetEncryptionConfiguration", + "s3:GetInventoryConfiguration", + "s3:GetLifecycleConfiguration", + "s3:GetMetricsConfiguration", + "s3:GetReplicationConfiguration", + "s3:PutAccelerateConfiguration", + "s3:PutAccessPointPolicy", + "s3:PutAccountPublicAccessBlock", + "s3:PutAnalyticsConfiguration", + "s3:PutBucket*", + "s3:PutEncryptionConfiguration", + "s3:PutInventoryConfiguration", + "s3:PutLifecycleConfiguration", + "s3:PutMetricsConfiguration", + "s3:PutReplicationConfiguration" + ], + "Resource": [ + "arn:aws:s3:::*-storage-offload-*", + "arn:aws:s3:::*-backup-*" + ] + + }, + { + "Sid": "RestrictDynamoAccess", + "Effect": "Allow", + "Action": [ + "dynamodb:*ContinuousBackups", + "dynamodb:CreateBackup", + "dynamodb:CreateGlobalTable", + "dynamodb:CreateTable*", + "dynamodb:Delete*", + "dynamodb:Describe*", + "dynamodb:RestoreTable*", + "dynamodb:TagResource", + "dynamodb:UntagResource", + "dynamodb:Update*" + ], + "Resource": [ + "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*vault-table", + "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:global-table/*vault-table" + ] + }, + { + "Sid": "IamRequireRequestTag", + "Effect": "Allow", + "Action": [ + "iam:CreateRole", + "iam:CreateOpenIDConnectProvider", + "iam:TagPolicy", + "iam:TagRole", + "iam:TagInstanceProfile", + "iam:TagOpenIDConnectProvider" + ], + "Resource": [ + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:oidc-provider/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:RequestTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "IamRequireResourceTag", + "Effect": "Allow", + "Action": [ + "iam:AddRoleToInstanceProfile", + "iam:CreateServiceLinkedRole", + "iam:DeleteInstanceProfile", + "iam:DeleteOpenIDConnectProvider", + "iam:DeleteRole", + "iam:DeleteServiceLinkedRole", + "iam:DetachRolePolicy", + "iam:PutRolePermissionsBoundary", + "iam:RemoveRoleFromInstanceProfile", + "iam:SetDefaultPolicyVersion", + "iam:UpdateAssumeRolePolicy", + "iam:UpdateOpenIDConnectProviderThumbprint", + "iam:UpdateRole", + "iam:UpdateRoleDescription" + ], + "Resource": [ + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:oidc-provider/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:ResourceTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "RestrictPassRoleToEKS", + "Effect": "Allow", + "Action": [ + "iam:PassRole" + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:PassedToService": "eks.amazonaws.com" + } + } + } + ] + } + + ManagementPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: "StreamNativeCloudManagementPolicy" + Path: "/StreamNative/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowedServices", + "Effect": "Allow", + "Action": [ + "cloudwatch:Describe*", + "cloudwatch:List*", + "cloudwatch:Get*", + "logs:Describe*", + "logs:List*", + "logs:Filter*", + "logs:StartQuery", + "logs:StopQuery", + "route53:Get*", + "route53:List*" + ], + "Resource": "*" + }, + { + "Sid": "AllowedIAMReadActions", + "Effect": "Allow", + "Action": [ + "iam:GetPolicy*", + "iam:GetRole*", + "iam:ListRole*", + "iam:ListPolic*" + ], + "Resource": [ + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*", + "arn:aws:iam::aws:policy/*" + ] + }, + { + "Sid": "IamRequireRequestTag", + "Effect": "Allow", + "Action": [ + "iam:CreateRole", + "iam:TagPolicy", + "iam:TagRole" + ], + "Resource": [ + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:RequestTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "IamRequireResourceTag", + "Effect": "Allow", + "Action": [ + "iam:DeleteRole", + "iam:DetachRolePolicy", + "iam:PutRolePermissionsBoundary", + "iam:SetDefaultPolicyVersion", + "iam:UpdateAssumeRolePolicy", + "iam:UpdateRole", + "iam:UpdateRoleDescription" + ], + "Resource": [ + "arn:aws:iam::${AWS::AccountId}:role/StreamNative/*", + "arn:aws:iam::${AWS::AccountId}:policy/StreamNative/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:ResourceTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "RestrictedActions", + "Effect": "Allow", + "Action": [ + "autoscaling:CancelInstanceRefresh", + "autoscaling:Describe*", + "autoscaling:PutScalingPolicy", + "autoscaling:ResumeProcesses", + "autoscaling:SetDesiredCapacity", + "autoscaling:StartInstanceRefresh", + "autoscaling:SuspendProcesses", + "autoscaling:UpdateAutoScalingGroup", + "ec2:Describe*", + "ec2:Get*", + "eks:Describe*", + "eks:List*", + "eks:UpdateNodegroupConfig", + "eks:UpdateNodegroupVersion", + "elasticloadbalancing:Describe*" + ], + "Resource": [ + "*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:ResourceTag/Vendor": "StreamNative" + } + } + } + ] + } + diff --git a/modules/managed-cloud/files/bootstrap_role_iam_policy.json.tpl b/modules/managed-cloud/files/bootstrap_role_iam_policy.json.tpl index 9ecb33e..31db513 100644 --- a/modules/managed-cloud/files/bootstrap_role_iam_policy.json.tpl +++ b/modules/managed-cloud/files/bootstrap_role_iam_policy.json.tpl @@ -12,10 +12,13 @@ "dynamodb:ListGlobalTables", "dynamodb:ListTables", "dynamodb:ListTagsOfResource", + "ec2:AuthorizeSecurityGroup*", "ec2:Describe*", "ec2:Get*", "eks:Describe*", "eks:List*", + "ec2:RevokeSecurityGroup*", + "ec2:RunInstances", "iam:AttachRolePolicy", "iam:GetInstanceProfile", "iam:GetOpenIDConnectProvider", @@ -43,6 +46,8 @@ "logs:CreateLogGroup", "logs:DescribeLogGroups", "logs:ListTagsLogGroup", + "route53:GetHostedZone", + "route53:ListHostedZones", "s3:ListAllMyBuckets", "s3:ListBucket" ], @@ -59,12 +64,12 @@ "iam:DeletePolicyVersion" ], "Resource": [ - "arn:aws:eks:${region}:${account_id}:nodegroup/*/snc-*-pool/*", + "arn:aws:eks:${region}:${account_id}:nodegroup/*/snc-*-pool*/*", "arn:aws:iam::${account_id}:policy/StreamNative/*" ] }, { - "Sid": "RequireRequestTag", + "Sid": "RequireAWSRequestTag", "Effect": "Allow", "Action": [ "acm:AddTagsToCertificate", @@ -92,19 +97,19 @@ "ec2:CreateVpc", "ec2:CreateVpcEndpoint", "ec2:CreateTags", - "ec2:RunInstances", "eks:Create*", + "eks:RegisterCluster", "eks:TagResource" ], "Resource": "*", "Condition": { - "ForAnyValue:StringEqualsIgnoreCase": { + "StringEquals": { "aws:RequestTag/Vendor": "StreamNative" } } }, { - "Sid": "RequireResourceTag", + "Sid": "RequireAWSResourceTag", "Effect": "Allow", "Action": [ "acm:DeleteCertificate", @@ -117,16 +122,14 @@ "autoscaling:AttachInstances", "autoscaling:CreateOrUpdateTags", "autoscaling:Delete*", - "ec2:AllocateAddress", "ec2:AssignPrivateIpAddresses", "ec2:Associate*", "ec2:AttachInternetGateway", - "ec2:AuthorizeSecurityGroup*", "ec2:CreateLaunchTemplateVersion", "ec2:CreateNatGateway", "ec2:CreateNetworkInterface", "ec2:CreateRoute", - "ec2:CreateRouteTable", + "ec2:CreateRouteTable", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", @@ -139,14 +142,18 @@ "ec2:Revoke*", "ec2:RunInstances", "ec2:Update*", - "eks:Delete*", + "eks:DeleteAddon", + "eks:DeleteCluster", + "eks:DeleteFargateProfile", + "eks:DeregisterCluster", + "eks:DisassociateIdentityProviderConfig", "eks:U*", "logs:DeleteLogGroup", "logs:PutRetentionPolicy" ], "Resource": "*", "Condition": { - "StringEqualsIgnoreCase": { + "StringEquals": { "aws:ResourceTag/Vendor": "StreamNative" } } diff --git a/modules/managed-cloud/files/management_role_iam_policy.json.tpl b/modules/managed-cloud/files/management_role_iam_policy.json.tpl index 9a6ac3a..53b876f 100644 --- a/modules/managed-cloud/files/management_role_iam_policy.json.tpl +++ b/modules/managed-cloud/files/management_role_iam_policy.json.tpl @@ -19,7 +19,7 @@ "Resource": "*" }, { - "Sid": "AllowedIAMActions", + "Sid": "AllowedIAMReadActions", "Effect": "Allow", "Action": [ "iam:GetPolicy*", @@ -33,6 +33,46 @@ "arn:aws:iam::aws:policy/*" ] }, + { + "Sid": "IamRequireRequestTag", + "Effect": "Allow", + "Action": [ + "iam:CreateRole", + "iam:TagPolicy", + "iam:TagRole" + ], + "Resource": [ + "arn:aws:iam::${account_id}:role/StreamNative/*", + "arn:aws:iam::${account_id}:policy/StreamNative/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:RequestTag/Vendor": "StreamNative" + } + } + }, + { + "Sid": "IamRequireResourceTag", + "Effect": "Allow", + "Action": [ + "iam:DeleteRole", + "iam:DetachRolePolicy", + "iam:PutRolePermissionsBoundary", + "iam:SetDefaultPolicyVersion", + "iam:UpdateAssumeRolePolicy", + "iam:UpdateRole", + "iam:UpdateRoleDescription" + ], + "Resource": [ + "arn:aws:iam::${account_id}:role/StreamNative/*", + "arn:aws:iam::${account_id}:policy/StreamNative/*" + ], + "Condition": { + "StringEqualsIgnoreCase": { + "aws:ResourceTag/Vendor": "StreamNative" + } + } + }, { "Sid": "RestrictedActions", "Effect": "Allow", diff --git a/modules/managed-cloud/files/permission_boundary_iam_policy.json.tpl b/modules/managed-cloud/files/permission_boundary_iam_policy.json.tpl index a4841c0..a247c9b 100644 --- a/modules/managed-cloud/files/permission_boundary_iam_policy.json.tpl +++ b/modules/managed-cloud/files/permission_boundary_iam_policy.json.tpl @@ -112,6 +112,7 @@ "arn:aws:iam::${account_id}:policy/StreamNative/*-VaultPolicy", "arn:aws:iam::${account_id}:policy/StreamNative/*-VeleroBackupPolicy", "arn:aws:iam::${account_id}:policy/StreamNative/*-elb-sl-role-*", + "arn:aws:iam::${account_id}:policy/StreamNative/*-deny-log-group*", "arn:aws:iam::${account_id}:policy/StreamNative/StreamNativeCloudPermissionBoundary", "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", "arn:aws:iam::aws:policy/AmazonEKSServicePolicy", @@ -141,7 +142,6 @@ "Sid": "RestrictChangesToVendorAccess", "Effect": "Deny", "Action": [ - "iam:Attach", "iam:Create*", "iam:Delete*", "iam:Put", diff --git a/modules/managed-cloud/main.tf b/modules/managed-cloud/main.tf index 59c0f03..2b0feef 100644 --- a/modules/managed-cloud/main.tf +++ b/modules/managed-cloud/main.tf @@ -33,25 +33,55 @@ data "aws_iam_policy_document" "streamnative_vendor_access" { } } +data "aws_iam_policy_document" "streamnative_control_plane_access" { + statement { + sid = "AllowStreamNativeVendorAccess" + effect = "Allow" + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = [ + var.streamnative_vendor_access_role_arn, + var.streamnative_control_plane_role_arn + ] + } + } + + statement { + sid = "AllowStreamNativeControlPlaneAccess" + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + identifiers = [ + "accounts.google.com" + ] + } + condition { + test = "StringEquals" + values = [var.streamnative_google_account_id] + variable = "accounts.google.com:aud" + } + } +} + ###### #-- Create the IAM Permission Boundary used by all StreamNative #-- IAM Resources. This restricts what type of access we have #-- within your AWS Account and is applied to all our IAM Roles ###### -data "template_file" "permission_boundary" { - template = file("${path.module}/files/permission_boundary_iam_policy.json.tpl") - vars = { - account_id = data.aws_caller_identity.current.account_id - region = var.region - } -} - resource "aws_iam_policy" "permission_boundary" { name = "StreamNativeCloudPermissionBoundary" description = "This policy sets the permission boundary for StreamNative's vendor access. It defines the limits of what StreamNative can do within this AWS account." path = "/StreamNative/" - policy = data.template_file.permission_boundary.rendered - tags = merge({ Vendor = "StreamNative" }, var.tags) + policy = templatefile("${path.module}/files/permission_boundary_iam_policy.json.tpl", + { + account_id = data.aws_caller_identity.current.account_id + region = var.region + }) + tags = merge({ Vendor = "StreamNative" }, var.tags) } ###### @@ -59,14 +89,6 @@ resource "aws_iam_policy" "permission_boundary" { #-- This role is only needed for the initial StreamNative Cloud #-- deployment to an AWS account, or when it is being removed. ###### -data "template_file" "bootstrap_role" { - template = file("${path.module}/files/bootstrap_role_iam_policy.json.tpl") - vars = { - account_id = data.aws_caller_identity.current.account_id - region = var.region - } -} - resource "aws_iam_role" "bootstrap_role" { count = var.create_bootstrap_role ? 1 : 0 name = "StreamNativeCloudBootstrapRole" @@ -82,8 +104,12 @@ resource "aws_iam_policy" "bootstrap_policy" { name = "StreamNativeCloudBootstrapPolicy" description = "This policy sets the minimum amount of permissions needed by the StreamNativeCloudBootstrapRole to bootstrap the StreamNative Cloud deployment." path = "/StreamNative/" - policy = data.template_file.bootstrap_role.rendered - tags = merge({ Vendor = "StreamNative" }, var.tags) + policy = templatefile("${path.module}/files/bootstrap_role_iam_policy.json.tpl", + { + account_id = data.aws_caller_identity.current.account_id + region = var.region + }) + tags = merge({ Vendor = "StreamNative" }, var.tags) } resource "aws_iam_role_policy_attachment" "bootstrap_policy" { @@ -97,26 +123,22 @@ resource "aws_iam_role_policy_attachment" "bootstrap_policy" { #-- This role is used by StreamNative for management and troubleshooting #-- of the managed deployment. ###### -data "template_file" "management_role" { - template = file("${path.module}/files/management_role_iam_policy.json.tpl") - vars = { - account_id = data.aws_caller_identity.current.account_id - region = var.region - } -} - resource "aws_iam_policy" "management_role" { name = "StreamNativeCloudManagementPolicy" description = "This policy sets the limits for the management role needed for StreamNative's vendor access." path = "/StreamNative/" - policy = data.template_file.management_role.rendered - tags = merge({ Vendor = "StreamNative" }, var.tags) + policy = templatefile("${path.module}/files/management_role_iam_policy.json.tpl", + { + account_id = data.aws_caller_identity.current.account_id + region = var.region + }) + tags = merge({ Vendor = "StreamNative" }, var.tags) } resource "aws_iam_role" "management_role" { name = "StreamNativeCloudManagementRole" description = "This role is used by StreamNative for the day to day management of the StreamNative Cloud deployment." - assume_role_policy = data.aws_iam_policy_document.streamnative_vendor_access.json + assume_role_policy = data.aws_iam_policy_document.streamnative_control_plane_access.json path = "/StreamNative/" permissions_boundary = aws_iam_policy.permission_boundary.arn tags = merge({ Vendor = "StreamNative" }, var.tags) @@ -125,4 +147,4 @@ resource "aws_iam_role" "management_role" { resource "aws_iam_role_policy_attachment" "management_role" { policy_arn = aws_iam_policy.management_role.arn role = aws_iam_role.management_role.name -} \ No newline at end of file +} diff --git a/modules/managed-cloud/variables.tf b/modules/managed-cloud/variables.tf index 1501ad6..534e9ec 100644 --- a/modules/managed-cloud/variables.tf +++ b/modules/managed-cloud/variables.tf @@ -29,6 +29,18 @@ variable "region" { type = string } +variable "streamnative_control_plane_role_arn" { + default = "arn:aws:iam::311022431024:role/cloud-manager" + description = "The ARN of the role that is used by StreamNative for Control Plane operations" + type = string +} + +variable "streamnative_google_account_id" { + default = "108050666045451143798" + description = "The Google Cloud service account ID used by StreamNative for Control Plane operations" + type = string +} + variable "streamnative_vendor_access_role_arn" { description = "The arn for the IAM principle (role) provided by StreamNative. This role is used exclusively by StreamNative (with strict permissions) for vendor access into your AWS account" type = string