Skip to content

Commit 3ea73a0

Browse files
committed
feat(gateway): improve security and documentation
- Add server-side encryption for S3 bucket - Block public access to bucket - Add KMS key for API key encryption - Add encrypted API key management
1 parent cb5ee29 commit 3ea73a0

File tree

6 files changed

+372
-11
lines changed

6 files changed

+372
-11
lines changed

gateway/README.md

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ The Gateway is an optional but recommended component for scenarios requiring sec
88

99
## Features
1010

11-
- **Secure Request Forwarding**: Manages access to the Core components `/files/structure` and `/files/content` endpoints using API key validation.
12-
- **Dynamic ngrok URL Management**: Synchronizes the Cores ngrok URLs dynamically using S3, allowing seamless updates without downtime.
11+
- **Secure Request Forwarding**: Manages access to the Core component's `/files/structure` and `/files/content` endpoints using API key validation.
12+
- **Dynamic ngrok URL Management**: Synchronizes the Core's ngrok URLs dynamically using S3, allowing seamless updates without downtime.
1313
- **API Key Authentication**: Ensures secure access to all endpoints using predefined API keys.
1414
- **Infrastructure as Code**: Fully deployable via Terraform, with modular configurations for EC2, S3, and Load Balancer setups.
15+
- **Enhanced Security**: Server-side encryption for all S3 objects using AWS KMS, bucket-level encryption, and public access blocking.
16+
- **API Key Management**: Secure storage and management of API keys using AWS KMS for encryption.
1517

1618
## Prerequisites
1719

1820
- Python 3.8+
19-
- AWS account with permissions for EC2, S3, and Route 53
21+
- AWS account with permissions for EC2, S3, KMS, and Route 53
2022
- Terraform installed locally
2123
- A configured `.env` file with relevant AWS and application variables
2224

@@ -42,9 +44,17 @@ API_KEYS=test-key,other-valid-key,O8i5EVRqYI+0OGjPgoXI5Ey2CQzfJ+uIyI7e7yn8j0A=
4244
EC2_USER="ec2-user"
4345
EC2_HOST="<Your-EC2-Host-URL>" # Replace with your EC2 public DNS or IP
4446
KEY_PATH="./secrets/codequery-keypair.pem" # Path to your SSH private key file
47+
48+
# AWS Configuration
49+
AWS_REGION="sa-east-1" # Your AWS region
50+
KMS_KEY_ID="arn:aws:kms:sa-east-1:YOUR_ACCOUNT_ID:key/YOUR_KEY_ID" # Your KMS key ARN
4551
```
4652

47-
**Important**: Replace `<Your-EC2-Host-URL>` with your actual EC2 instance’s public DNS (e.g., `ec2-xx-xx-xxx-xxx.region-id.compute.amazonaws.com`). Ensure the `KEY_PATH` points to your SSH private key file used for connecting to the EC2 instance.
53+
**Important**: Replace the placeholders with your actual values:
54+
55+
- `<Your-EC2-Host-URL>`: Your EC2 instance's public DNS (e.g., `ec2-xx-xx-xxx-xxx.region-id.compute.amazonaws.com`)
56+
- `YOUR_ACCOUNT_ID`: Your AWS account ID
57+
- `YOUR_KEY_ID`: The ID of your KMS key
4858

4959
### 3. Install Dependencies
5060

@@ -67,7 +77,29 @@ terraform plan
6777
terraform apply
6878
```
6979

70-
This will provision the EC2 instance, security groups, and other necessary resources.
80+
This will provision:
81+
82+
- EC2 instance with necessary IAM roles
83+
- S3 bucket with server-side encryption and public access blocking
84+
- KMS key for encrypting API keys and other sensitive data
85+
- Security groups and other required resources
86+
87+
### 5. API Key Management
88+
89+
The Gateway provides a secure way to manage API keys using the `manage_api_keys.sh` script:
90+
91+
```bash
92+
# Add a new API key
93+
./scripts/manage_api_keys.sh add "your-api-key" "User1"
94+
95+
# List all API keys (usernames only)
96+
./scripts/manage_api_keys.sh list
97+
98+
# Remove an API key
99+
./scripts/manage_api_keys.sh remove "your-api-key"
100+
```
101+
102+
All API keys are stored in S3 with server-side encryption using AWS KMS.
71103

72104
### 5. Start the Application Locally (Optional)
73105

@@ -273,7 +305,7 @@ This ensures the Gateway application is correctly deployed, managed, and monitor
273305
## Troubleshooting
274306

275307
- **API Key Issues**: Ensure that the API key is correctly set in your environment variables and the `.env` file on the EC2 instance.
276-
- **ngrok URL Mismatch**: If the Cores ngrok URL is not synchronizing correctly, check the S3 bucket for the current values and verify that the Gateway exposed URL is configured properly, and correctly set in `GATEWAY_BASE_URL` (Core-side variable).
308+
- **ngrok URL Mismatch**: If the Core's ngrok URL is not synchronizing correctly, check the S3 bucket for the current values and verify that the Gateway exposed URL is configured properly, and correctly set in `GATEWAY_BASE_URL` (Core-side variable).
277309
- **Permission Errors**: Verify that the EC2 instance and S3 bucket have the correct IAM roles and permissions.
278310

279311
## License

gateway/TECH_DEBT.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Technical Debt
2+
3+
## S3 and Security Improvements
4+
5+
### High Priority
6+
7+
1. ~~**S3 Bucket Security**~~
8+
- ~~Add server-side encryption for all S3 objects using existing KMS key~~
9+
- ~~Configure bucket-level encryption~~
10+
- ~~Add public access blocking~~
11+
- ~~Enforce SSL/TLS for S3 access~~
12+
13+
### Medium Priority
14+
15+
1. **IAM and KMS**
16+
- Review and tighten KMS key policy
17+
- Consider using AWS Secrets Manager for API key storage
18+
- Implement principle of least privilege for IAM roles
19+
- Add automated key rotation for KMS keys
20+
21+
### Low Priority
22+
23+
1. **Infrastructure**
24+
25+
- Add tags for better resource management
26+
- Implement backup strategy for S3 objects
27+
- Add monitoring and alerting for security events
28+
- Add CloudWatch metrics for API key usage
29+
30+
2. **Documentation**
31+
- Add architecture diagrams
32+
- Document disaster recovery procedures
33+
- Create runbooks for common operations
34+
35+
## Development Tools
36+
37+
### Medium Priority
38+
39+
1. **Build and Deployment**
40+
41+
- Add Makefile to streamline common operations:
42+
- `make init`: Setup development environment
43+
- `make deploy`: Deploy to EC2
44+
- `make test`: Run tests locally and on EC2
45+
- `make logs`: View and manage service logs
46+
- `make api-keys`: Manage API keys
47+
- `make clean`: Cleanup temporary files and logs
48+
- Add environment validation in Makefile targets
49+
- Add colored output for better visibility
50+
- Add help target with command descriptions
51+
52+
2. **Development Experience**
53+
- Add pre-commit hooks for code formatting
54+
- Add automated version management
55+
- Add development/staging/production environment configs

gateway/scripts/manage_api_keys.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/bin/bash
2+
3+
# Configuration
4+
BUCKET_NAME="codequery-gateway-storage"
5+
API_KEYS_FILE="api_keys.json"
6+
KMS_KEY_ID="arn:aws:kms:sa-east-1:796973484576:key/2caae67f-7fe1-4555-82f0-6f6942320426"
7+
8+
# Function to add an API key
9+
add_key() {
10+
local api_key=$1
11+
local user_name=$2
12+
13+
# Download current keys
14+
aws s3 cp s3://$BUCKET_NAME/$API_KEYS_FILE .tmp_keys.json || echo "{}" > .tmp_keys.json
15+
16+
# Add new key
17+
jq --arg key "$api_key" --arg user "$user_name" '. + {($key): $user}' .tmp_keys.json > .tmp_keys_new.json
18+
19+
# Upload back to S3 with encryption using specific KMS key
20+
aws s3 cp .tmp_keys_new.json s3://$BUCKET_NAME/$API_KEYS_FILE --sse aws:kms --sse-kms-key-id $KMS_KEY_ID
21+
22+
# Clean up
23+
rm -f .tmp_keys.json .tmp_keys_new.json
24+
25+
echo "API key added for user: $user_name"
26+
}
27+
28+
# Function to list all keys (usernames only for security)
29+
list_keys() {
30+
aws s3 cp s3://$BUCKET_NAME/$API_KEYS_FILE - | jq 'to_entries | map(.value)'
31+
}
32+
33+
# Function to remove a key
34+
remove_key() {
35+
local api_key=$1
36+
37+
# Download current keys
38+
aws s3 cp s3://$BUCKET_NAME/$API_KEYS_FILE .tmp_keys.json
39+
40+
# Remove key
41+
jq --arg key "$api_key" 'del(.[$key])' .tmp_keys.json > .tmp_keys_new.json
42+
43+
# Upload back to S3 with encryption using specific KMS key
44+
aws s3 cp .tmp_keys_new.json s3://$BUCKET_NAME/$API_KEYS_FILE --sse aws:kms --sse-kms-key-id $KMS_KEY_ID
45+
46+
# Clean up
47+
rm -f .tmp_keys.json .tmp_keys_new.json
48+
49+
echo "API key removed"
50+
}
51+
52+
# Usage instructions
53+
usage() {
54+
echo "Usage:"
55+
echo " $0 add <api_key> <user_name> - Add a new API key"
56+
echo " $0 list - List all users with API keys"
57+
echo " $0 remove <api_key> - Remove an API key"
58+
}
59+
60+
# Main script
61+
case "$1" in
62+
"add")
63+
if [ -z "$2" ] || [ -z "$3" ]; then
64+
usage
65+
exit 1
66+
fi
67+
add_key "$2" "$3"
68+
;;
69+
"list")
70+
list_keys
71+
;;
72+
"remove")
73+
if [ -z "$2" ]; then
74+
usage
75+
exit 1
76+
fi
77+
remove_key "$2"
78+
;;
79+
*)
80+
usage
81+
exit 1
82+
;;
83+
esac

gateway/src/s3_manager.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import logging
23
import os
34
import boto3
45
from botocore.exceptions import ClientError
@@ -10,19 +11,75 @@ class S3Manager:
1011
"""
1112

1213
def __init__(self):
14+
self.logger = logging.getLogger(__name__)
1315
self.s3_client = self.get_s3_client()
16+
self.kms_client = self.get_kms_client()
1417
self.bucket_name, self.object_key = self.get_s3_bucket_and_key()
1518

1619
def get_s3_client(self):
1720
"""Initialize and return a new S3 client."""
18-
return boto3.client('s3')
21+
region = os.getenv('AWS_REGION', 'us-east-1')
22+
return boto3.client('s3', region_name=region)
23+
24+
def get_kms_client(self):
25+
"""Initialize and return a new KMS client."""
26+
region = os.getenv('AWS_REGION', 'us-east-1')
27+
return boto3.client('kms', region_name=region)
1928

2029
def get_s3_bucket_and_key(self):
2130
"""Get the S3 bucket name and object key."""
2231
bucket_name = os.getenv('S3_BUCKET_NAME', 'codequery-gateway-storage')
2332
object_key = 'ngrok_urls.json'
2433
return bucket_name, object_key
2534

35+
def load_encrypted_api_keys(self):
36+
"""
37+
Load API keys from the S3 bucket. The data is automatically decrypted by S3
38+
when using server-side encryption with KMS.
39+
"""
40+
try:
41+
response = self.s3_client.get_object(
42+
Bucket=self.bucket_name, Key='api_keys.json'
43+
)
44+
# S3 automatically decrypts the data when using SSE-KMS
45+
raw_data = response['Body'].read().decode('utf-8')
46+
api_keys = json.loads(raw_data)
47+
return api_keys
48+
49+
except ClientError as e:
50+
if e.response['Error']['Code'] == 'NoSuchKey':
51+
self.logger.warning("API keys file not found in S3.")
52+
return None
53+
# Fixed logging
54+
self.logger.error("ClientError while accessing S3: %s", str(e))
55+
raise e
56+
57+
except json.JSONDecodeError as e:
58+
self.logger.error("Error decoding JSON data: %s", str(e))
59+
return None
60+
61+
def store_encrypted_api_keys(self, api_keys):
62+
"""
63+
Store API keys in the S3 bucket using server-side encryption with KMS.
64+
"""
65+
try:
66+
# Convert API keys dictionary to JSON
67+
raw_data = json.dumps(api_keys)
68+
69+
# Store the data in S3 with server-side encryption
70+
self.s3_client.put_object(
71+
Bucket=self.bucket_name,
72+
Key='api_keys.json',
73+
Body=raw_data,
74+
ServerSideEncryption='aws:kms',
75+
SSEKMSKeyId=os.getenv('KMS_KEY_ID'),
76+
ContentType='application/json'
77+
)
78+
return {"status": "success", "message": "API keys stored securely in S3"}
79+
80+
except ClientError as e:
81+
raise e
82+
2683
def load_ngrok_url(self, api_key):
2784
"""
2885
Load the ngrok URL for a given API key from the S3 bucket.

gateway/terraform/s3/.terraform.lock.hcl

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)