def set_ignore_queue(message): body = json.loads(message) name = kms_decrypted("IGNORE_QUEUE") system_alert_id = body["SystemAlertId"] region = kms_decrypted("REGION", "ap-northeast-1") sqs = boto3.resource('sqs', region_name=region) try: queue = sqs.get_queue_by_name(QueueName=name) except Exception as e: queue = sqs.create_queue(QueueName=name) queue.set_attributes(Attributes={ 'ReceiveMessageWaitTimeSeconds': '20', 'VisibilityTimeout': '30' }) print_json({ "type": "SQS", "message": "Send Message", "queue": name, "payload": message }) result = queue.send_message(MessageBody=message, MessageAttributes={ "SystemAlertId": { "StringValue": system_alert_id, 'DataType': 'String' } }) message_id = result["MessageId"] return message_id
def check_messages(event): ja = pytz.timezone("Asia/Tokyo") nt = datetime.datetime.now(ja) nt = nt.timestamp() * 1000 for message in event: body = json.loads(message['Body']) alert_id = message["MessageAttributes"]["SystemAlertId"]["StringValue"] sent_timestamp = int(message["Attributes"]["SentTimestamp"]) stime = datetime.datetime.fromtimestamp(sent_timestamp / 1000, ja) stime = "{0:%Y-%m-%d %H:%M:%S}".format(stime) lapsed_time = nt - sent_timestamp msg = None if lapsed_time < 86400000: msg = "24時間未満" # 24時間以上48時間以下 elif lapsed_time < 172800000: msg = "24時間経過" repost(message) # 48時間以上72時間以下 elif lapsed_time < 259200000: msg = "48時間経過" repost(message) # 72時間以上 else: msg = "72時間経過" set_status(alert_id) print_json({ "type": "Lambda", "message": msg, "send_time": stime, "alert": body })
def answer_false(event, action): value = action["value"] value = json.loads(value) print_json({ "type": "Slack", "message": "Interactive Event Value", "value": value, }) message = event["message"] alert_id = value["SystemAlertId"] user = event["user"], channel_id = event["container"]["channel_id"] blocks = message["blocks"] blocks[2] = { "type": "section", "block_id": "reason", "fields": [ {"type": "mrkdwn", "text": "*応答*"}, {"type": "mrkdwn", "text": "心当たりがない"}, {"type": "mrkdwn", "text": "*応答ユーザー*"}, {"type": "mrkdwn", "text": user[0]["username"]} ] } set_status(alert_id, False) slack_api_token = kms_decrypted("SLACK_API_TOKEN") slack_chat = Slack.Chat(token=slack_api_token) post_args = { "channel": channel_id, "text": message["text"], "ts": message["ts"], "blocks": blocks, } slack_chat.update_message(**post_args)
def main_function(event, context): name = kms_decrypted("IGNORE_QUEUE") region = kms_decrypted("REGION", "ap-northeast-1") sqs = boto3.client('sqs', region_name=region) url = sqs.get_queue_url(QueueName=name) url = url["QueueUrl"] print_json({"type": "SQS", "message": "Search Message", "queue": name}) while (True): result = sqs.receive_message(QueueUrl=url, MaxNumberOfMessages=10, VisibilityTimeout=30, WaitTimeSeconds=20, AttributeNames=["All"], MessageAttributeNames=["SystemAlertId"]) if "Messages" not in result: break if len(result["Messages"]) == 0: break print_json({ "type": "lambda", "message": "Lambdaを再帰呼出しします", "payload": result }) if lambda_tools.aws_request_id == "debug": lambda_handler(result, context) else: invoke(result)
def main_function(event): print_json({ "type": "Slack", "message": "Interactive Event", "payload": event, }) type = event["type"] if type == "view_submission": view_submission(event) elif type == "block_actions": block_actions(event)
def block_actions(event): for action in event["actions"]: print_json({ "type": "Slack", "message": "Interactive Event", "action": action }) action_id = action["action_id"] if (action_id == "answer_true"): call_modal(event) elif (action_id == "answer_false"): answer_false(event, action)
def set_status(system_alert_id, status, message: str = None): """ @brief Logic Appにステータスを送信 @params[in] system_alert_id Azure SentinelのSystem Alert Id @params[in] status ユーザー応答結果 @n True : 意図している @n False: 覚えがない @params[in] message 理由テキスト """ url = kms_decrypted("LOGIC_APP_URL") method = "POST" headers = {"Content-Type": "application/json"} data = { "SystemAlertId": system_alert_id, "UserResult": 0 if status else 1, } if message is not None: data["UserMessage"] = message ret = { "type": "Logic App", "id": system_alert_id, "payload": data, "status": status } data = json.dumps(data).encode("utf-8") request = urllib.request.Request( url, data=data, method=method, headers=headers ) with urllib.request.urlopen(request) as response: result = False ret["result"] = { "status": response.status, "reason": response.reason, "message": response.msg } if response.status == 200: result = True expires = response.getheader("Expires") if expires == "-1": ret["message"] = "Send Status(System Alert Id was Expired)" else: result = True else: ret["message"] = "Send Status({})".format(response.msg) ret["result"] = { "status": response.status, "reason": response.reason, "message": response.msg } print_json(ret) return result
def view_submission(event): """ @brief 入力用Modal ViewからSubmitssion Callbackの受け取り @params[in] event イベントペイロード @details 入力値を取得しprivate_metadataから呼出元メッセージを特定し @n chat.update APIにて入力値を呼び出し元に反映します """ view = event["view"] private_metadata = json.loads(view["private_metadata"]) container = private_metadata["container"] message = private_metadata["message"] channel_id = container["channel_id"] blocks = message["blocks"] user = private_metadata["user"] action_value = private_metadata["action_value"] reason = view["state"]["values"]["reason_input"]["reason_text"]["value"] status = action_value["Status"] alert_id = action_value["SystemAlertId"] message_id = action_value["MessageId"] blocks[2] = { "type": "section", "block_id": "reason", "fields": [ {"type": "mrkdwn", "text": "*応答*"}, {"type": "mrkdwn", "text": "意図している"}, {"type": "mrkdwn", "text": "*応答ユーザー*"}, {"type": "mrkdwn", "text": user["username"]}, {"type": "mrkdwn", "text": "*理由*"}, {"type": "mrkdwn", "text": reason} ] } reason = "[{}]{}".format(user["username"], reason) result = set_status(alert_id, status, reason) if result: slack_api_token = kms_decrypted("SLACK_API_TOKEN") slack_chat = Slack.Chat(token=slack_api_token) post_args = { "channel": channel_id, "text": message["text"], "ts": message["ts"], "blocks": blocks, } print_json({ "type": "Slack", "message": "Update Message", "metadata": post_args }) slack_chat.update_message(**post_args) remove_message(message_id, alert_id)
def remove_message(message_id, system_alert_id): """ @brief 指定したMessageIdとSystemAlertIdに合致するSQSメッセージの削除 @params[in] message_id SQSのメッセージId @params[in] system_alert_id Azure SentinelのアラートID """ name = kms_decrypted("IGNORE_QUEUE") region = kms_decrypted("REGION", "ap-northeast-1") sqs = boto3.resource('sqs', region_name=region) queue = sqs.get_queue_by_name(QueueName=name) id = None recipt_handle = None print_json({ "type": "SQS", "message": "Search Message", "queue": name, "message_id": message_id, }) while(True): result = queue.receive_messages( MaxNumberOfMessages=10, VisibilityTimeout=3, WaitTimeSeconds=20, MessageAttributeNames=["SystemAlertId"] ) if len(result) == 0: break for msg in result: if message_id != msg.message_id: continue said = msg.message_attributes.get("SystemAlertId", None) if said is None: continue if system_alert_id != said["StringValue"]: continue receipt_handle = msg.receipt_handle print_json({ "type": "SQS", "message": "Delete Message", 'message_id': message_id, 'receipt_handle': receipt_handle }) response = queue.delete_messages(Entries=[{ 'Id': message_id, 'ReceiptHandle': receipt_handle }]) return True return False
def google_uploader(file, stream, length, event): name = file["name"] mime = file["mimetype"] buf = io.BytesIO(stream.read()) # Google Drive用メタデータ作成 metadata = GSuite.Drive.Metadata() metadata.name = name metadata.writersCanShare = True parent_id = kms_decrypted("GSUITE_PARENT_ID") metadata.parents = [parent_id] print_json({ "type": "lambda", "message": "Slack上のFileをGoogle Driveにアップロードします", "name": name, "size": length }) # Google Driveにアップロード file = gdrive.insert( metadata=metadata, file_stream=buf, original_mime_type=mime, fields='id, webViewLink, permissions' ) print_json({ "type": "lambda", "message": "Slack上のFileをGoogle Driveにアップロードしました", "name": name, "size": length }) link = file.get("webViewLink") id = file.get("id") pids = file["permissions"] perm = {} perm["type"] = "domain" perm['role'] = 'reader' perm['domain'] = kms_decrypted("GSUITE_DOMAIN") gdrive_permission.create(file_id=id, permission=perm) link = file.get("webViewLink") return id, name, link
def set_status(system_alert_id): """ @brief Logic Appにステータスを送信 @params[in] system_alert_id Azure SentinelのSystem Alert Id """ url = kms_decrypted("LOGIC_APP_URL") method = "POST" headers = {"Content-Type": "application/json"} data = json.dumps({ "SystemAlertId": system_alert_id, }).encode("utf-8") request = urllib.request.Request(url, data=data, method=method, headers=headers) with urllib.request.urlopen(request) as response: result = False ret = { "type": "Logic App", "id": system_alert_id, } if response.status == 200: expires = response.getheader("Expires") if expires == "-1": ret["message"] = "Send Status(System Alert Id was Expired)" else: ret["message"] = "Send Status({})".format(response.msg) result = True else: ret["message"] = "Send Status({})".format(response.msg) ret["result"] = { "status": response.status, "reason": response.reason, "message": response.msg } print_json(ret) return result
def lambda_handler(event, context): get_lambda_info(context, "sentinel-alert-crawler") print_json({ "type": "lambda", "message": "イベント受信", "payload": event, }) response_url = None try: if 'Messages' in event: check_messages(event["Messages"]) else: main_function(event, context) return { "isBase64Encoded": False, "statusCode": 200, "headers": {}, "body": json.dumps({}) } except Exception as e: print_json({ "type": "lambda", "level": "error", "request-id": lambda_tools.aws_request_id, "message": str(e), "reason": error.exception_fail(e) }) return { "isBase64Encoded": False, "statusCode": 502, "headers": {}, "body": json.dumps({ "text": str(e), "response_type": "in_channel" }) }
def lambda_handler(event, context): get_lambda_info(context, "sentinel-alert-answer") print_json({ "type": "lambda", "message": "イベント受信", "payload": event, }) try: if 'body' in event: if lambda_tools.aws_request_id != "debug": verify_slack_signature(event) params = parse_qs(event['body']) print_json({ "type": "lambda", "message": "Lambdaを再帰呼出しします", "payload": params, }) if lambda_tools.aws_request_id == "debug": return lambda_handler(params, context) else: invoke(params) elif 'payload' in event: payload = event["payload"] payload = json.loads(payload[0]) main_function(payload) return { "isBase64Encoded": False, "statusCode": 200, "headers": { 'content-type': 'application/json' }, "body": "" } except Exception as e: print_json({ "type": "lambda", "level": "error", "request-id": lambda_tools.aws_request_id, "message": str(e), "reason": error.exception_fail(e) }) return { "isBase64Encoded": False, "statusCode": 502, "headers": { 'content-type': 'application/json' }, "body": json.dumps({ "text": str(e), "response_type": "in_channel" }) }
def lambda_handler(event, context): get_lambda_info(context, "sentinel-alert-receiver") print_json({ "type": "lambda", "message": "イベント受信", "payload": event, }) response_url = None try: if 'Records' in event: params = event['Records'] print_json({ "type": "lambda", "message": "Lambdaを再帰呼出しします", "payload": params, }) if lambda_tools.aws_request_id == "debug": return lambda_handler(params, context) else: invoke(params) else: main_function(event) return { "isBase64Encoded": False, "statusCode": 200, "headers": {}, "body": json.dumps({"response_type": "in_channel"}) } except Exception as e: print_json({ "type": "lambda", "level": "error", "request-id": lambda_tools.aws_request_id, "message": str(e), "reason": error.exception_fail(e) }) return { "isBase64Encoded": False, "statusCode": 502, "headers": {}, "body": json.dumps({ "text": str(e), "response_type": "in_channel" }) }
def main_function(events): slack_api_token = kms_decrypted("SLACK_API_TOKEN") slack_bot_token = kms_decrypted("SLACK_BOT_TOKEN") channel_id = kms_decrypted("SLACK_CHANNEL_ID") slack_chat = Slack.Chat(token=slack_api_token) for event in events: body = event.get("body", None) if body is None: continue id = set_ignore_queue(body) body = json.loads(body) username = body["Name"].replace('assumed-role/sso/', '') body["MessageId"] = id blocks = [] blocks.append({ "type": "section", "block_id": "alert", "text": { "type": "mrkdwn", "text": "@{}\nAzure Sentinel 警告通知".format(username) }, "fields": [ { "type": "mrkdwn", "text": "*アラート名*" }, { "type": "mrkdwn", "text": body["AlertName"] }, { "type": "mrkdwn", "text": "*アラート概要*" }, { "type": "mrkdwn", "text": body["Description"] }, ] }) blocks.append({"type": "divider"}) elements = [] body["Status"] = True elements.append({ "action_id": "answer_true", "type": "button", "text": { "type": "plain_text", "text": "意図している" }, "value": json.dumps(body) }) body["Status"] = False elements.append({ "action_id": "answer_false", "type": "button", "text": { "type": "plain_text", "text": "心当たりがない" }, "value": json.dumps(body) }) blocks.append({ "block_id": "answer", "type": "actions", "elements": elements }) post_args = { "channel": channel_id, "text": "Azure Sentinel 警告通知", "blocks": blocks, "link_names": True, "mrkdwn": True } print_json({ "type": "Slack", "message": "メッセージ送信", "channel-id": channel_id, "payload": post_args, }) result = slack_chat.post_message(**post_args)
def lambda_handler(event, context): get_lambda_info(context, "SlackUploadFileTransfer-Receiver") print_json({ "type": "lambda", "message": "イベント受信", "payload": event }) if "X-Slack-Retry-Reason" in event["headers"]: if event["headers"]["X-Slack-Retry-Reason"] == "http_timeout": return { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": "OK" } try: if isinstance(event["body"], str): slack_verification(event) event["body"] = json.loads(event["body"]) if "challenge" in event["body"]: print_json({ "type": "lambda", "message": "チャレンジイベント受信", "payload": event }) return { 'statusCode': '200', 'body': event["body"]["challenge"] } subtype = event["body"]["event"].get("subtype", None) channel = event["body"]["event"].get("channel", None) if slack_channel_ids is not None: if channel not in slack_channel_ids: return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": "OK" } if subtype == "file_share": print_json({ "type": "lambda", "message": "FileShareイベント受信", "payload": event }) print_json({ "type": "lambda", "message": "Lambdaを再帰呼出しします" }) if lambda_tools.aws_request_id == "debug": return lambda_handler(event, context) else: invoke(event) else: main_function(event, context) return { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": "OK" } except Exception as e: print_json({ "type": "lambda", "level": "error", "request-id": lambda_tools.aws_request_id, "message": str(e), "event": event, "reason": lambda_tools.exception_fail(e) }) return { "statusCode": 502, "headers": {}, "body": json.dumps({ "text": str(e) }) }
def main_function(data, context): credential_setting() body = data.get("body", {}) event = body.get("event", {}) text = event.get("text", None) channel_id = event.get("channel", None) channel_type = event.get("channel_type", None) ts = event.get("ts", None) files = event.get("files", []) user_id = event.get("user") thread_ts = event.get("thread_ts", None) blocks = event.get("blocks", None) links = [] for file in files: try: id, name, link = transfer(file, event) links.append(link) except urllib.error.HTTPError as e: if e.code == 404: print_json({ "level": "warning", "type": "Slack", "message": "Slack上にFileが存在しません", "id": file["id"], "name": file["name"] }) continue raise e except Exception as e: raise e if len(links) == 0: return # Slack処理 user_info = slack_user.info(user_id) profile = user_info["user"]["profile"] user_icon = profile.get("image_original", profile["image_192"]) user_name = profile.get("display_name") if user_name == "": user_name = profile.get("real_name") if channel_type == "im": channel_id = user_id try: slack_chat = Slack.Chat(token=slack_bot_token) message = { "channel": channel_id, "text": "{}\n{}".format(text, "\n".join(links)), "link_names": True, "username": user_name, "icon_url": user_icon, "thread_ts": thread_ts } print_json({ "type": "Slack", "message": "Slack上にメッセージをPostします", "data": message }) slack_chat.post_message(**message) except Exception as e: raise e # 古いメッセージを削除 try: print_json({ "type": "Slack", "message": "Slack上の古いメッセージを削除します", "channel": channel_id, "ts": ts }) slack_chat = Slack.Chat(token=slack_token) slack_chat.delete(channel=channel_id, ts=ts, as_user=True) except Exception as e: print_json({ "type": "Slac", "level": "error", "request-id": lambda_tools.aws_request_id, "channel": channel_id, "ts": ts, "message": "メッセージ削除に失敗しました[{}]".format(str(e)) }) # Slack上のファイルを削除 slack_file = Slack.File(token=slack_token) for file in files: try: print_json({ "type": "Slack", "message": "Slack上のファイルを削除します", "file": file["id"], "name": file["name"] }) slack_file.delete(file=file["id"]) except Exception as e: print_json({ "type": "lambda", "level": "error", "request-id": lambda_tools.aws_request_id, "message": "ファイル削除に失敗しました[{}]".format(str(e)), "file": file["id"], "name": file["name"] })
def box_uploader(file, stream, length, event): user_info = slack_user.info(event["user"]) user_name = user_info["user"]["profile"]["display_name"] if user_name == "": user_name = user_info["user"]["name"] parent_id = box_create_user_folder(user_name) name = file["name"].split(".") base = name[0] ext = name[1] # Boxにアップロードが可能かPreFlightCheck name = "{}.{}".format(base, ext) try: box_file.preflight(name, parent_id, file["size"]) except Exception as e: if e.code == "item_name_in_use": name = "{} ({}).{}".format(base, event["ts"], ext) else: raise e print_json({ "type": "Box", "message": "Slack上のFileをBoxにアップロードします", "name": name, "size": length }) # Upload try: if length <= 20000000: uploaded_file = box_file.upload( folder_id=parent_id, stream=io.BytesIO(stream.read()), name=name, overwrite=True ) else: # Chunk upload session = box_file.client.folder( folder_id=parent_id ).create_upload_session(file_size=length, file_name=name) parts = [] sha1 = hashlib.sha1() for part_index in range(session.total_parts): copied_length = 0 chunk = b'' while copied_length < session.part_size: buffer = stream.read(session.part_size - copied_length) if buffer is None: continue if len(buffer) == 0: break chunk += buffer copied_length += len(buffer) uploaded_part = session.upload_part_bytes( chunk, part_index*session.part_size, length) parts.append(uploaded_part) updated_sha1 = sha1.update(chunk) content_sha1 = sha1.digest() uploaded_file = session.commit( content_sha1=content_sha1, parts=parts) link = uploaded_file.get_shared_link() print_json({ "type": "Box", "message": "Slack上のFileをBoxにアップロードしました", "name": uploaded_file.name, "id": uploaded_file.id, "link": link }) return uploaded_file.id, uploaded_file.name, link except Exception as e: raise e