Skip to content

Commit f4a8a8b

Browse files
committed
Bugfixing and prep for basic auth
1 parent c80307f commit f4a8a8b

3 files changed

Lines changed: 120 additions & 17 deletions

File tree

infrastructure/cdk/src/main/java/com/unicorn/core/InfrastructureLambdaBedrock.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.List;
1919
import java.util.Map;
20+
import java.util.Objects;
2021

2122
public class InfrastructureLambdaBedrock extends Construct {
2223

@@ -66,10 +67,9 @@ public InfrastructureLambdaBedrock(Construct scope, String id, String region, Bu
6667
bedrockRole.addToPolicy(PolicyStatement.Builder.create()
6768
.effect(Effect.ALLOW)
6869
.actions(List.of("bedrock:InvokeModel", "bedrock:ListFoundationModels"))
69-
.resources(List.of("arn:aws:bedrock:*:*:inference-profile/eu.anthropic.claude-3-7-sonnet-20250219-v1:0"))
70+
.resources(List.of("arn:aws:bedrock:*:*:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0"))
7071
.build());
7172

72-
// IAM Role for Lambda
7373
// IAM Role for Lambda
7474
Role lambdaRole = Role.Builder.create(this, "LambdaBedrockRole")
7575
.assumedBy(new ServicePrincipal("lambda.amazonaws.com"))
@@ -81,7 +81,6 @@ public InfrastructureLambdaBedrock(Construct scope, String id, String region, Bu
8181
))
8282
.build();
8383

84-
// Add permissions for Bedrock, S3, and SNS
8584
// Add permissions for Bedrock, S3, and SNS
8685
// Add permissions for logs
8786
lambdaRole.addToPolicy(PolicyStatement.Builder.create()
@@ -105,6 +104,19 @@ public InfrastructureLambdaBedrock(Construct scope, String id, String region, Bu
105104
.resources(List.of("*")) // Grant access to all Bedrock resources
106105
.build());
107106

107+
// Add permissions for AWS Secrets Manager access
108+
lambdaRole.addToPolicy(PolicyStatement.Builder.create()
109+
.effect(Effect.ALLOW)
110+
.actions(List.of(
111+
"secretsmanager:GetSecretValue",
112+
"secretsmanager:DescribeSecret"
113+
))
114+
.resources(List.of(
115+
// Allow access to the specific secret for webhook credentials
116+
String.format("arn:aws:secretsmanager:%s:*:secret:grafana-webhook-credentials*", region)
117+
))
118+
.build());
119+
108120
// Add separate policy for S3 and SNS
109121
lambdaRole.addToPolicy(PolicyStatement.Builder.create()
110122
.effect(Effect.ALLOW)
@@ -119,7 +131,6 @@ public InfrastructureLambdaBedrock(Construct scope, String id, String region, Bu
119131
))
120132
.build());
121133

122-
123134
// Add permissions for EKS API access
124135
lambdaRole.addToPolicy(PolicyStatement.Builder.create()
125136
.effect(Effect.ALLOW)
@@ -161,14 +172,14 @@ public InfrastructureLambdaBedrock(Construct scope, String id, String region, Bu
161172
.securityGroups(List.of(lambdaSg))
162173
.environment(Map.of(
163174
"APP_LABEL", "unicorn-store-spring",
164-
"EKS_CLUSTER_NAME", eksCluster != null ? eksCluster.getCluster().getName() : "unicorn-store",
175+
"EKS_CLUSTER_NAME", Objects.requireNonNull(eksCluster.getCluster().getName()),
165176
"K8S_NAMESPACE", "unicorn-store-spring",
166177
"S3_BUCKET_NAME", s3Bucket.getBucketName(),
178+
"AWS_REGION", region,
167179
"KUBERNETES_AUTH_TYPE", "aws" // Use AWS IAM authentication for EKS
168180
))
169181
.build();
170182

171-
172183
s3Bucket.grantWrite(this.threadDumpFunction);
173184
s3Bucket.grantRead(this.threadDumpFunction);
174185

infrastructure/lambda/src/lambda_function.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import requests
66
import time
77
import random
8-
import re # Add this import for regex
8+
import re
9+
import base64
910
from datetime import datetime
1011
from typing import Dict, Any
1112
from kubernetes import client, config
@@ -18,6 +19,62 @@
1819
def is_invalid(value: str) -> bool:
1920
return value is None or value.strip() in ["", "[no value]"]
2021

22+
def verify_basic_auth(event: Dict[str, Any]) -> bool:
23+
"""
24+
Verify Basic Authentication credentials from the request headers.
25+
Returns True if authentication is successful, False otherwise.
26+
"""
27+
try:
28+
# Get headers from the event
29+
headers = event.get('headers', {})
30+
if not headers:
31+
logger.warning("No headers found in the request")
32+
return False
33+
34+
# Look for authorization header (case-insensitive)
35+
auth_header = None
36+
for key in headers:
37+
if key.lower() == 'authorization':
38+
auth_header = headers[key]
39+
break
40+
41+
if not auth_header or not auth_header.startswith('Basic '):
42+
logger.warning("No Basic Authorization header found")
43+
return False
44+
45+
# Extract and decode credentials
46+
encoded_credentials = auth_header.split(' ')[1]
47+
decoded_credentials = base64.b64decode(encoded_credentials).decode('utf-8')
48+
username, password = decoded_credentials.split(':')
49+
50+
# Get expected credentials from AWS Secrets Manager
51+
secret_name = "grafana-webhook-credentials"
52+
region_name = os.environ.get('AWS_REGION', 'us-east-1')
53+
54+
session = boto3.session.Session()
55+
client = session.client(
56+
service_name='secretsmanager',
57+
region_name=region_name
58+
)
59+
60+
response = client.get_secret_value(SecretId=secret_name)
61+
secret = json.loads(response['SecretString'])
62+
63+
# Validate credentials
64+
if username != secret['username'] or password != secret['password']:
65+
logger.warning("Invalid credentials provided")
66+
return False
67+
68+
logger.info("Authentication successful")
69+
return True
70+
71+
except ClientError as e:
72+
logger.error(f"Error retrieving credentials from Secrets Manager: {str(e)}")
73+
return False
74+
except Exception as e:
75+
logger.error(f"Authentication error: {str(e)}")
76+
return False
77+
2178
def extract_pod_info_from_valuestring(value_string: str) -> Dict[str, str]:
2279
"""Extract pod information from Grafana alert valueString"""
2380
try:
@@ -184,13 +241,24 @@ def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
184241
# Check if this is a direct webhook call from Grafana
185242
if 'body' in event:
186243
logger.info("Processing direct webhook from Grafana")
244+
245+
# Verify authentication for webhook calls
246+
if not verify_basic_auth(event):
247+
logger.warning("Authentication failed")
248+
return {
249+
'statusCode': 401,
250+
'headers': {'WWW-Authenticate': 'Basic'},
251+
'body': json.dumps({'error': 'Authentication failed'})
252+
}
253+
187254
try:
188255
# Parse the webhook payload
189256
if isinstance(event['body'], str):
190257
body = json.loads(event['body'])
191258
else:
192259
body = event['body']
193260

261+
# Rest of your webhook handling code remains the same
194262
alerts = body.get('alerts', [])
195263
if not alerts:
196264
return {
@@ -200,6 +268,8 @@ def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
200268

201269
results = []
202270
for alert in alerts:
271+
# Your existing alert processing code...
272+
# No changes needed here
203273
if alert.get('status') != 'firing':
204274
logger.info("Skipping resolved alert")
205275
continue

infrastructure/scripts/setup/monitoring.sh

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ cleanup() {
3030
}
3131
trap cleanup EXIT
3232

33+
# -- Generate secure username and password for webhook
34+
WEBHOOK_USER="grafana-alerts"
35+
WEBHOOK_PASSWORD=$(openssl rand -base64 16 | tr -d '\n')
36+
37+
# -- Create the secret for webhook
38+
aws secretsmanager create-secret \
39+
--name grafana-webhook-credentials \
40+
--description "Basic auth credentials for Grafana webhook to Lambda" \
41+
--secret-string "{\"username\":\"$WEBHOOK_USER\",\"password\":\"$WEBHOOK_PASSWORD\"}"
42+
43+
echo "Webhook credentials created:"
44+
echo "Username: $WEBHOOK_USER"
45+
echo "Password: $WEBHOOK_PASSWORD"
46+
echo "Save these credentials securely!"
47+
48+
3349
# --- Namespace & Helm setup ---
3450
kubectl create namespace "$NAMESPACE" 2>/dev/null || true
3551
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts || true
@@ -303,16 +319,22 @@ if [[ -z "$NOTIF_UID" ]]; then
303319
CONTACT_POINT_JSON=$(jq -n \
304320
--arg name "lambda-webhook" \
305321
--arg url "$LAMBDA_URL" \
306-
'{
307-
name: $name,
308-
type: "webhook",
309-
settings: {
310-
url: $url,
311-
httpMethod: "POST"
312-
},
313-
disableResolveMessage: false,
314-
isDefault: false
315-
}')
322+
--arg user "$WEBHOOK_USER" \
323+
--arg pass "$WEBHOOK_PASSWORD" \
324+
'{
325+
name: $name,
326+
type: "webhook",
327+
settings: {
328+
url: $url,
329+
httpMethod: "POST",
330+
username: $user,
331+
password: $pass,
332+
authorization_scheme: "basic"
333+
},
334+
disableResolveMessage: false,
335+
isDefault: false
336+
}')
337+
316338

317339
curl -s -X POST -u "$GRAFANA_USER:$GRAFANA_PASSWORD" \
318340
-H "Content-Type: application/json" \

0 commit comments

Comments
 (0)