|
6 | 6 | action: |
7 | 7 | description: 'Action to perform' |
8 | 8 | required: true |
9 | | - default: 'create-with-console' |
| 9 | + default: 'plan' |
10 | 10 | type: choice |
11 | 11 | options: |
12 | | - - create-with-console |
13 | | - - make-public |
14 | | - - make-private |
15 | | - - delete |
| 12 | + - plan |
| 13 | + - apply |
| 14 | + - show-creds |
| 15 | + - destroy |
16 | 16 | encryption_key: |
17 | | - description: 'Passphrase to encrypt credentials (required for create)' |
18 | | - required: false |
19 | | - type: string |
20 | | - console_password: |
21 | | - description: 'Console login password for admin user' |
| 17 | + description: 'Passphrase to encrypt credentials (for show-creds)' |
22 | 18 | required: false |
23 | 19 | type: string |
24 | 20 |
|
25 | 21 | env: |
26 | 22 | AWS_REGION: us-east-1 |
27 | 23 | S3_BUCKET: infosec-mgr-backups-866795125297 |
28 | 24 | S3_KEY: gpu-credentials/all-credentials.enc |
29 | | - SCOPED_USER: vector-traversal-gpu |
30 | | - ADMIN_USER: vector-traversal-admin |
31 | 25 |
|
32 | 26 | jobs: |
33 | | - manage-users: |
| 27 | + terraform: |
34 | 28 | name: ${{ inputs.action }} |
35 | 29 | runs-on: ubuntu-latest |
| 30 | + defaults: |
| 31 | + run: |
| 32 | + working-directory: terraform |
36 | 33 | steps: |
| 34 | + - name: Checkout |
| 35 | + uses: actions/checkout@v4 |
| 36 | + |
37 | 37 | - name: Configure AWS credentials |
38 | 38 | uses: aws-actions/configure-aws-credentials@v4 |
39 | 39 | with: |
40 | 40 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} |
41 | 41 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} |
42 | 42 | aws-region: us-east-1 |
43 | 43 |
|
44 | | - - name: Create users with console access |
45 | | - if: inputs.action == 'create-with-console' |
| 44 | + - name: Setup Terraform |
| 45 | + uses: hashicorp/setup-terraform@v3 |
| 46 | + with: |
| 47 | + terraform_wrapper: false |
| 48 | + |
| 49 | + - name: Terraform Init |
| 50 | + run: terraform init |
| 51 | + |
| 52 | + - name: Import existing resources |
| 53 | + if: inputs.action == 'plan' || inputs.action == 'apply' |
| 54 | + run: | |
| 55 | + # Import any existing resources so terraform doesn't try to recreate them |
| 56 | + # These may or may not exist from prior workflow runs |
| 57 | + terraform import aws_iam_user.gpu_scoped vector-traversal-gpu 2>/dev/null || true |
| 58 | + terraform import aws_iam_user.gpu_admin vector-traversal-admin 2>/dev/null || true |
| 59 | + # Access keys and policies can't be easily imported, terraform will recreate them |
| 60 | +
|
| 61 | + - name: Terraform Plan |
| 62 | + if: inputs.action == 'plan' || inputs.action == 'apply' |
| 63 | + run: | |
| 64 | + terraform plan \ |
| 65 | + -target=aws_iam_user.gpu_scoped \ |
| 66 | + -target=aws_iam_access_key.gpu_scoped \ |
| 67 | + -target=aws_iam_user_policy.gpu_scoped \ |
| 68 | + -target=aws_iam_user.gpu_admin \ |
| 69 | + -target=aws_iam_access_key.gpu_admin \ |
| 70 | + -target=aws_iam_user_policy_attachment.gpu_admin |
| 71 | +
|
| 72 | + - name: Terraform Apply |
| 73 | + if: inputs.action == 'apply' |
| 74 | + run: | |
| 75 | + terraform apply -auto-approve \ |
| 76 | + -target=aws_iam_user.gpu_scoped \ |
| 77 | + -target=aws_iam_access_key.gpu_scoped \ |
| 78 | + -target=aws_iam_user_policy.gpu_scoped \ |
| 79 | + -target=aws_iam_user.gpu_admin \ |
| 80 | + -target=aws_iam_access_key.gpu_admin \ |
| 81 | + -target=aws_iam_user_policy_attachment.gpu_admin |
| 82 | +
|
| 83 | + echo "" |
| 84 | + echo "=== Apply complete. Run 'show-creds' to export credentials ===" |
| 85 | +
|
| 86 | + - name: Export credentials |
| 87 | + if: inputs.action == 'show-creds' |
46 | 88 | env: |
47 | 89 | ENCRYPTION_KEY: ${{ inputs.encryption_key }} |
48 | | - CONSOLE_PASSWORD: ${{ inputs.console_password }} |
49 | 90 | run: | |
50 | | - set -euo pipefail |
51 | 91 | ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) |
52 | 92 |
|
53 | | - echo "=== Ensuring users exist ===" |
54 | | - aws iam create-user --user-name "${SCOPED_USER}" 2>/dev/null || echo "${SCOPED_USER} exists" |
55 | | - aws iam create-user --user-name "${ADMIN_USER}" 2>/dev/null || echo "${ADMIN_USER} exists" |
56 | | -
|
57 | | - echo "=== Attaching policies ===" |
58 | | - # Scoped user policy |
59 | | - aws iam put-user-policy \ |
60 | | - --user-name "${SCOPED_USER}" \ |
61 | | - --policy-name "gpu-experiment-policy" \ |
62 | | - --policy-document '{ |
63 | | - "Version": "2012-10-17", |
64 | | - "Statement": [ |
65 | | - { |
66 | | - "Sid": "EC2ForGPU", |
67 | | - "Effect": "Allow", |
68 | | - "Action": [ |
69 | | - "ec2:RunInstances", "ec2:TerminateInstances", |
70 | | - "ec2:DescribeInstances", "ec2:DescribeInstanceTypes", |
71 | | - "ec2:DescribeImages", "ec2:DescribeKeyPairs", |
72 | | - "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", |
73 | | - "ec2:DescribeVpcs", "ec2:CreateKeyPair", "ec2:DeleteKeyPair", |
74 | | - "ec2:CreateSecurityGroup", "ec2:DeleteSecurityGroup", |
75 | | - "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress", |
76 | | - "ec2:CreateTags", "ec2:DescribeAvailabilityZones", |
77 | | - "ec2:DescribeInstanceStatus" |
78 | | - ], |
79 | | - "Resource": "*" |
80 | | - }, |
81 | | - { |
82 | | - "Sid": "S3ForResults", |
83 | | - "Effect": "Allow", |
84 | | - "Action": ["s3:PutObject", "s3:GetObject", "s3:ListBucket", "s3:DeleteObject"], |
85 | | - "Resource": ["arn:aws:s3:::vector-traversal-*", "arn:aws:s3:::vector-traversal-*/*"] |
86 | | - }, |
87 | | - { |
88 | | - "Sid": "S3CreateBucket", |
89 | | - "Effect": "Allow", |
90 | | - "Action": ["s3:CreateBucket", "s3:PutBucketPolicy", "s3:PutBucketTagging"], |
91 | | - "Resource": "arn:aws:s3:::vector-traversal-*" |
92 | | - }, |
93 | | - { |
94 | | - "Sid": "IAMPassRole", |
95 | | - "Effect": "Allow", |
96 | | - "Action": "iam:PassRole", |
97 | | - "Resource": "*", |
98 | | - "Condition": {"StringEquals": {"iam:PassedToService": "ec2.amazonaws.com"}} |
99 | | - } |
100 | | - ] |
101 | | - }' |
102 | | -
|
103 | | - # Admin user: full admin |
104 | | - aws iam attach-user-policy \ |
105 | | - --user-name "${ADMIN_USER}" \ |
106 | | - --policy-arn "arn:aws:iam::aws:policy/AdministratorAccess" 2>/dev/null || echo "Already attached" |
107 | | -
|
108 | | - echo "=== Creating console login for admin user ===" |
109 | | - # Delete existing login profile if any |
110 | | - aws iam delete-login-profile --user-name "${ADMIN_USER}" 2>/dev/null || true |
111 | | -
|
112 | | - # Create console login |
113 | | - aws iam create-login-profile \ |
114 | | - --user-name "${ADMIN_USER}" \ |
115 | | - --password "${CONSOLE_PASSWORD}" \ |
116 | | - --no-password-reset-required |
| 93 | + SCOPED_AK=$(terraform output -raw gpu_scoped_access_key_id 2>/dev/null || echo "NOT_FOUND") |
| 94 | + SCOPED_SK=$(terraform output -raw gpu_scoped_secret_key 2>/dev/null || echo "NOT_FOUND") |
| 95 | + ADMIN_AK=$(terraform output -raw gpu_admin_access_key_id 2>/dev/null || echo "NOT_FOUND") |
| 96 | + ADMIN_SK=$(terraform output -raw gpu_admin_secret_key 2>/dev/null || echo "NOT_FOUND") |
117 | 97 |
|
118 | | - echo "" |
119 | | - echo "============================================================" |
120 | | - echo "=== SETUP COMPLETE ===" |
121 | | - echo "============================================================" |
122 | | - echo "" |
123 | | - echo "Users created:" |
124 | | - echo " - ${SCOPED_USER} (EC2 + S3 policy attached, no keys yet)" |
125 | | - echo " - ${ADMIN_USER} (AdministratorAccess, console login enabled)" |
126 | | - echo "" |
127 | | - echo "=== NEXT STEPS ===" |
128 | | - echo "" |
129 | | - echo "1. Log into AWS Console:" |
130 | | - echo " URL: https://${ACCOUNT_ID}.signin.aws.amazon.com/console" |
131 | | - echo " Username: ${ADMIN_USER}" |
132 | | - echo " Password: (the one you provided)" |
133 | | - echo "" |
134 | | - echo "2. Once logged in, go to IAM > Users > each user > Security Credentials" |
135 | | - echo " Create Access Keys for BOTH users" |
136 | | - echo "" |
137 | | - echo "3. Save all credentials to Bitwarden" |
138 | | - echo "" |
139 | | - echo "The admin user has full permissions to create access keys" |
140 | | - echo "for itself and for the scoped GPU user." |
141 | | - echo "============================================================" |
| 98 | + if [ "${SCOPED_AK}" = "NOT_FOUND" ] || [ "${ADMIN_AK}" = "NOT_FOUND" ]; then |
| 99 | + echo "ERROR: Credentials not found in terraform state." |
| 100 | + echo "Run 'apply' first to create the resources." |
| 101 | + exit 1 |
| 102 | + fi |
142 | 103 |
|
143 | | - - name: Make public |
144 | | - if: inputs.action == 'make-public' |
145 | | - run: | |
146 | | - aws s3api put-object-acl --bucket "${S3_BUCKET}" --key "${S3_KEY}" --acl public-read |
147 | | - echo "https://${S3_BUCKET}.s3.amazonaws.com/${S3_KEY}" |
| 104 | + cat > /tmp/creds.txt << EOF |
| 105 | + # AWS Credentials — Account ${ACCOUNT_ID} (us-east-1) |
| 106 | + # Created: $(date -u +%Y-%m-%dT%H:%M:%SZ) |
148 | 107 |
|
149 | | - - name: Make private |
150 | | - if: inputs.action == 'make-private' |
151 | | - run: | |
152 | | - aws s3api put-object-acl --bucket "${S3_BUCKET}" --key "${S3_KEY}" --acl private |
153 | | - echo "=== Private ===" |
| 108 | + [vector-traversal-gpu] |
| 109 | + # Scoped: EC2 + S3 (vector-traversal-* buckets) |
| 110 | + aws_access_key_id = ${SCOPED_AK} |
| 111 | + aws_secret_access_key = ${SCOPED_SK} |
| 112 | + region = us-east-1 |
154 | 113 |
|
155 | | - - name: Delete users |
156 | | - if: inputs.action == 'delete' |
| 114 | + [vector-traversal-admin] |
| 115 | + # FULL ADMIN — AdministratorAccess |
| 116 | + aws_access_key_id = ${ADMIN_AK} |
| 117 | + aws_secret_access_key = ${ADMIN_SK} |
| 118 | + region = us-east-1 |
| 119 | +
|
| 120 | + [infosec-deployer] |
| 121 | + # GitHub Actions deployer (from repo secrets) |
| 122 | + aws_access_key_id = ${AWS_ACCESS_KEY_ID} |
| 123 | + aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY} |
| 124 | + region = us-east-1 |
| 125 | + EOF |
| 126 | +
|
| 127 | + openssl enc -aes-256-cbc -pbkdf2 -iter 100000 \ |
| 128 | + -in /tmp/creds.txt -out /tmp/creds.enc \ |
| 129 | + -pass "pass:${ENCRYPTION_KEY}" |
| 130 | +
|
| 131 | + aws s3 cp /tmp/creds.enc "s3://${S3_BUCKET}/${S3_KEY}" |
| 132 | + rm -f /tmp/creds.txt /tmp/creds.enc |
| 133 | +
|
| 134 | + echo "Encrypted credentials uploaded to s3://${S3_BUCKET}/${S3_KEY}" |
| 135 | + echo "Run 'make-public' to download (TODO: add that action)" |
| 136 | +
|
| 137 | + - name: Terraform Destroy |
| 138 | + if: inputs.action == 'destroy' |
157 | 139 | run: | |
158 | | - for USERNAME in "${SCOPED_USER}" "${ADMIN_USER}"; do |
159 | | - echo "Deleting ${USERNAME}..." |
160 | | - for KEY_ID in $(aws iam list-access-keys --user-name "${USERNAME}" \ |
161 | | - --query 'AccessKeyMetadata[].AccessKeyId' --output text 2>/dev/null || true); do |
162 | | - aws iam delete-access-key --user-name "${USERNAME}" --access-key-id "${KEY_ID}" |
163 | | - done |
164 | | - for POLICY in $(aws iam list-user-policies --user-name "${USERNAME}" \ |
165 | | - --query 'PolicyNames[]' --output text 2>/dev/null || true); do |
166 | | - aws iam delete-user-policy --user-name "${USERNAME}" --policy-name "${POLICY}" |
167 | | - done |
168 | | - for POLICY_ARN in $(aws iam list-attached-user-policies --user-name "${USERNAME}" \ |
169 | | - --query 'AttachedPolicies[].PolicyArn' --output text 2>/dev/null || true); do |
170 | | - aws iam detach-user-policy --user-name "${USERNAME}" --policy-arn "${POLICY_ARN}" |
171 | | - done |
172 | | - aws iam delete-login-profile --user-name "${USERNAME}" 2>/dev/null || true |
173 | | - aws iam delete-user --user-name "${USERNAME}" 2>/dev/null || true |
174 | | - echo " Done" |
175 | | - done |
| 140 | + terraform import aws_iam_user.gpu_scoped vector-traversal-gpu 2>/dev/null || true |
| 141 | + terraform import aws_iam_user.gpu_admin vector-traversal-admin 2>/dev/null || true |
| 142 | +
|
| 143 | + terraform destroy -auto-approve \ |
| 144 | + -target=aws_iam_access_key.gpu_scoped \ |
| 145 | + -target=aws_iam_user_policy.gpu_scoped \ |
| 146 | + -target=aws_iam_access_key.gpu_admin \ |
| 147 | + -target=aws_iam_user_policy_attachment.gpu_admin \ |
| 148 | + -target=aws_iam_user.gpu_scoped \ |
| 149 | + -target=aws_iam_user.gpu_admin |
| 150 | +
|
176 | 151 | aws s3 rm "s3://${S3_BUCKET}/${S3_KEY}" 2>/dev/null || true |
| 152 | + echo "=== Cleanup complete ===" |
0 commit comments