[PYTHON] Users without an account access the AWS Management Console

Introduction

Do you guys use AWS in your business? It's very convenient because it supports AWS, infrastructure construction and operation. It's not just the infrastructure guys, but the features that benefit everyone who connects to the environment, like CloudWatch and Session Manager, are often released.

By the way (problem raised)

These great AWS features just benefit from infrastructure, and often don't reach consultants and product developers very often. The reason is that the number of people who can use the "management console" is limited. AWS is getting better and better, but it's a waste to have a limited number of people who can use it. On the other hand, it's hard to implement interfaces so that they can be used by various people, and it's also awkward to have to deal with each time AWS expands.

Then (solution)

Why not make the "Management Console", the strongest interface that can access all the functions of AWS, available to "people who are not assigned AWS users"? This is the main subject of this article.

What to use this time

1.Amazon Cognito https://dev.classmethod.jp/cloud/aws/what-is-the-cognito/ Cognito is a user identity-themed service announced in July 2014. Read "Kogunito". Simply put, it is a function that can provide resources in AWS separately for each user, for example, you can create a file upload area dedicated to Mr. A.

2. Allow custom identity brokers access to the AWS console

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html#STSConsoleLink_programPython To give users who sign in to your organization's network secure access to the AWS Management Console, you can write and run code that generates a URL for it. This URL contains the sign-in token obtained from AWS and uses it to authenticate the user to AWS.

3.Python(boto3) Use a module called boto3 to operate AWS API with python. https://aws.amazon.com/jp/sdk-for-python/

How to use (Overview)

You can use the Amazon Cognito ID Pool to identify AWS users who have policies with restricted access. They also have an access key that you can use to generate the login URL provided in Allowing Custom Identity Brokers to Access the AWS Console. To do this, we need to prepare some resources on the AWS account, so we will explain them as well.

AWS resource preparation

1. Add access to the user used to operate AWS resources

Attach the AmazonCognitoPowerUser policy and the AmazonCognitoDeveloperAuthenticatedIdentities policy.

2. Creating an ID Pool

image.png

From the URL below, create an area called IDPool that stores user information. https://ap-northeast-1.console.aws.amazon.com/cognito/create/?region=ap-northeast-1# 1.Identity Pool Name = Give it any name you like.

  1. Check "Enable Access to Unauthenticated Identities" to allow anonymous identities in Unauthenticated Identities. Since we will not explain the authentication logic this time, we will issue an access key without authentication.

  2. When using an unauthenticated user, you cannot authorize the use of the management console without using the basic (classic) flow, so check "Allow basic (classic) flow".

  3. Create a pool.

  4. The page of Identify the IAM roles to use with your new identity pool will be displayed. Please display the details. (By the way, the top is the authenticated ID and the bottom is the guest ID setting) image.png

  5. Create a new role. You can change the authority for authenticated and unauthenticated, so create Unauth and Authed respectively. By the way, I don't plan to use Authed this time, so it doesn't matter if the contents are appropriate.

  6. Attach ReadOnlyAccess and the following to Unauth. This time, let's make Session Manager an available guest role.



{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Cognito",
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "sts:GetFederationToken"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "Ssm",
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateDataChannel",
                "s3:GetEncryptionConfiguration",
                "ssm:UpdateInstanceInformation",
                "ssmmessages:OpenDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:CreateControlChannel",
                "ssm:StartSession"
            ],
            "Resource": "*"
        }
    ]
}
		
  1. Make a note of the ID pool ID written in the sample code. image.png

Python implementation

1. Implement login_test.py by replacing the credential setting part with cognito from the following python code.

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html#STSConsoleLink_programPython


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#Execute as follows.
#python3 login_test.py user_name

import urllib, json, sys
import requests
import boto3
import datetime

_args = sys.argv
_USER                       = _args[1]
_AWS_ACCOUNT                = "123456789012"
_CONV_AWS_ACCESS_KEY_ID     = "ASIA*******:"
_CONV_AWS_SECRET_ACCESS_KEY = "**********"
_CONV_AWS_SESSION_TOKEN     = "*********"
_REGION_NAME                = "ap-northeast-1"
_ID_POOL_ID                 = "Use the one you noted in step 8."
_ARN                        = "arn:aws:cognito-identity:ap-northeast-1:123456789012:identitypool/ap-northeast-1:*******"
_ROLE_ARN                   = "arn:aws:iam::123456789012:role/Unauth"

class TestClass():
    def cognito_auth(self):
        try:
            #Creating a client to operate cognit
            cognito_client = boto3.client('cognito-identity',
                region_name           = _REGION_NAME,
                aws_access_key_id     = _CONV_AWS_ACCESS_KEY_ID,
                aws_secret_access_key = _CONV_AWS_SECRET_ACCESS_KEY,
                aws_session_token     = _CONV_AWS_SESSION_TOKEN #Not needed if you're not using MFA
            )
            #ID acquisition
            user_id = cognito_client.get_id(
                AccountId      = _AWS_ACCOUNT,
                IdentityPoolId = _ID_POOL_ID
            )["IdentityId"]
            print("Cognito ID:"+user_id)
        except:
            raise Exception("get id faild.")

        try:
            #Role setting check
            roles = cognito_client.get_identity_pool_roles(IdentityPoolId=_ID_POOL_ID)["Roles"]["unauthenticated"]
            print("Use role:"+roles)

            # GetOpenIdToken + AssumeRoleWithWebIdenity
            token = cognito_client.get_open_id_token(
                IdentityId=user_id
            )

            sts_client = boto3.client('sts',
                region_name           = _REGION_NAME,
                aws_access_key_id     = _CONV_AWS_ACCESS_KEY_ID,
                aws_secret_access_key = _CONV_AWS_SECRET_ACCESS_KEY,
                aws_session_token     = _CONV_AWS_SESSION_TOKEN #Not needed if you're not using MFA
            )

            d_today = str(datetime.date.today())
            credentials_for_identity = sts_client.assume_role_with_web_identity(
                RoleArn = _ROLE_ARN,
                RoleSessionName = _USER + "-" + d_today,#Character string at the beginning of the log Let's enter information that can identify who the session is.
                WebIdentityToken = token["Token"]
            )

            AccessKeyId = credentials_for_identity["Credentials"]["AccessKeyId"]
            SecretKey = credentials_for_identity["Credentials"]["SecretAccessKey"]
            SessionToken = credentials_for_identity["Credentials"]["SessionToken"]
        except:
            #ID deletion
            #If you make a mistake, delete the id you created in vain.
            del_response = cognito_client.delete_identities(
                IdentityIdsToDelete=[
                user_id
                ]
            )
            raise Exception("cognito_auth faild.","delete id :"+str(del_response["ResponseMetadata"]["RequestId"]))

        url_credentials = {}
        url_credentials['sessionId'] = AccessKeyId
        url_credentials['sessionKey'] = SecretKey
        url_credentials['sessionToken'] = SessionToken
        json_string_with_temp_credentials = json.dumps(url_credentials)

        request_parameters = "?Action=getSigninToken"
        request_parameters += "&SessionDuration=43200"
        if sys.version_info[0] < 3:
            def quote_plus_function(s):
                return urllib.quote_plus(s)
        else:
            def quote_plus_function(s):
                return urllib.parse.quote_plus(s)
        request_parameters += "&Session=" + quote_plus_function(json_string_with_temp_credentials)
        request_url = "https://signin.aws.amazon.com/federation" + request_parameters
        r = requests.get(request_url)
        # Returns a JSON document with a single element named SigninToken.
        signin_token = json.loads(r.text)

        # Step 5: Create URL where users can use the sign-in token to sign in to
        # the console. This URL must be used within 15 minutes after the
        # sign-in token was issued.
        request_parameters = "?Action=login"
        request_parameters += "&Issuer=Example.org"
        request_parameters += "&Destination=" + quote_plus_function("https://console.aws.amazon.com/")
        request_parameters += "&SigninToken=" + signin_token["SigninToken"]
        request_url = "https://signin.aws.amazon.com/federation" + request_parameters

        # Send final URL to stdout
        print (request_url)

    def main(self):
        print("success login! welcome "+_USER)
        self.cognito_auth()

if __name__ == "__main__":
    TC = TestClass()
    TC.main()

2. Run login_test.py.

image.png

image.png

login_test.py is implemented on the assumption that the user has been authenticated by the time it is executed. So, if you hit it by itself, an account will be issued and a very long login URL will be issued. When you access that URL, you will be redirected to the management console screen and you will be successfully federated. In addition to ReadOnly permissions, Session Manager also allows you to see which instances are standing from EC2 and use Session Manager to access your environment.

in conclusion

This time, we have made it possible for users who have not been issued AWS users to log in to the AWS Management Console. With this mechanism, IAM can manage accounts individually and provide a management console easily without the need for maintenance every time a retired employee leaves the company. Since AWS will strengthen the management console without permission, we will be able to continue to improve business efficiency without spending our development man-hours. It's very convenient, so please give it a try.

I am also a beginner on AWS (about cloud practitioner), so I would like to thank you for any suggestions, questions, or impressions. If you have any questions, please leave a comment.

Thank you for your hard work.

Recommended Posts

Users without an account access the AWS Management Console
Get an access token for the Pocket API
Get an Access Token for your service account with the Firebase Admin Python SDK