Agents on Call, Μέρος 4. Εργαλεία και το Gateway: MCP, Allowlists, Read-Only ως προεπιλογή
Τα εργαλεία περνούν πίσω από το AgentCore Gateway: scoped Lambdas, cross-account read-only ρόλοι, και το ένα gated μονοπάτι που επιτρέπεται να αλλάξει κάτι.

Τέσσερα εργαλεία βρίσκονται τώρα πίσω από ένα AgentCore Gateway αντί να είναι συνδεδεμένα μία συνάρτηση Python τη φορά σε κάθε agent: τα cloudwatch-read, logs-read και cost-read αναλαμβάνουν έναν read-only ρόλο σε έναν λογαριασμό spoke και δεν μπορούν ποτέ να μεταβάλουν τίποτα, ενώ το ssm-execute, το μοναδικό εργαλείο της πλατφόρμας που μεταβάλλει κατάσταση, δεν μπορεί ούτε αυτό να φτάσει απευθείας σε ένα spoke, μπορεί μόνο να ξεκινήσει μια εκτέλεση Step Functions που παύει και περιμένει έγκριση ανθρώπου στο Slack. Αυτή η παύση δεν είναι μια ευκολία στο UI. Είναι το μοναδικό σημείο σε ολόκληρη την πλατφόρμα όπου δημιουργείται ένα AWS credential ικανό να αλλάξει κάτι σε έναν λογαριασμό spoke, και δημιουργείται μόνο αφού ένας άνθρωπος πατήσει έγκριση.
Το Μέρος 1 έστησε το σενάριο και επέλεξε AgentCore συν Strands. Το Μέρος 2 έχτισε το όριο λογαριασμού: τους ρόλους spoke ops-readonly και ops-mutate που αυτό το μέρος υποθέτει ότι υπάρχουν και επαναχρησιμοποιεί χωρίς τροποποίηση. Το Μέρος 3 παρέδωσε τον πρώτο agent με δύο εργαλεία συνδεδεμένα απευθείας, τα cloudwatch_read και logs_read ως απλές συναρτήσεις Python με decorator @tool που καλούσε in-process ο triage agent, και δήλωσε ξεκάθαρα ότι το Gateway θα ήταν έμμεση κατεύθυνση χωρίς ακόμα τίποτα να δρομολογήσει ανάμεσα. Αυτό το μέρος είναι εκεί όπου εκείνη η έμμεση κατεύθυνση αποδίδει: ένα δεύτερο εργαλείο ανάγνωσης (cost-read) και το πρώτο εργαλείο μετάβασης κατάστασης της πλατφόρμας (ssm-execute) υπάρχουν πλέον και τα δύο, και τέσσερα εργαλεία που κάθε agent μπορεί να καλέσει ανεξάρτητα είναι ακριβώς ο αριθμός στον οποίο ένα κοινόχρηστο, κεντρικά καθορισμένο επίπεδο εργαλείων παύει να είναι πρόωρο. Ο συνοδευτικός κώδικας βρίσκεται στο github.com/flightlesstux/agents-on-call, στα terraform/20-gateway-tools/ και agents/tools/, και κάθε απόσπασμα παρακάτω είναι αντιγραμμένο από αυτά τα αρχεία, όχι απλοποιημένο για το άρθρο.
Γιατί τα inline εργαλεία σταματούν να κλιμακώνονται στον δεύτερο agent
Τα δύο εργαλεία του Μέρους 3 ζούσαν μέσα στο agents/triage/agent.py: απλές συναρτήσεις, μία κλήση cross-account assume-role η καθεμία, καλούμενες απευθείας από τον έναν agent που τις χρειαζόταν. Τίποτα κακό σε αυτό με έναν agent. Το πρόβλημα ξεκινά στον δεύτερο: ο cost agent του Μέρους 5 χρειάζεται cloudwatch-read και cost-read, ο runbook agent του χρειάζεται cloudwatch-read, logs-read και τελικά ssm-execute. Συνδεδεμένα in-process όπως το έκανε το Μέρος 3, αυτό είναι η ίδια συνάρτηση Python επικολλημένη σε τρεις codebases agents, τρεις ρόλοι IAM που χορηγούν ανεξάρτητα το ίδιο δικαίωμα sts:AssumeRole, και τρία σημεία για να διορθώσεις ένα σφάλμα στον τρόπο που χτίζεται η cross-account session, ένα typo στον χειρισμό του external ID σε ένα αντίγραφο που κανείς δεν παρατηρεί μέχρι οι κλήσεις εκείνου του συγκεκριμένου agent να αρχίσουν να αποτυγχάνουν με τρόπο που οι άλλοι δύο δεν αναπαράγουν.
Το AgentCore Gateway αφαιρεί το copy-paste, όχι κάνοντας τα εργαλεία εξυπνότερα αλλά κάνοντάς τα να υπάρχουν ακριβώς μία φορά. Ένα Lambda ανά εργαλείο, ένας execution role ανά Lambda, ένα Gateway που δρομολογεί τις κλήσεις εργαλείων κάθε agent στον σωστό στόχο. Ο δικός του ρόλος IAM ενός agent δεν χρειάζεται πλέον καθόλου sts:AssumeRole σε κανένα spoke (ο ρόλος runtime του triage στο Μέρος 3 τον έχει ακόμα, αφού εκείνος ο agent προϋπάρχει αυτού του μέρους)· ένας μελλοντικός agent χρειάζεται μόνο δικαίωμα να καλέσει το MCP endpoint του Gateway, και κάθε cross-account read credential που δημιουργεί η πλατφόρμα προέρχεται από ακριβώς τέσσερις ρόλους εκτέλεσης Lambda που κατέχει αυτό το module, όχι από όσους agents τυχόν υπάρχουν μέχρι το τέλος της σειράς.
Το AgentCore Gateway ως επίπεδο εργαλείων MCP
Το AgentCore Gateway μετατρέπει APIs, συναρτήσεις Lambda και υπάρχουσες υπηρεσίες σε εργαλεία συμβατά με MCP από τη γενική διαθεσιμότητα του AgentCore στις 13 Οκτωβρίου 2025, με εξουσιοδότηση βασισμένη σε IAM διαθέσιμη από την ίδια εκείνη GA. Το ίδιο το MCP, το πρωτόκολλο που μιλά κάθε στόχος του Gateway, είναι το ανοιχτό πρότυπο που δημοσίευσε η Anthropic τον Νοέμβριο του 2024 για τη σύνδεση εφαρμογών AI με πηγές δεδομένων και εργαλείων· η συνεισφορά του Gateway είναι ότι μετατρέπει τέσσερις ξεχωριστές συναρτήσεις Lambda σε έναν διακομιστή MCP από τον οποίο η βιβλιοθήκη client ενός agent μπορεί να ανακαλύψει εργαλεία, αντί ο κώδικας του agent να χρειάζεται απευθείας το ARN και τη μορφή κλήσης κάθε Lambda. Η τιμολόγηση ακολουθεί το σχήμα του υπόλοιπου AgentCore: 0,005 $ ανά 1.000 κλήσεις API (ListTools, InvokeTool, Ping) και 0,02 $ ανά 100 εργαλεία που έχουν καταχωριστεί τον μήνα, αρκετά μικρό ώστε τέσσερα εργαλεία να μην κοστίζουν τίποτα δίπλα στο κόστος tokens μιας απλής κλήσης μοντέλου.
Ο ίδιος ο πόρος Gateway είναι σύντομος. Το authorizer_type = "AWS_IAM" σημαίνει ότι το ίδιο όριο IAM στο οποίο έχει στηριχθεί όλη αυτή η σειρά αποφασίζει ποιος μπορεί να καλέσει το Gateway γενικά, χωρίς να χρειάζεται να στηθεί ξεχωριστός εκδότης JWT για μια πλατφόρμα που έχει ήδη σύστημα ταυτότητας:
resource "aws_bedrockagentcore_gateway" "tools" {
name = "${var.platform_name}-tools"
description = "MCP tool plane: cloudwatch-read, logs-read, cost-read, ssm-execute. AWS_IAM authorizer, IAM does the access control both for who can call the Gateway and what each tool Lambda can touch."
role_arn = aws_iam_role.gateway.arn
protocol_type = "MCP"
authorizer_type = "AWS_IAM"
# Optional Cedar policy layer, see variables.tf's cedar_policy_engine_arn
# comment for why the engine itself is a variable, not a resource, at this
# part's date.
dynamic "policy_engine_configuration" {
for_each = var.cedar_policy_engine_arn == null ? [] : [var.cedar_policy_engine_arn]
content {
arn = policy_engine_configuration.value
mode = var.cedar_policy_engine_mode
}
}
tags = merge(var.tags, { Component = "gateway" })
}Αυτό το block policy_engine_configuration είναι το νεότερο κομμάτι Terraform σε αυτή τη σειρά μέχρι στιγμής, αξίζει έλεγχο ημερομηνίας. Η αξιολόγηση πολιτικών βασισμένη σε Cedar για το Gateway κυκλοφόρησε στον provider aws ως πόρος μόνο-λίστας στις 27 Μαΐου 2026, και μετά απέκτησε αυτό το block διαδρομής εγγραφής στις 10 Ιουνίου, έκδοση provider 6.50.0, οκτώ ημέρες πριν την ημερομηνία αυτού του ίδιου του μέρους. Ασφαλές κάτω από τον κανόνα backdating της σειράς, αλλά μόλις που τα καταφέρνει, και εξακολουθεί να μην υπάρχει πόρος Terraform για να δημιουργήσει κανείς την ίδια τη μηχανή πολιτικών Cedar, μόνο για να συνδέσει μία που ήδη υπάρχει. Το var.cedar_policy_engine_arn έχει προεπιλογή null και το block είναι τυλιγμένο σε dynamic, το ίδιο μοτίβο που χρησιμοποίησε το Μέρος 2 για την πρόσβαση σε μοντέλα Bedrock: μια πραγματική δυνατότητα AWS στην οποία μπορεί να αναφερθεί το Terraform αλλά όχι ακόμα να την προμηθεύσει πλήρως.
Ένα Lambda, μία δουλειά: το μοτίβο εργαλείου
Κάθε εργαλείο ακολουθεί το ίδιο σχήμα: μια συνάρτηση Lambda, έναν execution role καθορισμένο ακριβώς σε ό,τι χρειάζεται εκείνο το ένα εργαλείο, και έναν στόχο Gateway που περιγράφει το input schema του εργαλείου ώστε ο MCP client ενός agent να μπορεί να τον καλέσει χωρίς να διαβάσει τον κώδικα του Lambda. Το cloudwatch-read είναι το πιο ξεκάθαρο παράδειγμα, γιατί είναι η ίδια λογική που παρέδωσε ήδη το Μέρος 3, απλώς μετακινημένη. Ο execution role εμπιστεύεται μόνο την υπηρεσία Lambda και χορηγεί μόνο δύο πράγματα: να αναλάβει το ops-readonly στα ρυθμισμένα spokes, και να γράφει τα δικά του CloudWatch Logs:
data "aws_iam_policy_document" "cloudwatch_read_permissions" {
statement {
sid = "AssumeOpsReadonlyInSpokes"
effect = "Allow"
actions = ["sts:AssumeRole"]
resources = local.ops_readonly_role_arns
}
statement {
sid = "WriteOwnLogs"
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = [local.lambda_log_arn]
}
}
resource "aws_lambda_function" "cloudwatch_read" {
function_name = "${var.platform_name}-cloudwatch-read"
description = "AgentCore Gateway MCP tool: reads a CloudWatch metric from a spoke account via ops-readonly. Read-only."
role = aws_iam_role.cloudwatch_read.arn
handler = "cloudwatch_read.handler"
runtime = "python3.12"
timeout = 30
memory_size = 256
filename = data.archive_file.cloudwatch_read_zip.output_path
source_code_hash = data.archive_file.cloudwatch_read_zip.output_base64sha256
...
}Κανένα wildcard resource πουθενά σε αυτό το policy document: το local.ops_readonly_role_arns χτίζει ένα ARN ανά ρυθμισμένο account ID spoke, και τίποτα άλλο δεν μπορεί να αγγίξει αυτός ο ρόλος έξω από αυτή τη λίστα συν το δικό του log group. Τα logs-read και cost-read επαναχρησιμοποιούν πανομοιότυπα trust και permission documents· διαφέρουν μόνο ο κώδικας handler και το tool schema που βλέπει το Gateway. Ο ίδιος ο handler είναι η λογική του Μέρους 3 σχεδόν αναλλοίωτη, διαβάζοντας τα ορίσματά του από ένα event που παρέχει το Gateway αντί για keyword arguments Python:
def handler(event: dict, context) -> dict:
"""Lambda entrypoint for the cloudwatch-read Gateway target.
...
"""
spoke_account_id = event["spoke_account_id"]
namespace = event["namespace"]
metric_name = event["metric_name"]
dimensions = event.get("dimensions", {})
lookback_minutes = int(event.get("lookback_minutes", 60))
stat = event.get("stat", "Average")
period_seconds = int(event.get("period_seconds", 60))
session = assume_role(spoke_account_id, OPS_READONLY_ROLE_NAME, "cloudwatch-read")
cloudwatch = session.client("cloudwatch", config=_boto_config)Ο στόχος Gateway είναι αυτό που μετατρέπει εκείνο το Lambda σε κάτι που ο MCP client ενός agent μπορεί να ανακαλύψει: ένα όνομα, μια περιγραφή, και ένα συμβόλαιο εισόδου σε μορφή JSON-Schema. Το πλήρες block φτάνει τις επτά παραμέτρους· το σχήμα παρακάτω είναι το ίδιο για κάθε εργαλείο:
resource "aws_bedrockagentcore_gateway_target" "cloudwatch_read" {
gateway_identifier = aws_bedrockagentcore_gateway.tools.gateway_id
name = "cloudwatch-read"
description = "Reads a CloudWatch metric's recent datapoints from a spoke account."
credential_provider_configuration {
gateway_iam_role {}
}
target_configuration {
mcp {
lambda {
lambda_arn = aws_lambda_function.cloudwatch_read.arn
tool_schema {
inline_payload {
name = "cloudwatch_read"
description = "Read a CloudWatch metric's recent datapoints from a spoke account. An empty datapoint list is a real finding, not an error."
input_schema {
type = "object"
property {
name = "spoke_account_id"
type = "string"
description = "12-digit account ID of the spoke to query."
required = true
}
...
}
}
}
}
}
}
}Το credential_provider_configuration { gateway_iam_role {} } είναι η λεπτομέρεια που αξίζει να σταθούμε: λέει στο Gateway να καλέσει το Lambda στόχο χρησιμοποιώντας τον δικό του execution role του Gateway μέσω απλού IAM, όχι μια ροή OAuth ή ένα API key που τραβιέται από έναν credential provider. Αυτή είναι η σωστή επιλογή για ένα Lambda στον ίδιο λογαριασμό, στην ίδια πλατφόρμα· στο role_arn του Gateway χορηγείται lambda:InvokeFunction ακριβώς σε αυτά τα τέσσερα ARN Lambda και τίποτα άλλο, η ίδια πειθαρχία ενός-σκοπού-ενός-δικαιώματος όπως κάθε ρόλος σε αυτή τη σειρά μέχρι τώρα.
Το read-only δεν είναι σλόγκαν, είναι τρία ξεχωριστά γεγονότα IAM
Το «read-only ως προεπιλογή» σημαίνει κάτι μόνο αν επιβιώνει από την επαφή με ένα σφάλμα. Αυτή η σύνδεση επιβιώνει από τρία συγκεκριμένα. Ακτίνα έκρηξης: οι τρεις ρόλοι ανάγνωσης δεν κρατούν κανένα δικαίωμα που μπορεί να αλλάξει κατάσταση σε ένα spoke, μόνο sts:AssumeRole στο ops-readonly, του οποίου η πολιτική (Μέρος 2) επιστρώνει μια ρητή άρνηση πάνω από τις read-only managed πολιτικές του, οπότε ένα prompt injection που πείθει έναν agent να ζητήσει από ένα εργαλείο ανάγνωσης να διαγράψει κάτι δεν έχει πουθενά να πάει. Έλεγχος: κάθε ανάγνωση εξακολουθεί να διασχίζει ένα όριο λογαριασμού μέσω sts:AssumeRole, οπότε το CloudTrail στον λογαριασμό ασφαλείας βλέπει ακριβώς ποιο Lambda εργαλείου άγγιξε ποιο spoke και πότε. Εμπιστοσύνη με την ανθρώπινη ομάδα: ένας μηχανικός on-call που βλέπει το ssm-execute πίσω από μια πύλη έγκρισης, και τρία εργαλεία με το όνομα read των οποίων οι ρόλοι δεν κρατούν καμία ενέργεια μετάβασης κατάστασης πουθενά, έχει λόγο να πιστέψει τον ισχυρισμό «μόνο διαβάζει» αντί να τον δεχτεί βάσει πίστης, ελέγξιμο σε πέντε λεπτά με aws iam get-role-policy, όχι μια πρόταση σε ένα system prompt που κάποιος πρέπει να εμπιστευτεί ότι ακολούθησε το μοντέλο.
Το ένα εργαλείο μετάβασης κατάστασης, και η πύλη πίσω του
Το ssm-execute είναι το μοναδικό εργαλείο στην πλατφόρμα του οποίου ο ρόλος IAM δεν περιλαμβάνει καθόλου δικαίωμα sts:AssumeRole σε κανένα spoke. Το πλήρες σύνολο δικαιωμάτων του είναι states:StartExecution, καθορισμένο ακριβώς σε ένα ARN state machine:
data "aws_iam_policy_document" "ssm_execute_permissions" {
statement {
sid = "StartApprovalStateMachineOnly"
effect = "Allow"
actions = ["states:StartExecution"]
resources = [aws_sfn_state_machine.mutation_approval.arn]
}
...
}Η κλήση του ssm-execute δεν τρέχει τίποτα, προτείνει την εκτέλεση κάποιου πράγματος. Ο handler ελέγχει το προτεινόμενο document έναντι μιας τοπικής allowlist πριν καν δημιουργήσει μια εκτέλεση Step Functions, ένας πλεονάζων, fail-fast έλεγχος πάνω από τον πραγματικό, τη δική πολιτική IAM του ops-mutate από το Μέρος 2, που είναι αυτό που πραγματικά ισχύει αν ο κώδικας αυτού του Lambda έχει σφάλμα:
if ssm_document_arn not in ALLOWED_SSM_DOCUMENT_ARNS:
return {
"status": "rejected",
"reason": f"{ssm_document_arn} is not on the allowlist; no approval request was created.",
}
execution = _sfn.start_execution(
stateMachineArn=APPROVAL_STATE_MACHINE_ARN,
input=json.dumps({
"spoke_account_id": spoke_account_id,
"ssm_document_arn": ssm_document_arn,
"ssm_parameters": ssm_parameters,
"diagnosis": diagnosis,
"blast_radius": blast_radius,
}),
)Η state machine είναι εκεί όπου το .waitForTaskToken κερδίζει το όνομά του. Το ίδιο το tutorial των Step Functions πάνω σε αυτό το μοτίβο είναι ρητό ότι μια κατάσταση Task μπορεί να παύσει επ' αόριστον, να δημιουργήσει ένα token, και να συνεχίσει μόνο όταν κάτι έξω από τη state machine καλέσει SendTaskSuccess ή SendTaskFailure με ακριβώς εκείνο το token, και ότι τόσο η κατάσταση αναμονής όσο και ολόκληρη η εκτέλεση χρειάζονται δικό τους TimeoutSeconds, αλλιώς μια κολλημένη εκτέλεση δεν τελειώνει ποτέ. Το NotifySlackAndWaitForApproval καλεί το slack-post με το task token μέσα στο payload του, και μετά παύει το ίδιο το Step Functions, όχι το Lambda:
definition = jsonencode({
Comment = "Human approval gate for ops-mutate SSM Automation execution."
StartAt = "NotifySlackAndWaitForApproval"
TimeoutSeconds = var.state_machine_timeout_seconds
States = {
NotifySlackAndWaitForApproval = {
Type = "Task"
Resource = "arn:aws:states:::lambda:invoke.waitForTaskToken"
Parameters = {
FunctionName = aws_lambda_function.slack_post.arn
Payload = {
"TaskToken.$" = "$$.Task.Token"
"Diagnosis.$" = "$.diagnosis"
"BlastRadius.$" = "$.blast_radius"
"SsmDocument.$" = "$.ssm_document_arn"
"SsmParameters.$" = "$.ssm_parameters"
"SpokeAccountId.$" = "$.spoke_account_id"
}
}
TimeoutSeconds = var.approval_wait_timeout_seconds
Next = "RunApprovedAutomation"
Catch = [
{
ErrorEquals = ["States.ALL"]
Next = "ApprovalDeniedOrTimedOut"
}
]
}
...
}
})Ακολουθούν δύο ακόμα καταστάσεις, που δεν φαίνονται παραπάνω: το RunApprovedAutomation καλεί το executor.py με την ίδια την έξοδο της κατάστασης αναμονής ως payload του, και το ApprovalDeniedOrTimedOut είναι μια απλή κατάσταση Fail στην οποία δρομολογεί το Catch παραπάνω. Το slack-post δημοσιεύει μια κάρτα με τη διάγνωση, την ακτίνα έκρηξης, το ακριβές document SSM, και τις παραμέτρους, συν συνδέσμους έγκρισης/απόρριψης που κουβαλούν το task token, καθένας δείχνοντας σε μια διαδρομή API Gateway (/succeed, /fail) που στηρίζεται στο approval_callback.py. Η δουλειά εκείνου του Lambda στο σύνολό της είναι μία κλήση API, send_task_success ή send_task_failure με το token του συνδέσμου που πατήθηκε, και η πολιτική IAM του χορηγεί και τα δύο σε Resource = "*" για πραγματικό λόγο: κανένα από τα δύο APIs δεν απευθύνεται σε συγκεκριμένη εκτέλεση με ARN, μόνο στο αδιαφανές token, το ίδιο σχήμα που συνάντησε η δήλωση InspectAndStopOwnExecutions του Μέρους 2 για τα δικά τους APIs επιθεώρησης του SSM. Αξίζει να ειπωθεί ξεκάθαρα: η κωδικοποίηση εκείνου του token σε ένα query string συνδέσμου GET είναι πραγματική αδυναμία, ένας προωθημένος ή καταγεγραμμένος σύνδεσμος είναι μια προωθημένη ή καταγεγραμμένη έγκριση, τεκμηριωμένο στο slack_post.py αντί να είναι κρυμμένο, προς αντικατάσταση με ένα υπογεγραμμένο, μίας χρήσης, βασισμένο σε datastore token πριν αυτό τρέξει ενάντια σε κάτι που έχει σημασία. Αυτές οι διαδρομές δεν φέρουν καθόλου authorizer: η κατοχή του token είναι ολόκληρος ο έλεγχος πρόσβασης, οπότε οποιοσδήποτε αποκτήσει τον σύνδεσμο, όχι μόνο ο προοριζόμενος εγκριτής στο Slack, μπορεί να τον ενεργοποιήσει.
Μόνο με την έγκριση δημιουργείται οτιδήποτε έχει δικαίωμα να αλλάξει έναν λογαριασμό spoke. Το executor.py είναι το μοναδικό Lambda του οποίου ο execution role μπορεί να αναλάβει το ops-mutate: η trust policy του Μέρους 2 ονομάζει ακριβώς το ARN αυτού του ρόλου Lambda ως τον μοναδικό principal της, και η δική του διάρκεια credential είναι σκόπιμα σύντομη:
session = assume_role(
spoke_account_id,
OPS_MUTATE_ROLE_NAME,
"post-approval-executor",
duration_seconds=300,
)
ssm = session.client("ssm", config=_boto_config)
response = ssm.start_automation_execution(
DocumentName=ssm_document_arn,
Parameters={k: [v] if not isinstance(v, list) else v for k, v in ssm_parameters.items()},
)Πέντε λεπτά, λιγότερο από τα δεκαπέντε λεπτά προεπιλογής των εργαλείων ανάγνωσης: αυτό το credential υπάρχει για να κάνει ακριβώς μία κλήση API, όχι για να παραμείνει διαθέσιμο για επαναχρησιμοποίηση σε ένα follow-up που η συνάρτηση δεν σκόπευε ποτέ. Δύο Lambdas, δύο ρόλοι IAM, μία στενή παράδοση μέσω μιας εκτέλεσης state machine, είναι αυτό που κάνει το «το μοναδικό εργαλείο μετάβασης κατάστασης δεν μπορεί να φτάσει απευθείας σε ένα spoke» ιδιότητα της σύνδεσης, όχι μια υπόσχεση που τηρεί αυτός ο κώδικας επειδή επέλεξε να μην εισάγει τον client STS του boto3.
Τι βλέπει ο agent όταν ένα εργαλείο απορρίπτεται
Υπάρχουν τρία διακριτά είδη «όχι» εδώ, αξίζει να τα ξεχωρίσουμε. Μια απόρριψη σε επίπεδο εφαρμογής: ο έλεγχος allowlist του ssm-execute επιστρέφει {"status": "rejected", "reason": ...} ως κανονικό αποτέλεσμα εργαλείου, καμία εξαίρεση, καμία εκτέλεση δεν δημιουργείται· ο agent το αναφέρει απλά. Μια άρνηση σε επίπεδο IAM: μια απούσα χορήγηση lambda:InvokeFunction ή χορήγηση assume-role αποτυγχάνει την κλήση με μια AccessDeniedException της AWS που εμφανίζεται ως σφάλμα εργαλείου, όχι ως αποτέλεσμα εργαλείου, η σκληρή διάκριση που έχει ήδη εκπαιδεύσει το system prompt του Μέρους 3 τον triage agent να σέβεται. Και, από τη στιγμή που το cedar_policy_engine_arn οριστεί με mode = "ENFORCE", μια άρνηση σε επίπεδο πολιτικής: το Cedar απορρίπτει μια κλήση πριν καν φτάσει στο Lambda, βάσει κανόνων έξω από τον κώδικα οποιουδήποτε συγκεκριμένου εργαλείου· σε LOG_ONLY η ίδια κλήση καταγράφεται αλλά επιτρέπεται να περάσει, γι' αυτό η δοκιμή μιας νέας πολιτικής εκεί έναντι πραγματικής κίνησης έχει σημασία πριν τη μετάβαση σε ENFORCE, όπου ένας υπερβολικά ευρύς κανόνας αποτυγχάνει κλήσεις που ποτέ δεν ήταν πρόβλημα χωρίς κανένα log σε επίπεδο εργαλείου να εξηγεί γιατί.
Τρόποι αποτυχίας που πρέπει να προσέχεις
Πέντε πράγματα αξίζει να γνωρίζεις πριν αυτό το pipeline τρέξει ενάντια σε μια πραγματική πρόταση. Οι δύο allowlists document SSM, το τοπικό αντίγραφο του ssm-execute και η δική πολιτική IAM του ops-mutate, ζουν σε δύο ανεξάρτητα εφαρμοσμένα modules Terraform (αυτό εδώ και το 00-foundation), και τίποτα δεν επιβάλλει να παραμένουν ίδια· ένα document που λείπει από την τοπική λίστα απορρίπτεται πριν υπάρξει αίτημα έγκρισης, ένα document που λείπει από τη λίστα IAM δημιουργεί ένα αίτημα για κάτι που το ops-mutate μετά αρνείται να τρέξει, και η δεύτερη περίπτωση είναι χειρότερη γιατί ένας άνθρωπος έχει ήδη πατήσει έγκριση πριν το ανακαλύψει. Το να ξεχάσεις οποιαδήποτε τιμή TimeoutSeconds, της κατάστασης αναμονής ή της εκτέλεσης, μετατρέπει μια αναπάντητη πρόταση στο Slack σε μια παυμένη εκτέλεση που γερνά επ' αόριστον αντί να αποτύχει δυνατά στο ApprovalDeniedOrTimedOut, ακριβώς αυτό που επισημαίνει το tutorial έγκρισης ανθρώπου των Step Functions ως τον πιο συνηθισμένο τρόπο που σπάει αυτό το μοτίβο. Και η ίδια η αδυναμία του συνδέσμου έγκρισης παραμένει μέχρι να αντικατασταθεί με ένα υπογεγραμμένο, μίας χρήσης, βασισμένο σε datastore token: αντιμετώπισε κάθε κανάλι Slack στο οποίο δημοσιεύεται αυτό σαν ένα όπου η προώθηση ενός μηνύματος ισοδυναμεί με προώθηση μιας έγκρισης. Ένα αίτημα GET που μεταβάλλει κατάσταση προσκαλεί επίσης τυχαία έγκριση: ο δικός του unfurler συνδέσμων του Slack, εταιρικοί σαρωτές ασφάλειας συνδέσμων, και το prefetch του browser μπορούν όλα να ανακτήσουν εκείνο το URL πριν το πατήσει ποτέ ένας άνθρωπος, και κανένα μονοπάτι κώδικα εδώ δεν διακρίνει εκείνη την ανάκτηση από μια σκόπιμη έγκριση. Και το approval_callback.py περνά το JSON της πρότασης απευθείας από το query string του συνδέσμου που πατήθηκε στο send_task_success χωρίς να το ελέγξει έναντι αυτού που πραγματικά κατέγραψε το Step Functions για εκείνο το token, οπότε ένας παραποιημένος σύνδεσμος μπορεί να αντικαταστήσει με διαφορετικό ssm_document_arn ή ssm_parameters από αυτό που είδε ο άνθρωπος στο Slack· η δική της allowlist IAM του ops-mutate περιορίζει τη ζημιά σε κάτι ήδη επιτρεπτό, αλλά ο συγκεκριμένος συνδυασμός document-και-παραμέτρων που ενέκρινε ένας άνθρωπος δεν είναι αυτός που συνδέεται με ό,τι πραγματικά τρέχει.
Διάβασε επίσης
- Μέρος 3, Πρώτος Agent: Incident Triage στο Strands, για τα δύο εργαλεία που αυτό το μέρος μετέφερε πίσω από το Gateway και το μοτίβο assume-role του
ops-readonlyπου μοιράζονται και οι δύο γενιές αυτών των εργαλείων. - Μέρος 5, Η Ομάδα: Supervisor και Τρεις Ειδικοί, όπου οι agents runbook και cost γίνονται ο δεύτερος και ο τρίτος καλών των εργαλείων που έχτισε αυτό το μέρος, ακριβώς η πίεση κλιμάκωσης με την οποία άνοιξε αυτό το άρθρο.
- Secure Your Media Files by Removing Metadata with AWS Lambda στο ercan.cloud, η ίδια πειθαρχία ενός-Lambda-μίας-δουλειάς που ακολουθούν τα τέσσερα εργαλεία αυτού του μέρους, από ένα Lambda ενός σκοπού, event-driven, εντελώς έξω από την πλατφόρμα agents.
Το πλήρες module terraform/20-gateway-tools/ και agents/tools/, συμπεριλαμβανομένων των εγγράφων πολιτικής IAM και των τριών άλλων στόχων Gateway που κόπηκαν από τα αποσπάσματα παραπάνω, ζουν στο συνοδευτικό repository στο github.com/flightlesstux/agents-on-call. Για την πλευρά βάσεων δεδομένων και υποδομής της λειτουργίας πλατφορμών σαν αυτή σε κλίμακα, οι σημειώσεις πεδίου είναι στο ercan.cloud, και ο κόμβος είναι στο ercanermis.com.
Περισσότερα από τον Ercan
Δύο ακόμη ιστότοποι, ίδιος συγγραφέας, διαφορετικό έδαφος.
Cloud, AWS, EKS, Terraform, platform engineering.
Σημειώσεις πεδίου από συστήματα παραγωγής. EKS, IAM, Terraform σε κλίμακα οργανισμού, observability, βελτιστοποίηση κόστους.
Επισκεφθείτε ercan.cloud →Ο κόμβος. Σχετικά, συμβουλευτική, επικοινωνία.
Προσωπικός κόμβος και για τις δύο διαδρομές γραφής. Ποιος είμαι, πώς λειτουργεί η συμβουλευτική, πώς να επικοινωνήσετε.
Επισκεφθείτε ercanermis.com →