def get_port_from_custom_rules(method, path, data, headers): """ Determine backend port based on custom rules. """ # detect S3 presigned URLs if 'AWSAccessKeyId=' in path or 'Signature=' in path: return config.PORT_S3 # heuristic for SQS queue URLs if is_sqs_queue_url(path): return config.PORT_SQS # DynamoDB shell URLs if path.startswith('/shell') or path.startswith('/dynamodb/shell'): return config.PORT_DYNAMODB data_bytes = to_bytes(data or '') if path == '/' and to_bytes('QueueName=') in data_bytes: return config.PORT_SQS # TODO: move S3 public URLs to a separate port/endpoint, OR check ACLs here first stripped = path.strip('/') if method in ['GET', 'HEAD'] and '/' in stripped: # assume that this is an S3 GET request with URL path `/<bucket>/<key ...>` return config.PORT_S3 if stripped and '/' not in stripped: if method == 'PUT': # assume that this is an S3 PUT bucket request with URL path `/<bucket>` return config.PORT_S3 if method == 'POST' and is_s3_form_data(data_bytes): # assume that this is an S3 POST request with form parameters or multipart form in the body return config.PORT_S3
def get_api_from_custom_rules(method, path, data, headers): """ Determine backend port based on custom rules. """ # detect S3 presigned URLs if 'AWSAccessKeyId=' in path or 'Signature=' in path: return 's3', config.PORT_S3 # heuristic for SQS queue URLs if is_sqs_queue_url(path): return 'sqs', config.PORT_SQS # DynamoDB shell URLs if path.startswith('/shell') or path.startswith('/dynamodb/shell'): return 'dynamodb', config.PORT_DYNAMODB # API Gateway invocation URLs if ('/%s/' % PATH_USER_REQUEST) in path: return 'apigateway', config.PORT_APIGATEWAY data_bytes = to_bytes(data or '') if path == '/' and b'QueueName=' in data_bytes: return 'sqs', config.PORT_SQS if path.startswith('/2015-03-31/functions/'): return 'lambda', config.PORT_LAMBDA # TODO: move S3 public URLs to a separate port/endpoint, OR check ACLs here first stripped = path.strip('/') if method in ['GET', 'HEAD'] and '/' in stripped: # assume that this is an S3 GET request with URL path `/<bucket>/<key ...>` return 's3', config.PORT_S3 # detect S3 URLs if stripped and '/' not in stripped: if method == 'HEAD': # assume that this is an S3 HEAD bucket request with URL path `/<bucket>` return config.PORT_S3 if method == 'PUT': # assume that this is an S3 PUT bucket request with URL path `/<bucket>` return 's3', config.PORT_S3 if method == 'POST' and is_s3_form_data(data_bytes): # assume that this is an S3 POST request with form parameters or multipart form in the body return 's3', config.PORT_S3 if stripped.count('/') == 1 and method == 'PUT': # assume that this is an S3 PUT bucket object request with URL path `/<bucket>/object` return 's3', config.PORT_S3 # detect S3 requests sent from aws-cli using --no-sign-request option if 'aws-cli/' in headers.get('User-Agent', ''): return 's3', config.PORT_S3 # S3 delete object requests if method == 'POST' and 'delete=' in path and b'<Delete' in data_bytes and b'<Key>' in data_bytes: return 's3', config.PORT_S3 # SQS queue requests if ('QueueUrl=' in path and 'Action=' in path) or (b'QueueUrl=' in data_bytes and b'Action=' in data_bytes): return 'sqs', config.PORT_SQS
def get_api_from_custom_rules(method, path, data, headers): """ Determine backend port based on custom rules. """ # detect S3 presigned URLs if 'AWSAccessKeyId=' in path or 'Signature=' in path: return 's3', config.PORT_S3 # heuristic for SQS queue URLs if is_sqs_queue_url(path): return 'sqs', config.PORT_SQS # DynamoDB shell URLs if path.startswith('/shell') or path.startswith('/dynamodb/shell'): return 'dynamodb', config.PORT_DYNAMODB # API Gateway invocation URLs if ('/%s/' % PATH_USER_REQUEST) in path: return 'apigateway', config.PORT_APIGATEWAY data_bytes = to_bytes(data or '') if path == '/' and b'QueueName=' in data_bytes: return 'sqs', config.PORT_SQS if 'Action=ConfirmSubscription' in path: return 'sns', config.PORT_SNS if path.startswith('/2015-03-31/functions/'): return 'lambda', config.PORT_LAMBDA if b'Action=AssumeRoleWithWebIdentity' in data_bytes or 'Action=AssumeRoleWithWebIdentity' in path: return 'sts', config.PORT_STS if b'Action=AssumeRoleWithSAML' in data_bytes or 'Action=AssumeRoleWithSAML' in path: return 'sts', config.PORT_STS # CloudWatch backdoor API to retrieve raw metrics if path.startswith(PATH_GET_RAW_METRICS): return 'cloudwatch', config.PORT_CLOUDWATCH # SQS queue requests if ('QueueUrl=' in path and 'Action=' in path) or (b'QueueUrl=' in data_bytes and b'Action=' in data_bytes): return 'sqs', config.PORT_SQS # TODO: move S3 public URLs to a separate port/endpoint, OR check ACLs here first stripped = path.strip('/') if method in ['GET', 'HEAD'] and '/' in stripped: # assume that this is an S3 GET request with URL path `/<bucket>/<key ...>` return 's3', config.PORT_S3 # detect S3 URLs if stripped and '/' not in stripped: if method == 'HEAD': # assume that this is an S3 HEAD bucket request with URL path `/<bucket>` return 's3', config.PORT_S3 if method == 'PUT': # assume that this is an S3 PUT bucket request with URL path `/<bucket>` return 's3', config.PORT_S3 if method == 'POST' and is_s3_form_data(data_bytes): # assume that this is an S3 POST request with form parameters or multipart form in the body return 's3', config.PORT_S3 # detect S3 requests sent from aws-cli using --no-sign-request option if 'aws-cli/' in headers.get('User-Agent', ''): return 's3', config.PORT_S3 # S3 delete object requests if method == 'POST' and 'delete=' in path and b'<Delete' in data_bytes and b'<Key>' in data_bytes: return 's3', config.PORT_S3 # Put Object API can have multiple keys if stripped.count('/') >= 1 and method == 'PUT': # assume that this is an S3 PUT bucket object request with URL path `/<bucket>/object` # or `/<bucket>/object/object1/+` return 's3', config.PORT_S3 auth_header = headers.get('Authorization') or '' # detect S3 requests with "AWS id:key" Auth headers if auth_header.startswith('AWS '): return 's3', config.PORT_S3
def get_api_from_custom_rules(method, path, data, headers): """Determine backend port based on custom rules.""" # API Gateway invocation URLs if ("/%s/" % PATH_USER_REQUEST) in path: return "apigateway", config.service_port("apigateway") # detect S3 presigned URLs if "AWSAccessKeyId=" in path or "Signature=" in path: return "s3", config.service_port("s3") # heuristic for SQS queue URLs if is_sqs_queue_url(path): return "sqs", config.service_port("sqs") # DynamoDB shell URLs if path.startswith("/shell") or path.startswith("/dynamodb/shell"): return "dynamodb", config.service_port("dynamodb") data_bytes = to_bytes(data or "") version, action = extract_version_and_action(path, data_bytes) def _in_path_or_payload(search_str): return to_str(search_str) in path or to_bytes(search_str) in data_bytes if path == "/" and b"QueueName=" in data_bytes: return "sqs", config.service_port("sqs") if "Action=ConfirmSubscription" in path: return "sns", config.service_port("sns") if path.startswith("/2015-03-31/functions/"): return "lambda", config.service_port("lambda") if _in_path_or_payload("Action=AssumeRoleWithWebIdentity"): return "sts", config.service_port("sts") if _in_path_or_payload("Action=AssumeRoleWithSAML"): return "sts", config.service_port("sts") if _in_path_or_payload("Action=AssumeRole"): return "sts", config.service_port("sts") # CloudWatch backdoor API to retrieve raw metrics if path.startswith(PATH_GET_RAW_METRICS): return "cloudwatch", config.service_port("cloudwatch") # SQS queue requests if _in_path_or_payload("QueueUrl=") and _in_path_or_payload("Action="): return "sqs", config.service_port("sqs") if matches_service_action("sqs", action, version=version): return "sqs", config.service_port("sqs") # SNS topic requests if matches_service_action("sns", action, version=version): return "sns", config.service_port("sns") # TODO: move S3 public URLs to a separate port/endpoint, OR check ACLs here first stripped = path.strip("/") if method in ["GET", "HEAD"] and stripped: # assume that this is an S3 GET request with URL path `/<bucket>/<key ...>` return "s3", config.service_port("s3") # detect S3 URLs if stripped and "/" not in stripped: if method == "PUT": # assume that this is an S3 PUT bucket request with URL path `/<bucket>` return "s3", config.service_port("s3") if method == "POST" and is_s3_form_data(data_bytes): # assume that this is an S3 POST request with form parameters or multipart form in the body return "s3", config.service_port("s3") # detect S3 requests sent from aws-cli using --no-sign-request option if "aws-cli/" in headers.get("User-Agent", ""): return "s3", config.service_port("s3") # S3 delete object requests if (method == "POST" and "delete=" in path and b"<Delete" in data_bytes and b"<Key>" in data_bytes): return "s3", config.service_port("s3") # Put Object API can have multiple keys if stripped.count("/") >= 1 and method == "PUT": # assume that this is an S3 PUT bucket object request with URL path `/<bucket>/object` # or `/<bucket>/object/object1/+` return "s3", config.service_port("s3") auth_header = headers.get("Authorization") or "" # detect S3 requests with "AWS id:key" Auth headers if auth_header.startswith("AWS "): return "s3", config.service_port("s3") # certain EC2 requests from Java SDK contain no Auth headers (issue #3805) if b"Version=2016-11-15" in data_bytes: return "ec2", config.service_port("ec2")