├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── recipe-request.md ├── 101-Creating-and-Assuming-an-IAM-Role ├── .gitignore ├── README.md └── assume-role-policy-template.json ├── 102-Generate-Least-Privilege-IAM-Policy └── README.md ├── 103-Enforcing-IAM-User-Password-Policies └── README.md ├── 104-Testing-IAM-Policies-with-the-IAM-Policy-Simulator ├── README.md ├── TestingIAMPolicieswiththeIAMPolicySimulator.png └── assume-role-policy.json ├── 105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries ├── .gitignore ├── README.md ├── assume-role-policy-template.json ├── boundary-policy-template.json ├── lambda-assume-role-policy.json ├── lambda_function.py └── policy-template.json ├── 106-Connecting-to-EC2-Instances-Using-Session-Manager ├── ConnectingToEC2InstancesUsingAWSSSMSessionManager.png ├── README.md ├── assume-role-policy.json └── cdk-AWS-Cookbook-106 │ ├── .gitignore │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── cdk_aws_cookbook_106 │ ├── __init__.py │ └── cdk_aws_cookbook_106_stack.py │ ├── helper.py │ ├── requirements.txt │ ├── setup.py │ └── source.bat ├── 107-Encrypting-EBS-Volumes-Using-KMS-Keys └── README.md ├── 108-Storing-Encrypting-Accessing-Passwords ├── .gitignore ├── README.md ├── cdk-AWS-Cookbook-108 │ ├── .gitignore │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── cdk_aws_cookbook_108 │ │ ├── __init__.py │ │ └── cdk_aws_cookbook_108_stack.py │ ├── helper.py │ ├── requirements.txt │ ├── setup.py │ └── source.bat └── secret-access-policy-template.json ├── 109-Blocking-Public-Access-for-S3-Buckets ├── .gitignore ├── README.md └── public-read-template.json ├── 110-Serving-Web-Content-Securely-from-S3-with-CloudFront ├── .gitignore ├── README.md ├── bucket-policy-template.json └── distribution-template.json ├── LICENSE └── README.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/recipe-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Recipe request 3 | about: Suggest a recipe for this Chapter 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your recipe request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe what you'd like to learn** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /101-Creating-and-Assuming-an-IAM-Role/.gitignore: -------------------------------------------------------------------------------- 1 | assume-role-policy.json -------------------------------------------------------------------------------- /101-Creating-and-Assuming-an-IAM-Role/README.md: -------------------------------------------------------------------------------- 1 | # Creating and Assuming an IAM Role for Developer Access 2 | ## Clean up 3 | ### Detach the PowerUserAccess policy from the role: 4 | ``` 5 | aws iam detach-role-policy --role-name AWSCookbook101Role \ 6 | --policy-arn arn:aws:iam::aws:policy/PowerUserAccess 7 | ``` 8 | 9 | ### Delete the IAM role: 10 | 11 | `aws iam delete-role --role-name AWSCookbook101Role` 12 | 13 | ### Unset your local variables: 14 | ``` 15 | unset ROLE_ARN 16 | unset PRINCIPAL_ARN 17 | ``` 18 | -------------------------------------------------------------------------------- /101-Creating-and-Assuming-an-IAM-Role/assume-role-policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "AWS": "PRINCIPAL_ARN" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /102-Generate-Least-Privilege-IAM-Policy/README.md: -------------------------------------------------------------------------------- 1 | # Generate a Least Privilege IAM Policy Based on Access Patterns 2 | 3 | ## Clean up 4 | ### Delete the IAM policy that you created 5 | -------------------------------------------------------------------------------- /103-Enforcing-IAM-User-Password-Policies/README.md: -------------------------------------------------------------------------------- 1 | # Enforcing IAM User Password Policies in Your AWS Account 2 | 3 | ## Clean up 4 | ### Delete the login profiles that you created (including the validation step user): 5 | ``` 6 | aws iam delete-login-profile --user-name awscookbook103user 7 | aws iam delete-login-profile --user-name awscookbook103user2 8 | ``` 9 | 10 | ### Remove the user from the group: 11 | ``` 12 | aws iam remove-user-from-group --user-name awscookbook103user \ 13 | --group-name AWSCookbook103Group 14 | ``` 15 | 16 | ### Detach the policy from the group: 17 | ``` 18 | aws iam detach-group-policy --group-name AWSCookbook103Group \ 19 | --policy-arn arn:aws:iam::aws:policy/AWSBillingReadOnlyAccess 20 | ``` 21 | 22 | ### Delete the group: 23 | `aws iam delete-group --group-name AWSCookbook103Group` 24 | 25 | ### Delete the users that you created (including the validation step user): 26 | ``` 27 | aws iam delete-user --user-name awscookbook103user 28 | aws iam delete-user --user-name awscookbook103user2 29 | ``` 30 | 31 | ### Delete the account password policy that you configured: 32 | `aws iam delete-account-password-policy` 33 | 34 | ### Unset the local variables you created: 35 | ``` 36 | unset RANDOM_STRING 37 | unset RANDOM_STRING2 38 | ``` 39 | -------------------------------------------------------------------------------- /104-Testing-IAM-Policies-with-the-IAM-Policy-Simulator/README.md: -------------------------------------------------------------------------------- 1 | # Testing IAM Policies with the IAM Policy Simulator 2 | 3 | ## Problem 4 | You have an IAM policy that you would like to put into use but would like to test its effectiveness first. 5 | 6 | ## Solution 7 | Attach an IAM policy to an IAM role and simulate actions with the IAM Policy Simulator, as shown in Figure 1-6. 8 | 9 | ![Figure 1-6](./TestingIAMPolicieswiththeIAMPolicySimulator.png) 10 | 11 | ## Step 12 | 13 | 1. Create a file called assume-role-policy.json with the following content (file provided in the repository): 14 | ``` 15 | { 16 | "Version": "2012-10-17", 17 | "Statement": [ 18 | { 19 | "Effect": "Allow", 20 | "Principal": { 21 | "Service": "ec2.amazonaws.com" 22 | }, 23 | "Action": "sts:AssumeRole" 24 | } 25 | ] 26 | } 27 | ``` 28 | 29 | 2. Create an IAM role using the assume-role-policy.json file: 30 | ``` 31 | aws iam create-role --assume-role-policy-document \ 32 | file://assume-role-policy.json --role-name AWSCookbook104IamRole 33 | ``` 34 | 35 | You should see output similar to the following: 36 | ``` 37 | { 38 | "Role": { 39 | "Path": "/", 40 | "RoleName": "AWSCookbook104IamRole", 41 | "RoleId": "<>", 42 | "Arn": "arn:aws:iam::111111111111:role/AWSCookbook104IamRole", 43 | "CreateDate": "2021-09-22T23:37:44+00:00", 44 | "AssumeRolePolicyDocument": { 45 | "Version": "2012-10-17", 46 | "Statement": [ 47 | ... 48 | ``` 49 | 50 | 3. Attach the IAM managed policy for `AmazonEC2ReadOnlyAccess` to the IAM role: 51 | ``` 52 | aws iam attach-role-policy --role-name AWSCookbook104IamRole \ 53 | --policy-arn arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess 54 | ``` 55 | 56 | > Tip: You can find a list of all the actions, resources, and condition keys for EC2 in this AWS [article](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html#amazonec2-actions-as-permissions). The IAM [global condition context keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html) are also useful in authoring fine-grained policies. 57 | 58 | ### Validation checks 59 | Simulate the effect of the IAM policy you are using, testing several different types of actions on the EC2 service. 60 | 61 | Test the `ec2:CreateInternetGateway` action: 62 | ``` 63 | aws iam simulate-principal-policy \ 64 | --policy-source-arn arn:aws:iam::$AWS_ACCOUNT_ID:role/AWSCookbook104IamRole \ 65 | --action-names ec2:CreateInternetGateway 66 | ``` 67 | 68 | You should see output similar to the following (note the `EvalDecision`): 69 | ``` 70 | { 71 | "EvaluationResults": [ 72 | { 73 | "EvalActionName": "ec2:CreateInternetGateway", 74 | "EvalResourceName": "*", 75 | "EvalDecision": "implicitDeny", 76 | "MatchedStatements": [], 77 | "MissingContextValues": [] 78 | } 79 | ] 80 | } 81 | ``` 82 | 83 | > Note: Since you attached only the AWS managed `AmazonEC2ReadOnlyAccess` IAM policy to the role in this recipe, you will see an implicit deny for the `CreateInternetGateway` action. This is expected behavior. `AmazonEC2ReadOnlyAccess` does not grant any “create” capabilities for the EC2 service. 84 | 85 | Test the `ec2:DescribeInstances` action: 86 | ``` 87 | aws iam simulate-principal-policy \ 88 | --policy-source-arn arn:aws:iam::$AWS_ACCOUNT_ID:role/AWSCookbook104IamRole \ 89 | --action-names ec2:DescribeInstances 90 | ``` 91 | 92 | You should see output similar to the following: 93 | ``` 94 | { 95 | "EvaluationResults": [ 96 | { 97 | "EvalActionName": "ec2:DescribeInstances", 98 | "EvalResourceName": "*", 99 | "EvalDecision": "allowed", 100 | "MatchedStatements": [ 101 | { 102 | "SourcePolicyId": "AmazonEC2ReadOnlyAccess", 103 | "SourcePolicyType": "IAM Policy", 104 | "StartPosition": { 105 | "Line": 3, 106 | "Column": 17 107 | }, 108 | "EndPosition": { 109 | "Line": 8, 110 | "Column": 6 111 | } 112 | } 113 | ], 114 | "MissingContextValues": [] 115 | } 116 | ] 117 | } 118 | ``` 119 | 120 | > Note: The `AmazonEC2ReadOnlyAccess` policy allows read operations on the EC2 service, so the `DescribeInstances` operation succeeds when you simulate this action. 121 | 122 | ## Clean up 123 | ### Step Text 124 | Detach the AmazonEC2ReadOnlyAccess policy from the role: 125 | ``` 126 | aws iam detach-role-policy --role-name AWSCookbook104IamRole \ 127 | --policy-arn arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess 128 | ``` 129 | 130 | Delete the IAM Role for the proxy: 131 | 132 | ``` 133 | aws iam delete-role --role-name AWSCookbook104IamRole 134 | ``` 135 | 136 | ## Discussion 137 | [IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) let you define permissions for managing access in AWS. Policies can be attached to principals that allow you to grant (or deny) permissions to resources, users, groups and services. It is always best to scope your policies to the minimal set of permissions required as a security best practice. The [IAM Policy Simulator](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html) can be extremely helpful when designing and managing your own IAM policies for least-privileged access. 138 | 139 | The IAM Policy Simulator also exposes a web interface you can use to test and troubleshoot IAM policies and understand their net effect with the policy you define. You can test all the policies or a subset of policies that you have attached to users, groups, and roles. 140 | 141 | > Tip: The IAM Policy Simulator can help you simulate the effect of the following: 142 | > * Identity-based policies 143 | > * IAM permissions boundaries 144 | > * AWS Organizations service control policies (SCPs) 145 | > * Resource-based policies 146 | 147 | After you review the Policy Simulator results, you can add additional statements to your policies that either solve your issue (from a troubleshooting standpoint) or attach newly created policies to users, groups, and roles with the confidence that the net effect of the policy was what you intended. 148 | 149 | > Note: To help you easily build IAM policies from scratch, AWS provides the [AWS Policy Generator](https://awspolicygen.s3.amazonaws.com/policygen.html). 150 | 151 | ### Challenge 152 | Simulate the effect of a permissions boundary on an IAM principal (see [Recipe 1.5](https://github.com/AWSCookbook/Security/tree/main/105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries)). 153 | -------------------------------------------------------------------------------- /104-Testing-IAM-Policies-with-the-IAM-Policy-Simulator/TestingIAMPolicieswiththeIAMPolicySimulator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWSCookbook/Security/3e29c8c94742b12aed1e1acde333822510e288fd/104-Testing-IAM-Policies-with-the-IAM-Policy-Simulator/TestingIAMPolicieswiththeIAMPolicySimulator.png -------------------------------------------------------------------------------- /104-Testing-IAM-Policies-with-the-IAM-Policy-Simulator/assume-role-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "ec2.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | 14 | -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/.gitignore: -------------------------------------------------------------------------------- 1 | assume-role-policy.json 2 | policy.json 3 | boundary-policy.json -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/README.md: -------------------------------------------------------------------------------- 1 | # Delegating IAM Administrative Capabilities Using Permissions Boundaries 2 | 3 | ## Clean up 4 | 5 | ### Unset the variables you set to assume the AWSCookbook105 role in your terminal: 6 | ``` 7 | unset AWS_ACCESS_KEY_ID 8 | unset AWS_SECRET_ACCESS_KEY 9 | unset AWS_SESSION_TOKEN 10 | ``` 11 | 12 | ### Detach the AmazonDynamoDBFullAccess and CloudWatchFullAccess policy from the role: 13 | ``` 14 | aws iam detach-role-policy --role-name AWSCookbook105test1 \ 15 | --policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess 16 | 17 | aws iam detach-role-policy --role-name AWSCookbook105test1 \ 18 | --policy-arn arn:aws:iam::aws:policy/CloudWatchFullAccess 19 | ``` 20 | 21 | ### Delete the IAM Role you used to test: 22 | ``` 23 | aws iam delete-role --role-name AWSCookbook105test1 24 | ``` 25 | 26 | ### Detach the Policy you created from the role: 27 | ``` 28 | aws iam detach-role-policy --role-name AWSCookbook105Role \ 29 | --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSCookbook105Policy 30 | ``` 31 | 32 | ### Delete the policy: 33 | ``` 34 | aws iam delete-policy --policy-arn \ 35 | arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSCookbook105Policy 36 | ``` 37 | 38 | ### Delete the permissions boundary: 39 | ``` 40 | aws iam delete-policy --policy-arn \ 41 | arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSCookbook105PB 42 | ``` 43 | 44 | ### Delete the IAM Role: 45 | ``` 46 | aws iam delete-role --role-name AWSCookbook105Role 47 | ``` 48 | 49 | ### Unset the variables you set: 50 | ``` 51 | unset PRINCIPAL_ARN 52 | unset ROLE_ARN 53 | unset TEST_ROLE_1 54 | unset creds 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/assume-role-policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "AWS": "PRINCIPAL_ARN" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/boundary-policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "CreateLogGroup", 6 | "Effect": "Allow", 7 | "Action": "logs:CreateLogGroup", 8 | "Resource": "arn:aws:logs:*:AWS_ACCOUNT_ID:*" 9 | }, 10 | { 11 | "Sid": "CreateLogStreamandEvents", 12 | "Effect": "Allow", 13 | "Action": [ 14 | "logs:CreateLogStream", 15 | "logs:PutLogEvents" 16 | ], 17 | "Resource": "arn:aws:logs:*:AWS_ACCOUNT_ID:*" 18 | }, 19 | { 20 | "Sid": "DynamoDBPermissions", 21 | "Effect": "Allow", 22 | "Action": [ 23 | "dynamodb:PutItem", 24 | "dynamodb:UpdateItem", 25 | "dynamodb:DeleteItem" 26 | ], 27 | "Resource": "arn:aws:dynamodb:*:AWS_ACCOUNT_ID:table/AWSCookbook*" 28 | }, 29 | { 30 | "Sid": "S3Permissions", 31 | "Effect": "Allow", 32 | "Action": [ 33 | "s3:GetObject", 34 | "s3:PutObject" 35 | ], 36 | "Resource": "arn:aws:s3:::AWSCookbook*/*" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/lambda-assume-role-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "lambda.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/lambda_function.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | 5 | def lambda_handler(event, context): 6 | 7 | return { 8 | 'statusCode': 200, 9 | 'body': json.dumps('Hello AWSCookbook Reader!') 10 | } 11 | -------------------------------------------------------------------------------- /105-Delegating-IAM-Administrative-Capabilities-Using-Permissions-Boundaries/policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "DenyPBDelete", 6 | "Effect": "Deny", 7 | "Action": "iam:DeleteRolePermissionsBoundary", 8 | "Resource": "*" 9 | }, 10 | { 11 | "Sid": "IAMRead", 12 | "Effect": "Allow", 13 | "Action": [ 14 | "iam:Get*", 15 | "iam:List*" 16 | ], 17 | "Resource": "*" 18 | }, 19 | { 20 | "Sid": "IAMPolicies", 21 | "Effect": "Allow", 22 | "Action": [ 23 | "iam:CreatePolicy", 24 | "iam:DeletePolicy", 25 | "iam:CreatePolicyVersion", 26 | "iam:DeletePolicyVersion", 27 | "iam:SetDefaultPolicyVersion" 28 | ], 29 | "Resource": "arn:aws:iam::AWS_ACCOUNT_ID:policy/AWSCookbook*" 30 | }, 31 | { 32 | "Sid": "IAMRolesWithBoundary", 33 | "Effect": "Allow", 34 | "Action": [ 35 | "iam:CreateRole", 36 | "iam:DeleteRole", 37 | "iam:PutRolePolicy", 38 | "iam:DeleteRolePolicy", 39 | "iam:AttachRolePolicy", 40 | "iam:DetachRolePolicy" 41 | ], 42 | "Resource": [ 43 | "arn:aws:iam::AWS_ACCOUNT_ID:role/AWSCookbook*" 44 | ], 45 | "Condition": { 46 | "StringEquals": { 47 | "iam:PermissionsBoundary": "arn:aws:iam::AWS_ACCOUNT_ID:policy/AWSCookbook105PB" 48 | } 49 | } 50 | }, 51 | { 52 | "Sid": "ServerlessFullAccess", 53 | "Effect": "Allow", 54 | "Action": [ 55 | "lambda:*", 56 | "logs:*", 57 | "dynamodb:*", 58 | "s3:*" 59 | ], 60 | "Resource": "*" 61 | }, 62 | { 63 | "Sid": "PassRole", 64 | "Effect": "Allow", 65 | "Action": "iam:PassRole", 66 | "Resource": "arn:aws:iam::AWS_ACCOUNT_ID:role/AWSCookbook*", 67 | "Condition": { 68 | "StringLikeIfExists": { 69 | "iam:PassedToService": "lambda.amazonaws.com" 70 | } 71 | } 72 | }, 73 | { 74 | "Sid": "ProtectPB", 75 | "Effect": "Deny", 76 | "Action": [ 77 | "iam:CreatePolicyVersion", 78 | "iam:DeletePolicy", 79 | "iam:DeletePolicyVersion", 80 | "iam:SetDefaultPolicyVersion" 81 | ], 82 | "Resource": [ 83 | "arn:aws:iam::AWS_ACCOUNT_ID:policy/AWSCookbook105PB", 84 | "arn:aws:iam::AWS_ACCOUNT_ID:policy/AWSCookbook105Policy" 85 | ] 86 | } 87 | ] 88 | } -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/ConnectingToEC2InstancesUsingAWSSSMSessionManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWSCookbook/Security/3e29c8c94742b12aed1e1acde333822510e288fd/106-Connecting-to-EC2-Instances-Using-Session-Manager/ConnectingToEC2InstancesUsingAWSSSMSessionManager.png -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/README.md: -------------------------------------------------------------------------------- 1 | # Connecting to EC2 Instances Using AWS SSM Session Manager 2 | 3 | ## Problem 4 | You have an EC2 instance in a private subnet and need to connect to the instance without using SSH over the internet. 5 | 6 | ## Solution 7 | Create an IAM role, attach the `AmazonSSMManagedInstanceCore` policy, create an EC2 instance profile, attach the IAM role you created to the instance profile, associate the EC2 instance profile to an EC2 instance, and finally, run the `aws ssm start-session` command to connect to the instance. A logical flow of these steps is shown in Figure 1-8. 8 | 9 | ![Figure 1.8](ConnectingToEC2InstancesUsingAWSSSMSessionManager.png) 10 | 11 | ### Prerequisites 12 | * Amazon Virtual Private Cloud (VPC) with isolated or private subnets and associated route tables 13 | * [Required VPC endpoints for AWS Systems Manager](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-systems-manager-vpc-endpoints/) 14 | * AWS CLI v2 with the [Session Manager plugin installed](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) 15 | 16 | ## Preparation 17 | ### This recipe requires some “prep work” which deploys resources that you’ll build the solution on. You will use the AWS CDK to deploy these resources 18 | 19 | ### In the root of this Chapter’s repo cd to the “106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/” directory and follow the subsequent steps: 20 | 21 | ``` 22 | cd 106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/ 23 | test -d .venv || python3 -m venv .venv 24 | source .venv/bin/activate 25 | pip install --upgrade pip 26 | pip install -r requirements.txt 27 | cdk deploy 28 | ``` 29 | 30 | ### Wait for the cdk deploy command to complete. 31 | 32 | ### We created a helper.py script to let you easily create and export environment variables to make subsequent commands easier. Run the script, and copy the output to your terminal to export variables: 33 | 34 | `python helper.py` 35 | 36 | ### Navigate up to the main directory for this recipe (out of the “cdk-AWS-Cookbook-106” directory): 37 | 38 | ``` 39 | cd .. 40 | ``` 41 | 42 | 43 | ## Steps 44 | 1. Create a file named assume-role-policy.json with the following content (file provided in the repository): 45 | ``` 46 | { 47 | "Version": "2012-10-17", 48 | "Statement": [ 49 | { 50 | "Effect": "Allow", 51 | "Principal": { 52 | "Service": "ec2.amazonaws.com" 53 | }, 54 | "Action": "sts:AssumeRole" 55 | } 56 | ] 57 | } 58 | ``` 59 | 60 | 2. Create an IAM role with the statement in the provided assume-role-policy.json file using this command: 61 | ``` 62 | ROLE_ARN=$(aws iam create-role --role-name AWSCookbook106SSMRole \ 63 | --assume-role-policy-document file://assume-role-policy.json \ 64 | --output text --query Role.Arn) 65 | eiifccrckjintdrfblkfefnkifdfuufilcjjdhnlbrj 66 | ``` 67 | 68 | 3. Attach the AmazonSSMManagedInstanceCore managed policy to the role so that the role allows access to AWS Systems Manager: 69 | ``` 70 | aws iam attach-role-policy --role-name AWSCookbook106SSMRole \ 71 | --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore 72 | ``` 73 | 74 | 4. Create an instance profile: 75 | ``` 76 | aws iam create-instance-profile \ 77 | --instance-profile-name AWSCookbook106InstanceProfile 78 | ``` 79 | You should see output similar to the following: 80 | ``` 81 | { 82 | "InstanceProfile": { 83 | "Path": "/", 84 | "InstanceProfileName": "AWSCookbook106InstanceProfile", 85 | "InstanceProfileId": "(RandomString", 86 | "Arn": "arn:aws:iam::111111111111:instance-profile/AWSCookbook106InstanceProfile", 87 | "CreateDate": "2021-11-28T20:26:23+00:00", 88 | "Roles": [] 89 | } 90 | } 91 | ``` 92 | 93 | 5. Add the role that you created to the instance profile: 94 | ``` 95 | aws iam add-role-to-instance-profile \ 96 | --role-name AWSCookbook106SSMRole \ 97 | --instance-profile-name AWSCookbook106InstanceProfile 98 | ``` 99 | >NOTE: The EC2 instance profile contains a role that you create. The instance profile association with an instance allows it to define “who I am,” and the role defines “what I am permitted to do.” Both are required by IAM to allow an EC2 instance to communicate with other AWS services using the IAM service. You can get a list of instance profiles in your account by running the `aws iam list-instance-profiles` AWS CLI command. 100 | 101 | 6. Query SSM for the latest Amazon Linux 2 AMI ID available in your Region and save it as an environment variable: 102 | ``` 103 | AMI_ID=$(aws ssm get-parameters --names \ 104 | /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \ 105 | --query 'Parameters[0].[Value]' --output text) 106 | ``` 107 | 108 | 7. Launch an instance in one of your subnets that references the instance profile you created and also uses a `Name` tag that helps you identify the instance in the console: 109 | ``` 110 | INSTANCE_ID=$(aws ec2 run-instances --image-id $AMI_ID \ 111 | --count 1 \ 112 | --instance-type t3.nano \ 113 | --iam-instance-profile Name=AWSCookbook106InstanceProfile \ 114 | --subnet-id $SUBNET_1 \ 115 | --security-group-ids $INSTANCE_SG \ 116 | --metadata-options \ 117 | HttpTokens=required,HttpPutResponseHopLimit=64,HttpEndpoint=enabled \ 118 | --tag-specifications \ 119 | 'ResourceType=instance,Tags=[{Key=Name,Value=AWSCookbook106}]' \ 120 | 'ResourceType=volume,Tags=[{Key=Name,Value=AWSCookbook106}]' \ 121 | --query Instances[0].InstanceId \ 122 | --output text) 123 | ``` 124 | > TIP: [EC2 instance metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) is a feature you can use within your EC2 instance to access information about your EC2 instance over an HTTP endpoint from the instance itself. This is helpful for scripting and automation via [user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-add-user-data.html). You should always use the latest version of instance metadata. In step 7, you did this by specifying the `--metadata-options` flag and providing the `HttpTokens=required` option that forces [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html). 125 | 126 | ## Validation checks 127 | Ensure your EC2 instance has registered with SSM. Use the following command to check the status. This command should return the instance ID: 128 | ``` 129 | aws ssm describe-instance-information \ 130 | --filters Key=ResourceType,Values=EC2Instance \ 131 | --query "InstanceInformationList[].InstanceId" --output text 132 | ``` 133 | Connect to the EC2 instance by using SSM Session Manager: 134 | ``` 135 | aws ssm start-session --target $INSTANCE_ID 136 | ``` 137 | You should now be connected to your instance and see a bash prompt. From the bash prompt, run a command to validate you are connected to your EC2 instance by querying the metadata service for an IMDSv2 token and using the token to query metadata for the instance profile associated with the instance: 138 | ``` 139 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 140 | curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/info 141 | ``` 142 | You should see output similar to the following: 143 | ``` 144 | { 145 | "Code" : "Success", 146 | "LastUpdated" : "2021-09-23T16:03:25Z", 147 | "InstanceProfileArn" : "arn:aws:iam::111111111111:instance-profile/AWSCookbook106InstanceProfile", 148 | "InstanceProfileId" : "AIPAZVTINAMEXAMPLE" 149 | } 150 | ``` 151 | Exit the Session Manager session: 152 | ``` 153 | exit 154 | ``` 155 | ## Clean up 156 | 157 | ### Terminate the EC2 Instance: 158 | 159 | ``` 160 | aws ec2 terminate-instances --instance-ids $INSTANCE_ID 161 | ``` 162 | 163 | ### Detach the policy from the role: 164 | 165 | ``` 166 | aws iam detach-role-policy --role-name AWSCookbook106SSMRole --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore 167 | ``` 168 | 169 | ### Remove the role from the instance profile: 170 | 171 | ``` 172 | aws iam remove-role-from-instance-profile --instance-profile-name AWSCookbook106InstanceProfile --role-name AWSCookbook106SSMRole 173 | ``` 174 | 175 | ### Delete the instance profile: 176 | 177 | ``` 178 | aws iam delete-instance-profile --instance-profile-name AWSCookbook106InstanceProfile 179 | ``` 180 | 181 | ### Delete the role: 182 | 183 | ``` 184 | aws iam delete-role --role-name AWSCookbook106SSMRole 185 | ``` 186 | 187 | ### Go to the cdk-AWS-Cookbook-106 directory: 188 | 189 | ``` 190 | cd cdk-AWS-Cookbook-106/ 191 | ``` 192 | 193 | ### To clean up the environment variables, run the helper.py script in this recipe’s cdk- directory with the --unset flag, and copy the output to your terminal to export variables: 194 | 195 | ``` 196 | python helper.py --unset 197 | ``` 198 | 199 | ### Unset your manually created environment variables: 200 | 201 | ``` 202 | unset ROLE_ARN 203 | unset AMI_ID 204 | unset INSTANCE_ID 205 | ``` 206 | 207 | ### Use the AWS CDK to destroy the resources, deactivate your Python virtual environment, and go to the root of the chapter: 208 | 209 | ``` 210 | cdk destroy && deactivate && rm -r .venv/ && cd ../.. 211 | ``` 212 | 213 | ## Discussion 214 | When you use AWS SSM Session Manager to connect to EC2 instances, you eliminate your dependency on Secure Shell (SSH) over the internet for command-line access to your instances. Once you configure Session Manager for your instances, you can instantly connect to a bash shell session on Linux or a PowerShell session for Windows systems. 215 | 216 | > WARNING: SSM can log all commands and their output during a session. You can set a preference to stop the logging of sensitive data (e.g., passwords) with this command: `stty -echo; read passwd; stty echo;` There is more information in an [AWS article](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-logging.html) about logging session activity. 217 | 218 | Session Manager works by communicating with the AWS Systems Manager (SSM) API endpoints within the AWS Region you are using over HTTPS (TCP port 443). The agent on your instance registers with the SSM service at boot time. No inbound security group rules are needed for Session Manager functionality. We recommend configuring [VPC Endpoints for Session Manager](https://www.youtube.com/watch?v=cjSuHarpQJg) to avoid the need for internet traffic and the cost of Network Address Translation (NAT) gateways. 219 | 220 | Here are some examples of the increased security posture Session Manager provides: 221 | * No internet-facing TCP ports need to be allowed in security groups associated with instances. 222 | * You can run instances in private (or isolated) subnets without exposing them directly to the internet and still access them for management duties. 223 | * There is no need to create, associate, and manage SSH keys with instances. 224 | * There is no need to manage user accounts and passwords on instances. 225 | * You can delegate access to manage EC2 instances using IAM roles. 226 | 227 | > NOTE: Any tool like SSM that provides such powerful capabilities must be carefully audited. AWS provides information about [locking down permissions](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-ssm-user-permissions.html) for the SSM user, and more information about [auditing session activity](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-auditing.html). 228 | 229 | ### Challenge 230 | View the logs for a session and create an alert whenever the rm command is executed. 231 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/assume-role-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "ec2.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | .venv 6 | *.egg-info 7 | 8 | # CDK asset staging directory 9 | .cdk.staging 10 | cdk.out 11 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to your CDK Python project! 3 | 4 | This is a blank project for Python development with CDK. 5 | 6 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 7 | 8 | This project is set up like a standard Python project. The initialization 9 | process also creates a virtualenv within this project, stored under the `.venv` 10 | directory. To create the virtualenv it assumes that there is a `python3` 11 | (or `python` for Windows) executable in your path with access to the `venv` 12 | package. If for any reason the automatic creation of the virtualenv fails, 13 | you can create the virtualenv manually. 14 | 15 | To manually create a virtualenv on MacOS and Linux: 16 | 17 | ``` 18 | $ python3 -m venv .venv 19 | ``` 20 | 21 | After the init process completes and the virtualenv is created, you can use the following 22 | step to activate your virtualenv. 23 | 24 | ``` 25 | $ source .venv/bin/activate 26 | ``` 27 | 28 | If you are a Windows platform, you would activate the virtualenv like this: 29 | 30 | ``` 31 | % .venv\Scripts\activate.bat 32 | ``` 33 | 34 | Once the virtualenv is activated, you can install the required dependencies. 35 | 36 | ``` 37 | $ pip install -r requirements.txt 38 | ``` 39 | 40 | At this point you can now synthesize the CloudFormation template for this code. 41 | 42 | ``` 43 | $ cdk synth 44 | ``` 45 | 46 | To add additional dependencies, for example other CDK libraries, just add 47 | them to your `setup.py` file and rerun the `pip install -r requirements.txt` 48 | command. 49 | 50 | ## Useful commands 51 | 52 | * `cdk ls` list all stacks in the app 53 | * `cdk synth` emits the synthesized CloudFormation template 54 | * `cdk deploy` deploy this stack to your default AWS account/region 55 | * `cdk diff` compare deployed stack with current state 56 | * `cdk docs` open CDK documentation 57 | 58 | Enjoy! 59 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import aws_cdk as cdk 5 | 6 | from cdk_aws_cookbook_106.cdk_aws_cookbook_106_stack import CdkAwsCookbook106Stack 7 | 8 | 9 | app = cdk.App() 10 | CdkAwsCookbook106Stack(app, "cdk-aws-cookbook-106", 11 | # If you don't specify 'env', this stack will be environment-agnostic. 12 | # Account/Region-dependent features and context lookups will not work, 13 | # but a single synthesized template can be deployed anywhere. 14 | 15 | # Uncomment the next line to specialize this stack for the AWS Account 16 | # and Region that are implied by the current CLI configuration. 17 | 18 | #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), 19 | 20 | # Uncomment the next line if you know exactly what Account and Region you 21 | # want to deploy the stack to. */ 22 | 23 | # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html 24 | ) 25 | 26 | app.synth() 27 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:stackRelativeExports": "true", 6 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 7 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 8 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/cdk_aws_cookbook_106/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWSCookbook/Security/3e29c8c94742b12aed1e1acde333822510e288fd/106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/cdk_aws_cookbook_106/__init__.py -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/cdk_aws_cookbook_106/cdk_aws_cookbook_106_stack.py: -------------------------------------------------------------------------------- 1 | from constructs import Construct 2 | from aws_cdk import ( 3 | aws_ec2 as ec2, 4 | aws_iam as iam, 5 | Stack, 6 | CfnOutput, 7 | RemovalPolicy 8 | ) 9 | 10 | 11 | class CdkAwsCookbook106Stack(Stack): 12 | 13 | def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: 14 | super().__init__(scope, construct_id, **kwargs) 15 | 16 | isolated_subnets = ec2.SubnetConfiguration( 17 | name="ISOLATED", 18 | subnet_type=ec2.SubnetType.PRIVATE_ISOLATED, 19 | cidr_mask=24 20 | ) 21 | 22 | # create VPC 23 | vpc = ec2.Vpc( 24 | self, 25 | 'AWS-Cookbook-VPC-106', 26 | cidr='10.10.0.0/23', 27 | subnet_configuration=[isolated_subnets] 28 | ) 29 | 30 | # -------- Begin EC2 Helper --------- 31 | vpc.add_interface_endpoint( 32 | 'VPCSSMInterfaceEndpoint', 33 | service=ec2.InterfaceVpcEndpointAwsService('ssm'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 34 | private_dns_enabled=True, 35 | subnets=ec2.SubnetSelection( 36 | one_per_az=False, 37 | subnet_type=ec2.SubnetType.PRIVATE_ISOLATED 38 | ), 39 | ) 40 | 41 | vpc.add_interface_endpoint( 42 | 'VPCEC2MessagesInterfaceEndpoint', 43 | service=ec2.InterfaceVpcEndpointAwsService('ec2messages'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 44 | private_dns_enabled=True, 45 | subnets=ec2.SubnetSelection( 46 | one_per_az=False, 47 | subnet_type=ec2.SubnetType.PRIVATE_ISOLATED 48 | ), 49 | ) 50 | 51 | vpc.add_interface_endpoint( 52 | 'VPCSSMMessagesInterfaceEndpoint', 53 | service=ec2.InterfaceVpcEndpointAwsService('ssmmessages'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 54 | private_dns_enabled=True, 55 | subnets=ec2.SubnetSelection( 56 | one_per_az=False, 57 | subnet_type=ec2.SubnetType.PRIVATE_ISOLATED 58 | ), 59 | ) 60 | 61 | instanceSG = ec2.SecurityGroup( 62 | self, 63 | 'InstanceSecurityGroup', 64 | vpc=vpc, 65 | allow_all_outbound=True, 66 | ) 67 | 68 | # outputs 69 | isolated_subnets = vpc.select_subnets(subnet_type=ec2.SubnetType.PRIVATE_ISOLATED) 70 | 71 | CfnOutput( 72 | self, 73 | 'Subnet1', 74 | value=isolated_subnets.subnet_ids[0] 75 | ) 76 | 77 | CfnOutput( 78 | self, 79 | 'InstanceSg', 80 | value=instanceSG.security_group_id 81 | ) 82 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import argparse 4 | 5 | 6 | def change_case(str): 7 | res = [str[0]] 8 | for c in str[1:]: 9 | if c in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'): 10 | res.append('_') 11 | res.append(c) 12 | elif c in ('123456789'): 13 | res.append('_') 14 | res.append(c) 15 | else: 16 | res.append(c.upper()) 17 | 18 | return ''.join(res) 19 | 20 | 21 | parser = argparse.ArgumentParser(description="Generate commands to set and unset environment variables") 22 | parser.add_argument('--unset', action='store_true', help="Generate commands to unset environment variables by setting this flag") 23 | 24 | args = parser.parse_args() 25 | 26 | os.environ['AWS_DEFAULT_REGION'] = os.environ.get('AWS_REGION') 27 | 28 | cfn = boto3.client('cloudformation') 29 | stackname = os.path.basename(os.getcwd()).lower() 30 | response = cfn.describe_stacks(StackName=stackname) 31 | unsets = [] 32 | sets = [] 33 | 34 | outputs = response["Stacks"][0]["Outputs"] 35 | print("Copy and paste the commands below into your terminal") 36 | print("") 37 | for output in outputs: 38 | if ', ' in output["OutputValue"]: 39 | sets.append(change_case(output["OutputKey"]) + "='" + ', '.join('"{}"'.format(word) for word in output["OutputValue"].split(", ")) + "'") 40 | else: 41 | sets.append(change_case(output["OutputKey"]) + "='" + output["OutputValue"] + "'") 42 | unsets.append("unset " + change_case(output["OutputKey"])) 43 | 44 | if (args.unset): 45 | print('\n'.join(map(str, unsets))) 46 | else: 47 | print('\n'.join(map(str, sets))) 48 | 49 | print("") 50 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | boto3 -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | 4 | with open("README.md") as fp: 5 | long_description = fp.read() 6 | 7 | 8 | setuptools.setup( 9 | name="cdk_aws_cookbook_106", 10 | version="0.0.1", 11 | 12 | description="An empty CDK Python app", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | 16 | author="author", 17 | 18 | package_dir={"": "cdk_aws_cookbook_106"}, 19 | packages=setuptools.find_packages(where="cdk_aws_cookbook_106"), 20 | 21 | install_requires=[ 22 | "aws-cdk-lib==2.0.0-rc.21", 23 | "constructs>=10.0.0,<11.0.0", 24 | ], 25 | 26 | python_requires=">=3.6", 27 | 28 | classifiers=[ 29 | "Development Status :: 4 - Beta", 30 | 31 | "Intended Audience :: Developers", 32 | 33 | "Programming Language :: JavaScript", 34 | "Programming Language :: Python :: 3 :: Only", 35 | "Programming Language :: Python :: 3.6", 36 | "Programming Language :: Python :: 3.7", 37 | "Programming Language :: Python :: 3.8", 38 | 39 | "Topic :: Software Development :: Code Generators", 40 | "Topic :: Utilities", 41 | 42 | "Typing :: Typed", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /106-Connecting-to-EC2-Instances-Using-Session-Manager/cdk-AWS-Cookbook-106/source.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem The sole purpose of this script is to make the command 4 | rem 5 | rem source .venv/bin/activate 6 | rem 7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. 8 | rem On Windows, this command just runs this batch file (the argument is ignored). 9 | rem 10 | rem Now we don't need to document a Windows command for activating a virtualenv. 11 | 12 | echo Executing .venv\Scripts\activate.bat for you 13 | .venv\Scripts\activate.bat 14 | -------------------------------------------------------------------------------- /107-Encrypting-EBS-Volumes-Using-KMS-Keys/README.md: -------------------------------------------------------------------------------- 1 | # Encrypting EBS Volumes Using KMS Keys 2 | 3 | ## Clean up 4 | 5 | ### Set the KMS Key used for EBS Encryption back to the 6 | ``` 7 | aws ec2 modify-ebs-default-kms-key-id \ 8 | --kms-key-id alias/aws/ebs 9 | ``` 10 | 11 | OR Disable default EBS Encryption: 12 | ``` 13 | aws ec2 disable-ebs-encryption-by-default 14 | ``` 15 | 16 | ### Disable the KMS Key: 17 | 18 | `aws kms disable-key --key-id $KMS_KEY_ID` 19 | 20 | ### Scheduled the KMS Key for deletion: 21 | ``` 22 | aws kms schedule-key-deletion \ 23 | --key-id $KMS_KEY_ID \ 24 | --pending-window-in-days 7 25 | ``` 26 | 27 | ### Delete the Key Alias: 28 | 29 | `aws kms delete-alias --alias-name alias/AWSCookbook107Key` 30 | 31 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/.gitignore: -------------------------------------------------------------------------------- 1 | secret-access-policy.json 2 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/README.md: -------------------------------------------------------------------------------- 1 | # Storing, Encrypting, and Accessing Passwords Using Secrets Manager 2 | ## Preparation 3 | ### This recipe requires some “prep work” which deploys resources that you’ll build the solution on. You will use the AWS CDK to deploy these resources 4 | 5 | ### In the root of this Chapter’s repo cd to the “108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108” directory and follow the subsequent steps: 6 | 7 | ``` 8 | cd 108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108 9 | test -d .venv || python3 -m venv .venv 10 | source .venv/bin/activate 11 | pip install --upgrade pip 12 | pip install -r requirements.txt 13 | cdk deploy 14 | ``` 15 | 16 | ### Wait for the cdk deploy command to complete. 17 | 18 | ### We created a helper.py script to let you easily create and export environment variables to make subsequent commands easier. Run the script, and copy the output to your terminal to export variables: 19 | 20 | `python helper.py` 21 | 22 | ### Navigate up to the main directory for this recipe (out of the “cdk-AWS-Cookbook-207” directory): 23 | 24 | `cd ..` 25 | 26 | 27 | ## Clean up 28 | 29 | ### Delete the Secret: 30 | 31 | `aws secretsmanager delete-secret --secret-id AWSCookbook108/Secret1 --recovery-window-in-days 7` 32 | 33 | ### Detach the IAM Policy that you created from the IAM Role associated with EC2 Instance: 34 | ``` 35 | aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSCookbook108SecretAccess 36 | ``` 37 | 38 | ### Delete the IAM Policy you created: 39 | ``` 40 | aws iam delete-policy --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AWSCookbook108SecretAccess 41 | ``` 42 | 43 | ### Go to the cdk-AWS-Cookbook-108 directory: 44 | 45 | `cd cdk-AWS-Cookbook-108/` 46 | 47 | ### To clean up the environment variables, run the helper.py script in this recipe’s cdk- directory with the --unset flag, and copy the output to your terminal to export variables: 48 | 49 | `python helper.py --unset` 50 | 51 | ### Unset your manually created environment variables: 52 | 53 | ``` 54 | unset RANDOM_STRING 55 | unset SECRET_ARN 56 | ``` 57 | 58 | ### Use the AWS CDK to destroy the resources, deactivate your Python virtual environment, and go to the root of the chapter: 59 | 60 | `cdk destroy && deactivate && rm -r .venv/ && cd ../..` 61 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | .venv 6 | *.egg-info 7 | 8 | # CDK asset staging directory 9 | .cdk.staging 10 | cdk.out 11 | secret-access-policy.json 12 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to your CDK Python project! 3 | 4 | This is a blank project for Python development with CDK. 5 | 6 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 7 | 8 | This project is set up like a standard Python project. The initialization 9 | process also creates a virtualenv within this project, stored under the `.venv` 10 | directory. To create the virtualenv it assumes that there is a `python3` 11 | (or `python` for Windows) executable in your path with access to the `venv` 12 | package. If for any reason the automatic creation of the virtualenv fails, 13 | you can create the virtualenv manually. 14 | 15 | To manually create a virtualenv on MacOS and Linux: 16 | 17 | ``` 18 | $ python3 -m venv .venv 19 | ``` 20 | 21 | After the init process completes and the virtualenv is created, you can use the following 22 | step to activate your virtualenv. 23 | 24 | ``` 25 | $ source .venv/bin/activate 26 | ``` 27 | 28 | If you are a Windows platform, you would activate the virtualenv like this: 29 | 30 | ``` 31 | % .venv\Scripts\activate.bat 32 | ``` 33 | 34 | Once the virtualenv is activated, you can install the required dependencies. 35 | 36 | ``` 37 | $ pip install -r requirements.txt 38 | ``` 39 | 40 | At this point you can now synthesize the CloudFormation template for this code. 41 | 42 | ``` 43 | $ cdk synth 44 | ``` 45 | 46 | To add additional dependencies, for example other CDK libraries, just add 47 | them to your `setup.py` file and rerun the `pip install -r requirements.txt` 48 | command. 49 | 50 | ## Useful commands 51 | 52 | * `cdk ls` list all stacks in the app 53 | * `cdk synth` emits the synthesized CloudFormation template 54 | * `cdk deploy` deploy this stack to your default AWS account/region 55 | * `cdk diff` compare deployed stack with current state 56 | * `cdk docs` open CDK documentation 57 | 58 | Enjoy! 59 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import aws_cdk as cdk 5 | 6 | from cdk_aws_cookbook_108.cdk_aws_cookbook_108_stack import CdkAwsCookbook108Stack 7 | 8 | 9 | app = cdk.App() 10 | CdkAwsCookbook108Stack(app, "cdk-aws-cookbook-108", 11 | # If you don't specify 'env', this stack will be environment-agnostic. 12 | # Account/Region-dependent features and context lookups will not work, 13 | # but a single synthesized template can be deployed anywhere. 14 | 15 | # Uncomment the next line to specialize this stack for the AWS Account 16 | # and Region that are implied by the current CLI configuration. 17 | 18 | #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), 19 | 20 | # Uncomment the next line if you know exactly what Account and Region you 21 | # want to deploy the stack to. */ 22 | 23 | # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html 24 | ) 25 | 26 | app.synth() 27 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:stackRelativeExports": "true", 6 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 7 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 8 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/cdk_aws_cookbook_108/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AWSCookbook/Security/3e29c8c94742b12aed1e1acde333822510e288fd/108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/cdk_aws_cookbook_108/__init__.py -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/cdk_aws_cookbook_108/cdk_aws_cookbook_108_stack.py: -------------------------------------------------------------------------------- 1 | from constructs import Construct 2 | from aws_cdk import ( 3 | aws_ec2 as ec2, 4 | aws_iam as iam, 5 | Stack, 6 | CfnOutput, 7 | RemovalPolicy 8 | ) 9 | 10 | 11 | class CdkAwsCookbook108Stack(Stack): 12 | 13 | def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: 14 | super().__init__(scope, construct_id, **kwargs) 15 | 16 | isolated_subnets = ec2.SubnetConfiguration( 17 | name="ISOLATED", 18 | subnet_type=ec2.SubnetType.ISOLATED, 19 | cidr_mask=24 20 | ) 21 | 22 | # create VPC 23 | vpc = ec2.Vpc( 24 | self, 25 | 'AWS-Cookbook-VPC-108', 26 | cidr='10.10.0.0/23', 27 | subnet_configuration=[isolated_subnets] 28 | ) 29 | 30 | vpc.add_interface_endpoint( 31 | 'VPCSecretsManagerInterfaceEndpoint', 32 | service=ec2.InterfaceVpcEndpointAwsService('secretsmanager'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 33 | private_dns_enabled=True, 34 | subnets=ec2.SubnetSelection( 35 | one_per_az=False, 36 | subnet_type=ec2.SubnetType.ISOLATED 37 | ), 38 | ) 39 | 40 | # -------- Begin EC2 Helper --------- 41 | vpc.add_interface_endpoint( 42 | 'VPCSSMInterfaceEndpoint', 43 | service=ec2.InterfaceVpcEndpointAwsService('ssm'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 44 | private_dns_enabled=True, 45 | subnets=ec2.SubnetSelection( 46 | one_per_az=False, 47 | subnet_type=ec2.SubnetType.ISOLATED 48 | ), 49 | ) 50 | 51 | vpc.add_interface_endpoint( 52 | 'VPCEC2MessagesInterfaceEndpoint', 53 | service=ec2.InterfaceVpcEndpointAwsService('ec2messages'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 54 | private_dns_enabled=True, 55 | subnets=ec2.SubnetSelection( 56 | one_per_az=False, 57 | subnet_type=ec2.SubnetType.ISOLATED 58 | ), 59 | ) 60 | 61 | vpc.add_interface_endpoint( 62 | 'VPCSSMMessagesInterfaceEndpoint', 63 | service=ec2.InterfaceVpcEndpointAwsService('ssmmessages'), # Find names with - aws ec2 describe-vpc-endpoint-services | jq '.ServiceNames' 64 | private_dns_enabled=True, 65 | subnets=ec2.SubnetSelection( 66 | one_per_az=False, 67 | subnet_type=ec2.SubnetType.ISOLATED 68 | ), 69 | ) 70 | 71 | ami = ec2.MachineImage.latest_amazon_linux( 72 | generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, 73 | edition=ec2.AmazonLinuxEdition.STANDARD, 74 | virtualization=ec2.AmazonLinuxVirt.HVM, 75 | storage=ec2.AmazonLinuxStorage.GENERAL_PURPOSE 76 | ) 77 | user_data = ec2.UserData.for_linux() 78 | # user_data.add_commands('sudo yum -y update', 79 | # 'sudo yum install -y httpd', 80 | # 'sudo systemctl start httpd') 81 | 82 | iam_role = iam.Role(self, "InstanceSSM", assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")) 83 | 84 | iam_role.add_managed_policy(iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore")) 85 | 86 | 87 | instance = ec2.Instance( 88 | self, 89 | "Instance", 90 | instance_type=ec2.InstanceType("t3.nano"), 91 | machine_image=ami, 92 | user_data=user_data, 93 | role=iam_role, 94 | vpc=vpc, 95 | ) 96 | 97 | CfnOutput( 98 | self, 99 | 'InstanceId', 100 | value=instance.instance_id 101 | ) 102 | # -------- End EC2 Helper --------- 103 | 104 | # outputs 105 | 106 | CfnOutput( 107 | self, 108 | 'RoleName', 109 | value=iam_role.role_name 110 | ) 111 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import argparse 4 | 5 | 6 | def change_case(str): 7 | res = [str[0]] 8 | for c in str[1:]: 9 | if c in ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'): 10 | res.append('_') 11 | res.append(c) 12 | elif c in ('123456789'): 13 | res.append('_') 14 | res.append(c) 15 | else: 16 | res.append(c.upper()) 17 | 18 | return ''.join(res) 19 | 20 | 21 | parser = argparse.ArgumentParser(description="Generate commands to set and unset environment variables") 22 | parser.add_argument('--unset', action='store_true', help="Generate commands to unset environment variables by setting this flag") 23 | 24 | args = parser.parse_args() 25 | 26 | os.environ['AWS_DEFAULT_REGION'] = os.environ.get('AWS_REGION') 27 | 28 | cfn = boto3.client('cloudformation') 29 | stackname = os.path.basename(os.getcwd()).lower() 30 | response = cfn.describe_stacks(StackName=stackname) 31 | unsets = [] 32 | sets = [] 33 | 34 | outputs = response["Stacks"][0]["Outputs"] 35 | print("Copy and paste the commands below into your terminal") 36 | print("") 37 | for output in outputs: 38 | if ', ' in output["OutputValue"]: 39 | sets.append(change_case(output["OutputKey"]) + "='" + ', '.join('"{}"'.format(word) for word in output["OutputValue"].split(", ")) + "'") 40 | else: 41 | sets.append(change_case(output["OutputKey"]) + "='" + output["OutputValue"] + "'") 42 | unsets.append("unset " + change_case(output["OutputKey"])) 43 | 44 | if (args.unset): 45 | print('\n'.join(map(str, unsets))) 46 | else: 47 | print('\n'.join(map(str, sets))) 48 | 49 | print("") 50 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | boto3 -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | 4 | with open("README.md") as fp: 5 | long_description = fp.read() 6 | 7 | 8 | setuptools.setup( 9 | name="cdk_aws_cookbook_108", 10 | version="0.0.1", 11 | 12 | description="An empty CDK Python app", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | 16 | author="author", 17 | 18 | package_dir={"": "cdk_aws_cookbook_108"}, 19 | packages=setuptools.find_packages(where="cdk_aws_cookbook_108"), 20 | 21 | install_requires=[ 22 | "aws-cdk-lib==2.0.0-rc.21", 23 | "constructs>=10.0.0,<11.0.0", 24 | ], 25 | 26 | python_requires=">=3.6", 27 | 28 | classifiers=[ 29 | "Development Status :: 4 - Beta", 30 | 31 | "Intended Audience :: Developers", 32 | 33 | "Programming Language :: JavaScript", 34 | "Programming Language :: Python :: 3 :: Only", 35 | "Programming Language :: Python :: 3.6", 36 | "Programming Language :: Python :: 3.7", 37 | "Programming Language :: Python :: 3.8", 38 | 39 | "Topic :: Software Development :: Code Generators", 40 | "Topic :: Utilities", 41 | 42 | "Typing :: Typed", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/cdk-AWS-Cookbook-108/source.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem The sole purpose of this script is to make the command 4 | rem 5 | rem source .venv/bin/activate 6 | rem 7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. 8 | rem On Windows, this command just runs this batch file (the argument is ignored). 9 | rem 10 | rem Now we don't need to document a Windows command for activating a virtualenv. 11 | 12 | echo Executing .venv\Scripts\activate.bat for you 13 | .venv\Scripts\activate.bat 14 | -------------------------------------------------------------------------------- /108-Storing-Encrypting-Accessing-Passwords/secret-access-policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "secretsmanager:GetResourcePolicy", 8 | "secretsmanager:GetSecretValue", 9 | "secretsmanager:DescribeSecret", 10 | "secretsmanager:ListSecretVersionIds" 11 | ], 12 | "Resource": [ 13 | "SECRET_ARN" 14 | ] 15 | }, 16 | { 17 | "Effect": "Allow", 18 | "Action": "secretsmanager:ListSecrets", 19 | "Resource": "*" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /109-Blocking-Public-Access-for-S3-Buckets/.gitignore: -------------------------------------------------------------------------------- 1 | public-read.json -------------------------------------------------------------------------------- /109-Blocking-Public-Access-for-S3-Buckets/README.md: -------------------------------------------------------------------------------- 1 | # Blocking Public Access for an S3 Bucket 2 | ## Preparation 3 | ### Set a unique suffix to use for the S3 BucketName: 4 | ``` 5 | RANDOM_STRING=$(aws secretsmanager get-random-password \ 6 | --exclude-punctuation --exclude-uppercase \ 7 | --password-length 6 --require-each-included-type \ 8 | --output text \ 9 | --query RandomPassword) 10 | ``` 11 | 12 | ### Create a S3 Bucket: 13 | ``` 14 | aws s3api create-bucket --bucket awscookbook109-$RANDOM_STRING 15 | ``` 16 | 17 | ### Apply a bucket policy that grants public read to objects in your bucket by following the next steps. 18 | 19 | ### Create a file named public-read-template.json to create a bucket policy that you will apply to your S3 bucket (File provided in this chapter’s repo): 20 | ``` 21 | { 22 | "Version":"2012-10-17", 23 | "Statement":[ 24 | { 25 | "Sid":"PublicRead", 26 | "Effect":"Allow", 27 | "Principal": "*", 28 | "Action":["s3:GetObject","s3:GetObjectVersion"], 29 | "Resource":["arn:aws:s3:::BUCKET_NAME/*"] 30 | } 31 | ] 32 | } 33 | ``` 34 | 35 | ### Use the sed command to replace the values in public-read-template.json: 36 | ``` 37 | sed -e "s/BUCKET_NAME/awscookbook109-$RANDOM_STRING/g" \ 38 | public-read-template.json > public-read.json 39 | ``` 40 | 41 | ### Add the S3 Bucket Policy to your S3 bucket: 42 | ``` 43 | aws s3api put-bucket-policy \ 44 | --bucket awscookbook109-$RANDOM_STRING \ 45 | --policy file://public-read.json 46 | ``` 47 | 48 | 49 | ## Clean up 50 | ### Delete the S3 bucket: 51 | ``` 52 | aws s3api delete-bucket --bucket awscookbook109-$RANDOM_STRING 53 | ``` 54 | 55 | ### Delete the Access Analyzer: 56 | ``` 57 | aws accessanalyzer delete-analyzer --analyzer-name awscookbook109 58 | ``` 59 | 60 | ### Unset the environment variables that you created manually: 61 | ``` 62 | unset RANDOM_STRING 63 | unset ANALYZER_ARN 64 | ``` 65 | 66 | 67 | -------------------------------------------------------------------------------- /109-Blocking-Public-Access-for-S3-Buckets/public-read-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version":"2012-10-17", 3 | "Statement":[ 4 | { 5 | "Sid":"PublicRead", 6 | "Effect":"Allow", 7 | "Principal": "*", 8 | "Action":["s3:GetObject","s3:GetObjectVersion"], 9 | "Resource":["arn:aws:s3:::BUCKET_NAME/*"] 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /110-Serving-Web-Content-Securely-from-S3-with-CloudFront/.gitignore: -------------------------------------------------------------------------------- 1 | bucket-policy.json 2 | distribution.json 3 | index.html -------------------------------------------------------------------------------- /110-Serving-Web-Content-Securely-from-S3-with-CloudFront/README.md: -------------------------------------------------------------------------------- 1 | # Serving Web Content Securely from S3 with CloudFront 2 | ## Preparation 3 | ### Create an index.html file 4 | ``` 5 | echo AWSCookbook > index.html 6 | ``` 7 | 8 | ### Set a unique suffix to use for the S3 bucket name: 9 | ``` 10 | RANDOM_STRING=$(aws secretsmanager get-random-password \ 11 | --exclude-punctuation --exclude-uppercase \ 12 | --password-length 6 --require-each-included-type \ 13 | --output text \ 14 | --query RandomPassword) 15 | ``` 16 | 17 | ### Create a S3 bucket: 18 | ``` 19 | aws s3api create-bucket --bucket awscookbook110-$RANDOM_STRING 20 | ``` 21 | 22 | ### Copy the previously created file to the bucket: 23 | ``` 24 | aws s3 cp index.html s3://awscookbook110-$RANDOM_STRING/ 25 | ``` 26 | 27 | ### Set the public access block for your bucket: 28 | ``` 29 | aws s3api put-public-access-block \ 30 | --bucket awscookbook110-$RANDOM_STRING \ 31 | --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" 32 | ``` 33 | 34 | ## Clean up 35 | ### Disable the CloudFront distribution by logging into the console and clicking the Disable button for the distribution that you created (this process can take up to 15 minutes) 36 | 37 | ### Delete the CloudFront distribution: 38 | ``` 39 | aws cloudfront delete-distribution --id $DISTRIBUTION_ID --if-match $(aws cloudfront get-distribution --id $DISTRIBUTION_ID --query ETag --output text) 40 | ``` 41 | 42 | ### Delete the Origin Access Identity: 43 | ``` 44 | aws cloudfront delete-cloud-front-origin-access-identity --id $OAI --if-match $(aws cloudfront get-cloud-front-origin-access-identity --id $OAI --query ETag --output text) 45 | ``` 46 | 47 | ### Clean up the bucket: 48 | ``` 49 | aws s3 rm s3://awscookbook110-$RANDOM_STRING/index.html 50 | ``` 51 | 52 | ### Delete the S3 bucket: 53 | ``` 54 | aws s3api delete-bucket --bucket awscookbook110-$RANDOM_STRING 55 | ``` 56 | 57 | Unset your manually created environment variables: 58 | ``` 59 | unset DISTRIBUTION_ID 60 | unset DOMAIN_NAME 61 | unset OAI 62 | unset RANDOM_STRING 63 | unset BUCKET_NAME 64 | ``` 65 | -------------------------------------------------------------------------------- /110-Serving-Web-Content-Securely-from-S3-with-CloudFront/bucket-policy-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Id": "PolicyForCloudFrontPrivateContent", 4 | "Statement": [ 5 | { 6 | "Effect": "Allow", 7 | "Principal": { 8 | "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity CLOUDFRONT_OAI" 9 | }, 10 | "Action": "s3:GetObject", 11 | "Resource": "arn:aws:s3:::S3_BUCKET_NAME/*" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /110-Serving-Web-Content-Securely-from-S3-with-CloudFront/distribution-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "awscookbook template", 3 | "CacheBehaviors": { 4 | "Quantity": 0 5 | }, 6 | "DefaultRootObject": "index.html", 7 | "Origins": { 8 | "Items": [ 9 | { 10 | "S3OriginConfig": { 11 | "OriginAccessIdentity": "origin-access-identity/cloudfront/CLOUDFRONT_OAI" 12 | }, 13 | "Id": "S3-origin", 14 | "DomainName": "S3_BUCKET_NAME.s3.amazonaws.com" 15 | } 16 | ], 17 | "Quantity": 1 18 | }, 19 | "PriceClass": "PriceClass_All", 20 | "Enabled": true, 21 | "DefaultCacheBehavior": { 22 | "TrustedSigners": { 23 | "Enabled": false, 24 | "Quantity": 0 25 | }, 26 | "TargetOriginId": "S3-origin", 27 | "ViewerProtocolPolicy": "allow-all", 28 | "ForwardedValues": { 29 | "Headers": { 30 | "Quantity": 0 31 | }, 32 | "Cookies": { 33 | "Forward": "none" 34 | }, 35 | "QueryString": false 36 | }, 37 | "SmoothStreaming": false, 38 | "AllowedMethods": { 39 | "Items": [ 40 | "GET", 41 | "HEAD" 42 | ], 43 | "Quantity": 2 44 | }, 45 | "MinTTL": 0 46 | }, 47 | "CallerReference": "example", 48 | "ViewerCertificate": { 49 | "CloudFrontDefaultCertificate": true 50 | }, 51 | "CustomErrorResponses": { 52 | "Quantity": 0 53 | }, 54 | "Restrictions": { 55 | "GeoRestriction": { 56 | "RestrictionType": "none", 57 | "Quantity": 0 58 | } 59 | }, 60 | "Aliases": { 61 | "Quantity": 0 62 | } 63 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 John Culkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 - Security 2 | ## Set and export your default region: 3 | 4 | ``` 5 | export AWS_REGION=us-east-1 6 | ``` 7 | 8 | ## Set your AWS ACCOUNT ID:: 9 | 10 | ``` 11 | AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) 12 | ``` 13 | 14 | ## Validate AWS Cli Setup and access: 15 | 16 | ``` 17 | aws ec2 describe-instances 18 | ``` 19 | --------------------------------------------------------------------------------