AWS S3 a une "URL signée". SQS fait quelque chose de similaire à cette fonctionnalité.
Par exemple, supposons que vous souhaitiez créer un système comme celui-ci.
Considérez la configuration sur AWS.
** <Configuration A: API Gateway + Lambda> **
Lambda + API Gateway a la réputation d'être bon marché, mais elle peut être assez coûteuse pour traiter un grand nombre de requêtes.
C'est un calcul approximatif, mais lorsqu'il y a 172,8 millions de demandes par jour, les frais d'accumulation de données sont les suivants.
un service | Prix par million de demandes | Charge journalière | conditions |
---|---|---|---|
Lambda | Dépend des conditions | 387 USD | Traitement moyen d'une seconde, allocation de mémoire 128 Mo |
Api Gateway | 4.25 USD | 731 USD | REST API |
SQS | 0.4 USD | 69 USD |
Total: 1187 $ par jour
Avec les retards Lambda, WAF, CloudFront, l'authentification et plus encore, cela va encore plus haut.
** <Configuration B: AWS IoT + Rule Engine + SQS> **
Le prix sera inférieur si la configuration consiste à transférer d'AWS IoT vers SQS avec le moteur de règles. Mais cette fois ...
un service | Prix par million de demandes | Charge journalière | conditions |
---|---|---|---|
AWS IoT | Envoyer: 1.2 USD Règle: 0.18 USD Action: 0.18 USD |
268 USD | règle+Passer à l'action |
SQS | 0.4 USD | 69 USD |
Total: 337 $ par jour
Cette fois, MQTT est impossible en raison du port, et WebSocket est également impossible en raison du côté terminal.
**
D'ailleurs, si vous pouvez envoyer directement du terminal à SQS, cela vous coûtera environ 1/20, 69 dollars par jour. Il y a un problème de sécurité car l'AWS-SDK ne peut pas être utilisé. Quelque chose de plus que ça ...
C'est probablement Sorya.
** <Configuration D: URL signée SQS +> **
Si vous y réfléchissez bien, il semble que vous puissiez bien le faire si vous pouvez l'envoyer directement à SQS avec une authentification qui n'utilise pas le SDK.
Afin de créer un tel mécanisme, nous réaliserons une URL signée SQS +.
** Le traitement à exécuter et le calendrier d'exécution de chacun sont les suivants **
En traitement | Cible | Horaire |
---|---|---|
Obtenir l'URL signée SQS | Terminal → Passerelle API | Exécuter une fois toutes les 15 minutes |
Envoyer l'état du terminal à SQS | Terminal → SQS | Exécuter une fois toutes les 5 secondes |
** Les données lancées par SQS sont supposées être les suivantes cette fois **
Données à envoyer | Contenu des données |
---|---|
Nom de la file d'attente SQS | sqs-send-request-test-0424 |
Schéma des données à transmettre | Open/Ouvert (terminal activé, sentiment humain activé) Close/Ouvert (terminal éteint, sentiment humain activé) Open/Fermer (terminal activé, sensation humaine désactivée) Close/Fermer (terminal éteint, sensation humaine désactivée) * Envoyez l'une des 4 façons |
Demander une URL signée à API Gateway [^ 1]
[^ 1]: * L'authentification API Gateway est omise cette fois. Si vous souhaitez vous authentifier, veuillez mettre les informations d'authentification dans l'en-tête des données transmises.
Demande côté terminal
curl "https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/url" -s -X POST \
-d '{"que_name":"sqs-send-request-test-0424", "patterns":["Open/Open","Close/Open","Open/Close","Close/Close"]}'
Si la demande aboutit, vous recevrez le même nombre d'URL signées que le modèle de données.
Données reçues côté terminal
{
"url": {
"Open/Open": "https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/sqs-send-request-test-0424?AWSAccessKeyId=xxxxx&Action=SendMessage&MessageBody=Open%2FOpen&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-04-30T10%3A42%3A54&Version=2012-11-05&Signature=xxxxxxxxxxxxxxxxxxxxx",
"Close/Open": "https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/sqs-send-request-test-0424?AWSAccessKeyId=xxxxx&Action=SendMessage&MessageBody=Close%2FOpen&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-04-30T10%3A42%3A54&Version=2012-11-05&Signature=xxxxxxxxxxxxxxxxxxxxx",
"Open/Close": "https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/sqs-send-request-test-0424?AWSAccessKeyId=xxxxxx&Action=SendMessage&MessageBody=Open%2FClose&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-04-30T10%3A42%3A54&Version=2012-11-05&Signature=xxxxxxxxxxxxxxxxxxxxx",
"Close/Close": "https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/sqs-send-request-test-0424?AWSAccessKeyId=xxxxxx&Action=SendMessage&MessageBody=Close%2FClose&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-04-30T10%3A42%3A54&Version=2012-11-05&Signature=xxxxxxxxxxxxxxxxxxxxx"
}
}
L'URL à envoyer correspond aux données des données ["url"] [$ {state you want to send}]. Dans l'exemple, "Ouvrir / Fermer" est envoyé.
Demande côté terminal
curl "https://sqs.ap-northeast-1.amazonaws.com/xxxxxx/sqs-send-request-test-0424?AWSAccessKeyId=xxxxxx&Action=SendMessage&MessageBody=Open%2FClose&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2020-04-30T10%3A42%3A54&Version=2012-11-05&Signature=xxxxxxxxxxxxxxxxxxxxx"
Chaque fois que vous l'exécutez, un message $ {state you want to send} sera enregistré dans le sujet défini. Dans l'exemple, "Ouvrir / Fermer" est enregistré dans "sqs-send-request-test-0424".
En guise de préparation préliminaire, faites une file d'attente avec SQS. Entrez le nom de la file d'attente que vous aimez et cliquez sur "File d'attente rapide" en bas de l'écran.
Créez également un utilisateur IAM dédié à SQS. Définissez AmazonSQSFullAccess sur la stratégie. Aucun privilège autre que SQS n'est requis.
Après avoir créé l'utilisateur IAM, obtenez l'ID de la clé d'accès et la clé d'accès secrète.
Créez Lambda avec Python 3.8.
Définissez les variables d'environnement Lambda comme suit.
Clé de variable d'environnement | Valeur à définir |
---|---|
AWS_ACCOUNT_NUMBER | Numéro d'identification de compte AWS (Le numéro à 12 chiffres en bas à gauche de l'écran IAM) |
SQS_ACCOUNT_ID | ID de clé d'accès utilisateur IAM dédié à SQS |
SQS_SECRET_KEY | Clé d'accès secrète pour les utilisateurs IAM dédiée à SQS |
Collez la source suivante dans Lambda.
Lambda
# coding: utf-8
import boto3, json, hashlib, hmac, base64, os
from datetime import datetime, timezone, timedelta
from urllib import parse as url_encode
#Informations de signature (format SHA256, la version de signature est 2)
SIGNATURE_METHOD = "HmacSHA256"
SIGNATURE_VERSION = "2"
ENCODE = "utf-8"
#Informations sur la méthode SQS (envoyer un message à la file d'attente à l'aide de l'API REST GET)
HTTP_METHOD = "GET"
SQS_METHOD = "SendMessage"
AWS_VERSION = "2012-11-05"
#Fuseau horaire
UTC_TIMEZONE = timezone(timedelta(hours = 0), 'UTC')
class Credentials:
"""
Définir les informations d'identification de l'utilisateur IAM
"""
@staticmethod
def from_iam():
instance = Credentials()
instance.aws_access_key = os.environ.get("SQS_ACCOUNT_ID", DEFAULT.SQS_ACCOUNT_ID)
instance.aws_secret_key = os.environ.get("SQS_SECRET_KEY", DEFAULT.SQS_SECRET_KEY)
return instance
class Endpoint:
"""
Définir les informations du point de terminaison SQS
"""
def __init__(self, topic_name):
self.protocol = "https"
self.host_name = "sqs.{}.amazonaws.com".format(os.environ.get("AWS_REGION", DEFAULT.AWS_REGION))
self.url_path = "/{}/{}".format(os.environ.get("AWS_ACCOUNT_NUMBER", DEFAULT.AWS_ACCOUNT_NUMBER), topic_name)
@property
def url(self):
return f"{self.protocol}://{self.host_name}{self.url_path}"
#Créer une URL signée SQS
def create_presigned_url(credential, endpoint, message):
#Obtenez la date et l'heure actuelles avec UTC et convertissez-les en chaîne de caractères
current_date = datetime.now().astimezone(UTC_TIMEZONE)
current_date_str = url_encode.quote(
current_date.strftime('%Y-%m-%dT%H:%M:%S')
)
#Créer un hachage avec une clé d'accès secrète en fonction des données à envoyer
return endpoint.url + "?" + create_query(SQS_METHOD, message, credential.aws_access_key, current_date_str, option = {
#Étant donné que les données de hachage sont un tableau d'octets, l'encodage d'URL + l'encodage en base64 afin qu'il puisse être envoyé par GET.
"Signature" : url_encode.quote(
base64.b64encode(
sign(
#Spécifiez la clé d'accès secrète à utiliser pour la signature
credential.aws_secret_key.encode(ENCODE),
#Spécifiez les données de transmission (les données spécifiées sont hachées avec SHA256)
create_certificate(endpoint, SQS_METHOD, message, credential.aws_access_key, current_date_str)
)
)
)
})
#Hash avec clé d'accès secrète
def sign(key, msg):
return hmac.new(key, msg.encode(ENCODE), hashlib.sha256).digest()
#Créer des données de signature au format v2
#Se référer au document officiel pour le format (https)://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-2.html)
def create_certificate(endpoint, method, message_body, aws_access_key, current_date_str):
return "\n".join([
HTTP_METHOD,
endpoint.host_name,
endpoint.url_path,
create_query(method, message_body, aws_access_key, current_date_str)
])
#Normaliser les données de requête
#Se référer au document officiel pour le format (https)://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-2.html)
def create_query(method, message_body, aws_access_key, current_date_str, option = None):
query_map = {
"AWSAccessKeyId" : aws_access_key,
"Action" : method,
"MessageBody" : message_body,
"SignatureMethod" : SIGNATURE_METHOD,
"SignatureVersion" : SIGNATURE_VERSION,
"Timestamp" : current_date_str,
"Version" : AWS_VERSION
}
#La signature ne doit pas être incluse dans le hachage, uniquement lors de l'envoi
#Ajouter des données à la requête si Signature est spécifiée
if option is not None:
query_map.update(option)
#Convertir au format de requête GET
return "&".join([
f"{key}={value}"
for key, value in query_map.items()
])
#Créez autant d'URL signées que vous le souhaitez
def request(credential, endpoint, data_patterns):
url = {}
for pattern in data_patterns:
url[pattern] = create_presigned_url(credential, endpoint, url_encode.quote(pattern, safe = ""))
return {
"url" : url
}
#Obtenez les données POSTées à partir des arguments APIGateway (HTTP API) (les données sont encodées en base64 du côté APIGateway)
def get_payload_from_event(event):
payload_str = ""
if event["isBase64Encoded"]:
payload_str = base64.b64decode(event["body"].encode(ENCODE))
else:
payload_str = event["body"]
return json.loads(payload_str)
#Point d'entrée lors de l'exécution de Lambda
def lambda_handler(event, context):
payload = get_payload_from_event(event)
return {
'statusCode': 200,
'body': json.dumps(request(Credentials.from_iam(), Endpoint(payload["que_name"]), payload["patterns"]))
}
# ---------------------------------------------
#Ce qui suit est pour une exécution locale
#Spécifier lors de l'exécution sur autre que Lambda
# ---------------------------------------------
#Variables pour l'exécution locale
class _Default:
def __init__(self):
self.SQS_ACCOUNT_ID = "" #Spécifiez l'ID de clé d'accès IAM
self.SQS_SECRET_KEY = "" #Spécifiez la clé secrète IAM
self.AWS_ACCOUNT_NUMBER = "" #Spécifiez l'ID de compte AWS
self.AWS_REGION = "ap-northeast-1" #Spécifiez la région où se trouve SQS
self.SQS_QUEUE_NAME = "" #Spécifiez le nom de la file d'attente de destination
DEFAULT = _Default()
#Point d'entrée lors de l'exécution du débogage de l'environnement local
if __name__ == "__main__":
print(json.dumps(request(Credentials.from_iam(), Endpoint(DEFAULT.SQS_QUEUE_NAME), [
"Open/Open", "Close/Open", "Wait/Open",
"Open/Close", "Close/Close", "Wait/Close",
"Open/Wait", "Close/Wait", "Wait/Wait"
])))
Définissez le Lambda créé comme back-end d'API Gateway.
Si vous déménagez, ce n'est pas grave si vous avez lu jusqu'ici. Après avoir déployé API Gateway, cela devrait fonctionner comme le "dessin terminé".
À partir de maintenant, ce sera une histoire détaillée.
À l'origine, SQS dispose d'un mécanisme pour envoyer des messages par GET ou POST sans passer par AWS-SDK.
Référence: faire une requête d'API de requête https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-making-api-requests.html
La méthode de demande la plus simple est la suivante. Lorsque vous frappez l'URL sur le site de référence, un message (données) sera envoyé à la file d'attente (MyQueue). [^ 2]
[^ 2]: * Version est la version de SQS. Pour le moment, vous pouvez l'envoyer sans penser à rien.
https://sqs.us-east-2.amazonaws.com/123456789012/MyQueue ?
Version = 2012-11-05 &
Action = SendMessage &
MessageBody = data
Notez que cette requête volera les messages d'utilisateurs anonymes. La file d'attente par défaut n'arrive pas avec une erreur d'autorisation.
Pour recevoir des messages dans cet état, vous devez les avoir configurés pour accepter la divulgation publique. Modifiez les autorisations de la file d'attente telles que capturées et cochez "Tout le monde".
Si vous souhaitez envoyer à un utilisateur spécifique sans paramètre d'acceptation de la version publique, définissez «Qui suis-je».
https://sqs.us-east-2.amazonaws.com/123456789012/MyQueue ?
Version = 2012-11-05 &
Action = SendMessage &
MessageBody = data &
AWSAccessKeyId = AKIA****************
Depuis que j'ai défini l'AWSAccessKeyID, "quelqu'un" peut être communiqué. Cependant, je ne sais pas si c'est vraiment la personne. Vous ne pouvez vous faire passer pour vous-même.
Utilisez la clé d'accès secrète pour prouver votre identité. Cependant, comme il n'est pas possible d'envoyer la clé d'accès secrète telle quelle, nous enverrons la version hachée.
https://sqs.us-east-2.amazonaws.com/123456789012/MyQueue ?
Version = 2012-11-05 &
Action = SendMessage &
MessageBody = data &
AWSAccessKeyId = AKIA**************** &
Signature = ********************************** &
SignatureMethod = HmacSHA256 &
SignatureVersion = 2 &
Timestamp = 2020-04-30T10:42:54
La signature est un hachage des données à envoyer, en utilisant la clé d'accès secrète comme clé. Les données à envoyer sont l'ensemble de la requête à l'exclusion de la signature et des informations d'hôte combinées.
Signature Method est l'algorithme utilisé pour hacher Signature. Puisqu'il s'agit du HMAC-SHA256, je vais en parler à AWS.
La version de signature est la version de signature. Cette fois, j'utilise la version 2 de signature.
TimeStamp est la date et l'heure hachées.
Remarque: processus de signature de la version 2 de signature https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-2.html
Le comportement est différent de celui de "l'URL signée" du S3 d'origine.
** La plage dans laquelle les données peuvent être modifiées après l'émission de l'URL est différente **
Dans SQS, la signature change en fonction du contenu du message envoyé. Avec S3, vous pouvez envoyer avec la même signature quel que soit le contenu du fichier que vous envoyez.
Pour envoyer différents messages avec SQS, vous devez émettre autant de signatures.
** La durée de vie des informations d'identification temporaires transmises par STS est différente **
Pour S3, l'ID de clé d'accès temporaire émis par Lambda sera disponible jusqu'à l'expiration de l'URL signée. Que se passe-t-il si vous émettez et accordez également un ID de clé d'accès temporaire et une clé d'accès secrète dans SQS?
Étant donné que l'ID de la clé d'accès disparaît à la fermeture de Lambda, l'authentification ne passera pas lorsque Lambda renvoie l'URL à API Gateway.
Recommended Posts