Created
December 19, 2019 20:14
-
-
Save Cyberax/71030b46ebeb0ccda48e471664cd5540 to your computer and use it in GitHub Desktop.
Revisions
-
Cyberax created this gist
Dec 19, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,115 @@ #!/usr/bin/python3 # Interweave two JSON log streams import json import os import sys import time import urllib.request from os import getenv from subprocess import Popen, PIPE, STDOUT, check_output, check_call, call from sys import stdout from threading import Lock, Thread AGENT = "/usr/bin/amazon-ssm-agent" def weave(stream, output, lock): while True: # noinspection PyBroadException try: ln = stream.readline() with lock: output.write(ln) except Exception: break def init_ssm(): # Get the optional task information meta_url = getenv("ECS_CONTAINER_METADATA_URI", "") creds_meta_url = getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "") if not meta_url or not creds_meta_url: return False contents = urllib.request.urlopen(meta_url + "/task").read() meta = json.loads(contents.decode("utf-8")) # E.g.: arn:aws:ecs:us-east-1:3412341234241:task\/e36711e4-0272-4f08-83e4-b80d36b9e180 taskId = meta['TaskARN'].split("/")[-1] creds_contents = urllib.request.urlopen("http://169.254.170.2" + creds_meta_url).read() creds_meta = json.loads(creds_contents.decode("utf-8")) tags = [ "--tags", "Key=Name,Value=%s" % taskId, "Key=TaskId,Value=%s" % taskId, "Key=Role,Value=Fargate", "Key=Cluster,Value=%s" % meta['Cluster'], "Key=TaskFamily,Value=%s" % meta['Family'], "Key=TaskRevision,Value=%s" % meta['Revision'], ] # arn:aws:iam::123412341234124:role/service-role/ApolloAlphaTask transformed # to service-role/ApolloAlphaTask ssm_role = "service-role/" + creds_meta["RoleArn"].split("/")[-1] # E.g.: arn:aws:ecs:us-east-1:123412341234:task\/e36711e4-0272-4f08-83e4-b80d36b9e180 ssm_region = meta["TaskARN"].split(":")[3] # arn:aws:ecs:us-east-1:1234123411313:cluster\/ApolloAlpha cluster_name = meta["Cluster"].split("/")[-1] ssm_desc = cluster_name + " Task" act_out = check_output(["aws", "ssm", "create-activation", "--iam-role", ssm_role, "--region", ssm_region, "--description", ssm_desc, "--default-instance-name", "t-"+taskId] + tags) act_data = json.loads(act_out.decode("utf-8")) print('{"msg": "Received activation id %s"}' % (act_data["ActivationId"])) # Register the instance in SSM check_call([AGENT, "-register", "-y", "-code", act_data["ActivationCode"], "-id", act_data["ActivationId"], "-region", ssm_region, "-i", taskId]) print('{"msg": "Finished activation"}') # Now we should have the machine-id, so we can link the task with it! with open("/var/lib/amazon/ssm/registration") as fl: reg_data = json.load(fl) machine_id = reg_data["ManagedInstanceID"] call(["aws", "ecs", "tag-resource", "--resource-arn", meta['TaskARN'], "--tags", "key=MachineId,value=%s" % machine_id]) # Allow sudo access with open("/etc/sudoers.d/ssm", "w") as fl: fl.write('Defaults:ssm-user env_keep += *\n') fl.write("ssm-user ALL=(ALL) NOPASSWD:ALL\n") return True def main(): mtx = Lock() if not init_ssm(): # No SSM? Just launch the target directly os.execlp(sys.argv[1], *sys.argv[1:]) ssm_process = Popen([AGENT], stdin=None, stdout=PIPE, stderr=STDOUT) Thread(target=lambda: weave(ssm_process.stdout, stdout.buffer, mtx), name="SSMPump").start() delegated = Popen(sys.argv[1:], stdin=sys.stdin, stdout=PIPE, stderr=sys.stderr) Thread(target=lambda: weave(delegated.stdout, stdout.buffer, mtx), name="Delegated").start() start = time.time() delegated.wait() # If the target has exited too fast (in less than 5 mins) then wait for # 15 minutes before stopping the SSM to give us ability to log in and debug # the issue (and to reduce the churn velocity). if time.time() - start < 300: time.sleep(900) ssm_process.kill() ssm_process.wait() if __name__ == "__main__": main()