diff --git a/docs/docs/how-tos/nebari-aws.md b/docs/docs/how-tos/nebari-aws.md index 26bd1594..9211f9f7 100644 --- a/docs/docs/how-tos/nebari-aws.md +++ b/docs/docs/how-tos/nebari-aws.md @@ -4,6 +4,12 @@ title: Deploy Nebari on AWS description: A basic overview of how to deploy Nebari on AWS --- +import CodeBlock from '@theme/CodeBlock'; +import DeployPolicy from '!!raw-loader!../../static/policies/aws/deploy.json'; +import DestroyPolicy from '!!raw-loader!../../static/policies/aws/destroy.json'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + ## Introduction This guide is to help first-time users set up an Amazon Web Services (AWS) account specifically for the purpose of using and deploying Nebari at a production scale. In this guide @@ -41,11 +47,34 @@ happens. ## Authentication In order for Nebari to make requests against the AWS API and create its infrastructure, an authentication method with the appropriate permissions will be required. The best way -to do this is using an [IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) with suitable permissions for your AWS account and Elastic Kubernetes Service (EKS). +to do this is using an [IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) with all the necessary permissions. + +Below are two sets of minimal IAM permissions required to deploy and destroy Nebari. You may either [create separate IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html) for each action or combine them into a single policy that includes all permissions. + +
+ AWS IAM Policies to deploy and destroy Nebari + + + {DeployPolicy} + + + {DestroyPolicy} + + +
+ +:::note + +Make sure to replace the following placeholders in the policies with your own values: + +- `REGION`: The AWS region where you want to deploy Nebari (e.g., `us-west-2`) +- `ACCOUNT_ID`: Your AWS account ID (e.g., `123456789012`) +- `PROJECT_NAME`: The name of your Nebari project, specified under the `project_name` field in your `nebari-config.yaml` file (e.g., `my-nebari-project`) +- `NAMESPACE`: The namespace you want to use for your Nebari deployment, specified under the `namespace` field in your `nebari-config.yaml` file (e.g., `dev`) + ::: As a [best practice](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#lock-away-credentials), do not use the AWS account `root` user for any task where it's not -required. Instead, create a new IAM user for each person that requires administrator access. Then make those users administrators by placing them into an "Administrators" user -group, to which you attach the `AdministratorAccess` managed policy. +required. Instead, create a new IAM user for each person that requires administrator access. Then make those users administrators by placing them into an "Administrators" (or any other name) user group, to which you attach the policies outlined above. If you are using an already existing IAM user, please refer to [Managing access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) for detailed information on how to @@ -63,7 +92,7 @@ Follow these steps to set up your access keys and user accounts: ![Account setup steps for setting your first IAM user on AWS, the image contains an input for creating your username and two item boxes for selecting the type of credential needed for this account](/img/how-tos/how-tos-aws-new-iam-user.png "Creating your IAM user account") -4. Select **Attach existing policies directly**, then select `AdministratorAccess` from the list of policies. For more information, please refer to +4. Select **Attach existing policies directly**, then select the previously created policies to deploy and destroy Nebari from the list of policies. For more information, please refer to [Policies and permissions in IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html); 5. Then proceed with the new user creation setup. diff --git a/docs/package.json b/docs/package.json index f287dba8..c170eba3 100644 --- a/docs/package.json +++ b/docs/package.json @@ -47,6 +47,7 @@ "docusaurus-lunr-search": "^3.3.0", "docusaurus-plugin-sass": "^0.2.5", "prism-react-renderer": "^2.1.0", + "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.77.8" diff --git a/docs/static/policies/aws/deploy.json b/docs/static/policies/aws/deploy.json new file mode 100644 index 00000000..4fdaeaaf --- /dev/null +++ b/docs/static/policies/aws/deploy.json @@ -0,0 +1,283 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTags", + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInstanceTypes", + "ec2:DescribeInternetGateways", + "ec2:DescribeNetworkAcls", + "ec2:DescribeRegions", + "ec2:DescribeRouteTables", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSubnets", + "ec2:DescribeVpcs", + "eks:CreateCluster", + "eks:DescribeAddonVersions", + "elasticfilesystem:CreateFileSystem", + "iam:GetOpenIDConnectProvider", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:TagOpenIDConnectProvider", + "kms:CreateKey", + "kms:DescribeKey", + "kms:ListKeys", + "resource-groups:CreateGroup", + "sts:GetCallerIdentity" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "s3:CreateBucket", + "s3:GetAccelerateConfiguration", + "s3:GetBucketAcl", + "s3:GetBucketCORS", + "s3:GetBucketLogging", + "s3:GetBucketObjectLockConfiguration", + "s3:GetBucketPolicy", + "s3:GetBucketPublicAccessBlock", + "s3:GetBucketRequestPayment", + "s3:GetBucketTagging", + "s3:GetBucketVersioning", + "s3:GetBucketWebsite", + "s3:GetEncryptionConfiguration", + "s3:GetLifecycleConfiguration", + "s3:GetReplicationConfiguration", + "s3:ListBucket", + "s3:PutBucketPublicAccessBlock", + "s3:PutBucketTagging", + "s3:PutBucketVersioning", + "s3:PutEncryptionConfiguration" + ], + "Resource": "arn:aws:s3:::PROJECT_NAME-NAMESPACE-terraform-state" + }, + { + "Effect": "Allow", + "Action": [ + "dynamodb:CreateTable", + "dynamodb:DeleteItem", + "dynamodb:DescribeContinuousBackups", + "dynamodb:DescribeTable", + "dynamodb:DescribeTimeToLive", + "dynamodb:GetItem", + "dynamodb:ListTagsOfResource", + "dynamodb:PutItem", + "dynamodb:TagResource" + ], + "Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/PROJECT_NAME-NAMESPACE-terraform-state-lock" + }, + { + "Effect": "Allow", + "Action": [ + "kms:EnableKeyRotation", + "kms:GetKeyPolicy", + "kms:GetKeyRotationStatus", + "kms:ListResourceTags" + ], + "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/*" + }, + { + "Effect": "Allow", + "Action": [ + "ecr:CreateRepository", + "ecr:DescribeRepositories", + "ecr:ListTagsForResource", + "ecr:TagResource" + ], + "Resource": "arn:aws:ecr:REGION:ACCOUNT_ID:repository/PROJECT_NAME-NAMESPACE-jupyterlab" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateVpc", + "ec2:DescribeVpcAttribute", + "ec2:ModifyVpcAttribute" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + }, + { + "Effect": "Allow", + "Action": [ + "iam:AttachRolePolicy", + "iam:CreateRole", + "iam:GetRole", + "iam:ListAttachedRolePolicies", + "iam:ListRolePolicies", + "iam:TagRole" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:role/*" + }, + { + "Effect": "Allow", + "Action": [ + "iam:CreatePolicy" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:policy/*" + }, + { + "Effect": "Allow", + "Action": [ + "resource-groups:GetGroup", + "resource-groups:GetGroupConfiguration", + "resource-groups:GetGroupQuery", + "resource-groups:GetTags", + "resource-groups:Tag" + ], + "Resource": "arn:aws:resource-groups:REGION:ACCOUNT_ID:group/PROJECT_NAME" + }, + { + "Effect": "Allow", + "Action": [ + "elasticfilesystem:CreateMountTarget", + "elasticfilesystem:DescribeFileSystems", + "elasticfilesystem:DescribeLifecycleConfiguration", + "elasticfilesystem:TagResource" + ], + "Resource": "arn:aws:elasticfilesystem:REGION:ACCOUNT_ID:file-system/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateSubnet" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:subnet/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateInternetGateway" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:internet-gateway/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupEgress", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:CreateSecurityGroup", + "ec2:RevokeSecurityGroupEgress" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:security-group/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AttachInternetGateway" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:internet-gateway/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AssociateRouteTable", + "ec2:ModifySubnetAttribute" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:subnet/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateRouteTable" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:route-table/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:AssociateRouteTable", + "ec2:CreateRoute" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:route-table/*" + }, + { + "Effect": "Allow", + "Action": [ + "iam:PassRole" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:role/PROJECT_NAME-NAMESPACE-eks-cluster-role" + }, + { + "Effect": "Allow", + "Action": [ + "elasticfilesystem:DescribeMountTargetSecurityGroups", + "elasticfilesystem:DescribeMountTargets" + ], + "Resource": "arn:aws:elasticfilesystem:REGION:ACCOUNT_ID:file-system/*" + }, + { + "Effect": "Allow", + "Action": [ + "eks:CreateAddon", + "eks:CreateNodegroup", + "eks:DescribeCluster", + "eks:ListNodegroups", + "eks:TagResource" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:cluster/PROJECT_NAME-NAMESPACE" + }, + { + "Effect": "Allow", + "Action": [ + "iam:PassRole" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:role/PROJECT_NAME-NAMESPACE-eks-node-group-role" + }, + { + "Effect": "Allow", + "Action": [ + "eks:DescribeNodegroup" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:nodegroup/PROJECT_NAME-NAMESPACE/*" + }, + { + "Effect": "Allow", + "Action": [ + "eks:DescribeAddon" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:addon/PROJECT_NAME-NAMESPACE/*" + }, + { + "Effect": "Allow", + "Action": [ + "iam:CreateOpenIDConnectProvider" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:oidc-provider/*" + }, + { + "Effect": "Allow", + "Action": [ + "autoscaling:CreateOrUpdateTags" + ], + "Resource": "arn:aws:autoscaling:REGION:ACCOUNT_ID:autoScalingGroup:*:autoScalingGroupName/*" + }, + { + "Effect": "Allow", + "Action": [ + "s3:GetObject", + "s3:GetObjectTagging", + "s3:GetObjectVersion", + "s3:ListMultipartUploadParts", + "s3:PutObject", + "s3:PutObjectAcl", + "s3:PutObjectTagging" + ], + "Resource": "arn:aws:s3:::PROJECT_NAME-NAMESPACE-terraform-state/*" + } + ] +} diff --git a/docs/static/policies/aws/destroy.json b/docs/static/policies/aws/destroy.json new file mode 100644 index 00000000..ddde7900 --- /dev/null +++ b/docs/static/policies/aws/destroy.json @@ -0,0 +1,217 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeAvailabilityZones", + "ec2:DescribeInstanceTypes", + "ec2:DescribeInternetGateways", + "ec2:DescribeNetworkAcls", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeRegions", + "ec2:DescribeRouteTables", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSubnets", + "ec2:DescribeVpcs", + "eks:DescribeAddonVersions", + "iam:DeleteOpenIDConnectProvider", + "iam:DeletePolicy", + "iam:GetOpenIDConnectProvider", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:ListPolicyVersions", + "kms:ListKeys", + "sts:GetCallerIdentity" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "kms:DescribeKey", + "kms:GetKeyPolicy", + "kms:GetKeyRotationStatus", + "kms:ListResourceTags", + "kms:ScheduleKeyDeletion" + ], + "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/*" + }, + { + "Effect": "Allow", + "Action": [ + "s3:DeleteBucket", + "s3:GetAccelerateConfiguration", + "s3:GetBucketAcl", + "s3:GetBucketCORS", + "s3:GetBucketLogging", + "s3:GetBucketObjectLockConfiguration", + "s3:GetBucketPolicy", + "s3:GetBucketPublicAccessBlock", + "s3:GetBucketRequestPayment", + "s3:GetBucketTagging", + "s3:GetBucketVersioning", + "s3:GetBucketWebsite", + "s3:GetEncryptionConfiguration", + "s3:GetLifecycleConfiguration", + "s3:GetReplicationConfiguration", + "s3:ListBucket", + "s3:ListBucketVersions", + "s3:PutBucketPublicAccessBlock", + "s3:PutEncryptionConfiguration" + ], + "Resource": "arn:aws:s3:::PROJECT_NAME-NAMESPACE-terraform-state" + }, + { + "Effect": "Allow", + "Action": [ + "s3:DeleteObject", + "s3:DeleteObjectVersion", + "s3:GetObject", + "s3:GetObjectTagging", + "s3:GetObjectVersion", + "s3:ListMultipartUploadParts", + "s3:PutObject", + "s3:PutObjectAcl", + "s3:PutObjectTagging" + ], + "Resource": "arn:aws:s3:::PROJECT_NAME-NAMESPACE-terraform-state/*" + }, + { + "Effect": "Allow", + "Action": [ + "dynamodb:DeleteItem", + "dynamodb:DeleteTable", + "dynamodb:DescribeContinuousBackups", + "dynamodb:DescribeTable", + "dynamodb:DescribeTimeToLive", + "dynamodb:GetItem", + "dynamodb:ListTagsOfResource", + "dynamodb:PutItem" + ], + "Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/PROJECT_NAME-NAMESPACE-terraform-state-lock" + }, + { + "Effect": "Allow", + "Action": [ + "eks:DeleteCluster", + "eks:DescribeCluster" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:cluster/PROJECT_NAME-NAMESPACE" + }, + { + "Effect": "Allow", + "Action": [ + "ecr:DeleteRepository", + "ecr:DescribeRepositories", + "ecr:ListTagsForResource" + ], + "Resource": "arn:aws:ecr:REGION:ACCOUNT_ID:repository/PROJECT_NAME-NAMESPACE-jupyterlab" + }, + { + "Effect": "Allow", + "Action": [ + "iam:DeleteRole", + "iam:DetachRolePolicy", + "iam:GetRole", + "iam:ListAttachedRolePolicies", + "iam:ListInstanceProfilesForRole", + "iam:ListRolePolicies" + ], + "Resource": "arn:aws:iam::ACCOUNT_ID:role/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticfilesystem:DeleteFileSystem", + "elasticfilesystem:DescribeFileSystems", + "elasticfilesystem:DescribeLifecycleConfiguration" + ], + "Resource": "arn:aws:elasticfilesystem:REGION:ACCOUNT_ID:file-system/*" + }, + { + "Effect": "Allow", + "Action": [ + "resource-groups:DeleteGroup", + "resource-groups:GetGroup", + "resource-groups:GetGroupConfiguration", + "resource-groups:GetGroupQuery", + "resource-groups:GetTags" + ], + "Resource": "arn:aws:resource-groups:REGION:ACCOUNT_ID:group/PROJECT_NAME" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeVpcAttribute" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + }, + { + "Effect": "Allow", + "Action": [ + "elasticfilesystem:DeleteMountTarget", + "elasticfilesystem:DescribeMountTargetSecurityGroups", + "elasticfilesystem:DescribeMountTargets" + ], + "Resource": "arn:aws:elasticfilesystem:REGION:ACCOUNT_ID:file-system/*" + }, + { + "Effect": "Allow", + "Action": [ + "eks:DeleteNodegroup", + "eks:DescribeNodegroup" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:nodegroup/PROJECT_NAME-NAMESPACE/*" + }, + { + "Effect": "Allow", + "Action": [ + "eks:DeleteAddon", + "eks:DescribeAddon" + ], + "Resource": "arn:aws:eks:REGION:ACCOUNT_ID:addon/PROJECT_NAME-NAMESPACE/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DisassociateRouteTable" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:route-table/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:subnet/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DeleteRouteTable" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:route-table/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DetachInternetGateway" + ], + "Resource": [ + "arn:aws:ec2:REGION:ACCOUNT_ID:internet-gateway/*", + "arn:aws:ec2:REGION:ACCOUNT_ID:vpc/*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DeleteSubnet" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:subnet/*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DeleteSecurityGroup" + ], + "Resource": "arn:aws:ec2:REGION:ACCOUNT_ID:security-group/*" + } + ] +} diff --git a/docs/yarn.lock b/docs/yarn.lock index d00d69ad..ff65b0ed 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -8714,6 +8714,14 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + rc@1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz"