UnityからAWS Cognito Identity Providerで認証機能を実装する(User_Auth_Flow)

前置き:AWSSDK for Unityでは現状,公式にCognito Identity Providerの機能をサポートしていません.紹介した方法は自己責任で参考にしてください.

UnityからCognito User Poolを使って認証する

この記事ではAWSのCognito User Poolを使用してログイン機能をUnityのアプリケーションに実装し,AWSのリソースにアクセスするためのトークンを取得する方法を紹介します.

Cognito User Poolとは

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools.html

自分でサーバー側の実装を書くことなくユーザー認証機能を実装できます.

手順

環境

AWSの設定

User Poolの作成と設定

UserPool.png

Cognitoからユーザーを管理するためのUser Poolを作成し,以下のように設定します.

  • プール名

    • わかりやすい任意の名前を設定します.
  • 属性
    • 標準属性のチェックを全て外し,ユーザー名とパスワードだけでログインできるように設定します.
  • アプリクライアントの追加

    • “アプリクライアントの追加”をクリックします.
    • アプリクライアント名を設定します.
    • “クライアントシークレットを生成する”をオフにします.

設定し終わったら”確認タブ”からプール作成を終了し,

  • プールIDとプール名 (リージョン名と固有なプール名の組み合わせ)

    • 例) ap-northeast-1_Hoge0Hoge
      を確認します.

さらにアプリの統合>アプリクライアントの設定タブから,

  • “有効なIDプロバイダ”のチェック欄に表示されている”Cognito User Pool”にチェックを入れます.
  • アプリクライアントID (25桁のAlphanumericな値)を確認します.

Identity Poolの作成と設定

IdentityPool.png

Cognitoから”フェデレーテッドアイデンティティ”のページに移動し,”新しいIDプールの作成”をクリックするとIDプール作成ウィザードが開始します.

  • IDプール名を入力します.
  • “認証されていないIDに対してアクセスを有効にする”をオンにするとゲストユーザーとしてアプリを使用することが可能です.ここでは必要ないのでオフのままで大丈夫です.
  • 認証プロバイダーのセクションで”Cognito”タブをクリックし,先ほど確認したユーザープールのIDとアプリクライアントIDを入力します.

Createpolicy.png

“プールの作成”をクリックすると,”AWSの諸々にアクセスするための権限の設定をする必要がある”ということで,ゲストユーザーと認証ユーザーの二つのユーザーについてIAMロールを設定するように言われます.

ここではどちらも”新しいIAMロールの作成”を選択し,そのまま右下の許可を押します.ここで作成した二つのIAMロール(Cognito_*Hoge*Auth_RoleCognito_*Hoge*Unauth_Role)はあとで編集するので名前を覚えておきます.

IAMロールの編集

先ほど作成したロールの権限を編集し,Cognito User Poolに対して認証リクエストを送る権限を付与します.

CogitoIdentityProvider.dll for Unityのビルド

AWS Mobile SDK for UnityではCognito User Poolがサポートされておらず,自分でCognitoIdentityProvider.dllをビルドする必要があります.dotNet版のコードジェネレーターの設定をいじることでUnityに対応したdllを得ることができます.

GitHubのissueに具体的な方法が書かれており,それを翻訳すると以下のようになります.

  1. AWS SDK dot netのリポジトリをクローンします.

  2. aws-sdk-netgeneratorServiceModelscognito-idpmetadata.jsonを編集し,"platforms": ["Unity"],をjsonのrootに追加します.

  3. aws-sdk-netgeneratorServiceClientGeneratorLib/Generators/ProjectFiles/UnityProjectFile.csの265行目でUnityEngine.dllのパスを修正します. UnityEngine.dllの場所はMacOSとWindowsで異なるので注意.ここではWindowsでビルドします.修正後のパスはC:/Program Files/Unity/Editor/Data/Managed/UnityEngine.dllとなります.

  4. aws-sdk-netgeneratorAWSSDKGenerator.slnをVisual Studioで開きます.実行ボタン(上の緑の三角ボタン)を押すとビルドされたコード生成プログラムが実行され,上手くいけばaws-sdk-netsdksrcServicesCognitoIdentityProvider内部にAWSSDK.CognitoIdentityProvider.Unity.csprojというファイルが生成されます.

  5. 次にaws-sdk-netsdkAWSSDK.Unity.slnを開き,Build TypeをReleaseに設定してからビルド(ctrl + shift + B)します.すると新しくaws-sdk-netsdksrcServicesCognitoIdentityProviderbinReleaseunityフォルダが作られ,中にCognitoIdentityProvider.dllが生成されます.

  6. UnityプロジェクトのAssets/下任意の場所にdllを置きます.

metadata.json
{
  "platforms": ["Unity"],
  "active": true, 
  "synopsis": "You can create a user pool in Amazon Cognito Identity to manage directories and users. You can authenticate a user to obtain tokens related to user identity and access policies. This API reference provides information about user pools in Amazon Cognito Identity, which is a new capability that is available as a beta."
}
UnityProjectFile.cs
...
 this.Write(this.ToStringHelper.ToStringWithCulture(Path.Combine((string)this.Session["UnityPath"], "Editor", "Data", "Managed", "UnityEngine.dll")));
...

Unityで認証フローを実装する

AWS Mobile SDK for Unityを公式サイトからダウンロードし,AWSSDK.IdentityManagement.unitypackageをプロジェクトにインポートします.

以下,ブログエントリーを参考にして認証フローと暗号化処理を実装します.

AdminInitiateAuth関数を使った方法が紹介されることが多いですが,パスワードが平文で送信されるのでモバイルアプリで用いるにはセキュリティ的に非常に危険です.今回はSecure Remote Password (SRP)プロトコルをつかった認証フローであるUSER_SRP_AUTHに従います.

暗号化の実装はブログエントリーが詳しく,また依存するHkdfクラスの実装もGistに公開されているものが完動するのでコピペします.Bouncy Castle C#のライブラリが必要なのでBouncy Castle C#のGitHubリポジトリから最新リリースをダウンロードし,.net2.0版のdllをプロジェクトにコピーします.

注意すべき点として, 作成したばかりのユーザーのステータスはFORCE_CHANGE_PASSWORDと設定されており,User Poolからのレスポンス(チャレンジ)として新しいパスワードを要求してきます.下のプログラムではまだ実装しておらず,パスワード変更済みのユーザーに対してログイン処理をかけています.

認証フローが成功すると
* IdToken
* AccessToken
* RefreshToken
の三つが手に入ります.

得られたIdトークンを使ってCredential.AddLogins(IdentityProviderName , IdToken)することで,GetCredentialForIdentityAsync関数を実行した時にAWSのAPIを叩く上で必要なトークンを得ることができます.このトークンを使って認可されるアクション,アクセスできるリソースはIAMで設定したCognito_HogeAuth_Roleに従います.

リフレッシュトークンはPlayerPrefなどに保存し,アプリを起動した際にIdトークンを更新することで自動ログイン処理を行います.

CognitoUserPoolClient.cs
namespace CognitoLogInSample
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using System;
    using System.Globalization;
    using Amazon;
    using Amazon.Runtime;
    using Amazon.CognitoIdentity;
    using Amazon.CognitoIdentity.Model;
    using Amazon.CognitoIdentityProvider;
    using Amazon.CognitoIdentityProvider.Model;

    public class CognitoUserPoolClient : MonoBehaviour
    {
        #region CognitoCredentials
        public string IdentityPoolId;            // IDプールのID
        public string CognitoIdentityRegion;     // リージョン名 例)ap-northeast-1

        private RegionEndpoint _CognitoIdentityRegion
        {
            get { return RegionEndpoint.GetBySystemName(CognitoIdentityRegion); }
        }
        private CognitoAWSCredentials _credentials;

        private CognitoAWSCredentials Credentials
        {
            get
            {
                if (_credentials == null)
                {
                    _credentials = new CognitoAWSCredentials(IdentityPoolId, _CognitoIdentityRegion);
                    _credentials.IdentityChangedEvent += Credentials_IdentityChangedEvent;
                }
                return _credentials;
            }
        }
        #endregion

        #region CognitoIdP
        public string CognitoIdPRegion;          // User Poolのリージョン 例)ap-northeast-1

        private RegionEndpoint _CognitoIdPRegion
        {
            get { return RegionEndpoint.GetBySystemName(CognitoIdPRegion); }
        }

        [SerializeField]
        string userPoolName;                     // User Poolの固有名 アンダーバーで区切ったうちの後半
        [SerializeField]
        string clientId;                         // User PoolのクライアントID

        string UserPoolId
        {
            get
            {
                return string.Format("{0}_{1}", _CognitoIdPRegion, userPoolName);
            }
        }

        public string CognitoIdentityProviderName
        {
            get
            {
                return string.Format("cognito-idp.{0}.amazonaws.com/{1}", _CognitoIdentityRegion.SystemName, UserPoolId);
            }
        }
        #endregion

        AmazonCognitoIdentityProviderClient idpClient;
        AmazonCognitoIdentityClient cognitoIdentityClient;

        public InputField IdInputField;
        public InputField passwordInputField;
        public Toggle clearCredentialToggle;

        TokenCacheManager tokenCacheManager;
        public bool cleanPlayerPrefSetting;

        string currentSession;
        string currentUserName;

        private void Start()
        {
            tokenCacheManager = new TokenCacheManager();
            if (cleanPlayerPrefSetting)
            {
                tokenCacheManager.DeleteCachedToken();
            }
            //AdminAuthenticateWithRefreshToken();
            SignInWithRefreshToken();
        }

        public void OnButtonClick()
        {
            if (clearCredentialToggle.isOn)
            {
                Credentials.Clear();
            }
            SignIn(IdInputField.text, passwordInputField.text);
        }

        #region InitiateAuthFlow

        /// <summary>
        /// Signs In with refresh token (USER_AUTH_FLOW).
        /// </summary>
        void SignInWithRefreshToken()
        {
            tokenCacheManager.GetCachedTokens((getCachedTokensResult) =>
            {
                if (getCachedTokensResult.IsCacheAvailable)
                {
                    Debug.Log(getCachedTokensResult.Token.ToString());
                    // RefreshToken
                    idpClient = new AmazonCognitoIdentityProviderClient(Credentials, _CognitoIdentityRegion);
                    InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest()
                    {
                        ClientId = clientId,
                        AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH,
                    };
                    initiateAuthRequest.AuthParameters.Add("REFRESH_TOKEN", getCachedTokensResult.Token.refreshToken);

                    idpClient.InitiateAuthAsync(initiateAuthRequest, (initiateAuthResponse) =>
                    {
                        if (initiateAuthResponse.Exception != null) return;
                        CognitoIdentityProviderToken cognitoIdentityProviderToken = new CognitoIdentityProviderToken
                        {
                            accessToken = initiateAuthResponse.Response.AuthenticationResult.AccessToken,
                            idToken = initiateAuthResponse.Response.AuthenticationResult.IdToken ?? getCachedTokensResult.Token.idToken,
                            refreshToken = initiateAuthResponse.Response.AuthenticationResult.RefreshToken ?? getCachedTokensResult.Token.refreshToken,
                            expireTime = initiateAuthResponse.Response.AuthenticationResult.ExpiresIn
                        };
                        tokenCacheManager.CacheTokens(cognitoIdentityProviderToken);

                        Credentials.AddLogin(CognitoIdentityProviderName, initiateAuthResponse.Response.AuthenticationResult.IdToken);
                        Credentials.GetIdentityIdAsync(responce =>
                        {
                            Debug.Log("Logged In with refreshed IdToken : " + responce.Response);
                        });
                    });
                    idpClient.Dispose();
                }
                else
                {
                    Credentials.Clear();
                    // Redirect to LogIn Dialog
                    Debug.Log("RefreshToken is not available");
                }
            });
        }


        /// <summary>
        /// Sign In.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        void SignIn(string userName, string password)
        {
            Debug.Log("Initiate Authentication Flow");

            var cred = new AnonymousAWSCredentials();

            idpClient = new AmazonCognitoIdentityProviderClient(cred, _CognitoIdentityRegion);
            var TupleAa = AuthenticationHelper.CreateAaTuple();

            var initiateAuthRequest = new InitiateAuthRequest
            {
                AuthFlow = AuthFlowType.USER_SRP_AUTH,
                ClientId = clientId,
                AuthParameters = new Dictionary<string, string>(){
                            {"USERNAME", userName},
                            {"SRP_A", TupleAa.Item1.ToString(16)},
                        }
            };

            Debug.Log(initiateAuthRequest.AuthParameters["SRP_A"]);

            idpClient.InitiateAuthAsync(initiateAuthRequest, (initiateAuthResponse) =>
            {
                var challengeName = initiateAuthResponse.Response.ChallengeName;
                if (challengeName == ChallengeNameType.NEW_PASSWORD_REQUIRED)
                {
                    // newPasswordRequired
                    idpClient.Dispose();
                }
                else if (challengeName == ChallengeNameType.PASSWORD_VERIFIER)
                {
                    DateTime timestamp = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now);
                    var usCulture = new CultureInfo("en-US");
                    string timeStr = timestamp.ToString("ddd MMM d HH:mm:ss "UTC" yyyy", usCulture);

                    byte[] claim = AuthenticationHelper.authenticateUser(initiateAuthResponse.Response.ChallengeParameters["USERNAME"],
                                                                         password,
                                                                         userPoolName,
                                                                         TupleAa,
                                                                         initiateAuthResponse.Response.ChallengeParameters["SALT"],
                                                                         initiateAuthResponse.Response.ChallengeParameters["SRP_B"],
                                                                         initiateAuthResponse.Response.ChallengeParameters["SECRET_BLOCK"],
                                                                         timeStr
                                                                        );
                    string claimBase64 = Convert.ToBase64String(claim);

                    var respondToAuthChallengeRequest = new RespondToAuthChallengeRequest()
                    {
                        ChallengeName = initiateAuthResponse.Response.ChallengeName,
                        ClientId = clientId,
                        ChallengeResponses = new Dictionary<string, string>(){
                                    { "PASSWORD_CLAIM_SECRET_BLOCK", initiateAuthResponse.Response.ChallengeParameters["SECRET_BLOCK"]},
                                    { "PASSWORD_CLAIM_SIGNATURE", claimBase64 },
                                    { "USERNAME", userName },
                                    { "TIMESTAMP", timeStr }
                        }
                    };

                    Debug.Log(timeStr);

                    idpClient.RespondToAuthChallengeAsync(respondToAuthChallengeRequest, respondToAuthChallengeResponse =>
                   {
                       try
                       {
                           Debug.LogFormat("User was verified in SRP Auth Flow : {0}", respondToAuthChallengeResponse.Response.AuthenticationResult.IdToken);
                           Credentials.AddLogin(CognitoIdentityProviderName, respondToAuthChallengeResponse.Response.AuthenticationResult.IdToken);

                           tokenCacheManager.CacheTokens(new CognitoIdentityProviderToken()
                           {
                               accessToken = respondToAuthChallengeResponse.Response.AuthenticationResult.AccessToken,
                               idToken = respondToAuthChallengeResponse.Response.AuthenticationResult.IdToken,
                               refreshToken = respondToAuthChallengeResponse.Response.AuthenticationResult.RefreshToken,
                               expireTime = respondToAuthChallengeResponse.Response.AuthenticationResult.ExpiresIn,
                           });
                       }
                       catch (Exception e)
                       {
                           Debug.LogErrorFormat("Encountered exception: {0}", e);
                       }
                       finally
                       {
                           idpClient.Dispose();
                       }
                   });
                }
            });

            currentUserName = userName;
        }
        #endregion

        private void Credentials_IdentityChangedEvent(object sender, CognitoAWSCredentials.IdentityChangedArgs e)
        {
            Debug.Log(string.Format("Identity has changed from {0} to {1}", e.OldIdentityId, e.NewIdentityId));
        }
    }
}

参考資料

続きを読む

copy files from local to aws S3 Bucket(aws cli + s3 bucket)

                       **AWS CLI and S3 Bucket**

maxresdefault.jpg

In my current project, I need to deploy/copy my front-end code into AWS S3 bucket. But I do not know how to perform it. One of my colleagues found a way to perform this task.

here are the guidelines from start to end, how to install aws cli, how to use aws cli and other functionalities.

So, let’s start from the beginning.
In my mac, I do not installed aws cli, so I got the error when running the following command.
Open your terminal,

$ aws --version

output
-bash: aws: command not found

(Here I got the solution, https://qiita.com/maimai-swap/items/999eb69b7a4420d6ab64)

So now let’s install the brew, if you do not installed yet.
Step1. Install the brew

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Step2. check the brew version

$ brew -v

output
Homebrew 1.5.2
Homebrew/homebrew-core (git revision 58b9f; last commit 2018-01-24)

Step3. install aws cli, use the following command

$ brew install awscli

Step4. check the aws cli version

$ aws --version

output
aws-cli/1.14.30 Python/3.6.4 Darwin/17.3.0 botocore/1.8.34

that’s great, now it’s time to configure the AWS credential.
Step5. now configure the aws profile

$ aws configure
AWS Access Key ID [None]: <your access key>
AWS Secret Access Key [None]: <your secret key>
Default region name [None]: <your region name>
Default output format [None]: ENTER

All settings are done.
now you can access your s3 bucket.

now from here, we can learn how to manage our bucket.
Managing Buckets
aws s3 commands support commonly used bucket operations, such as creating, removing, and listing buckets.

1.Listing Buckets

$ aws s3 ls

output
2017-12-29 08:26:08 my-bucket1
2017-11-28 18:45:47 my-bucket2

The following command lists the objects in bucket-name/path (in other words, objects in bucket-name filtered by the prefix path/).

$ aws s3 ls s3://bucket-name

2.Creating Buckets

$ aws s3 mb s3://bucket-name

(aws s3 mb command to create a new bucket. Bucket names must be unique.)

3.Removing Buckets
To remove a bucket, use the aws s3 rb command.

$ aws s3 rb s3://bucket-name

By default, the bucket must be empty for the operation to succeed. To remove a non-empty bucket, you need to include the –force option.

$ aws s3 rb s3://bucket-name --force

This will first delete all objects and subfolders in the bucket and then remove the bucket.

Managing Objects
The high-level aws s3 commands make it convenient to manage Amazon S3 objects as well. The object commands include aws s3 cp, aws s3 ls, aws s3 mv, aws s3 rm, and sync. The cp, ls, mv, and rm commands work similarly to their Unix

The cp, mv, and sync commands include a –grants option that can be used to grant permissions on the object to specified users or groups. You set the –grants option to a list of permissions using the following syntax:

--grants Permission=Grantee_Type=Grantee_ID
         [Permission=Grantee_Type=Grantee_ID ...]

Each value contains the following elements:
* Permission – Specifies the granted permissions, and can be set to read, readacl, writeacl, or full.
* Grantee_Type – Specifies how the grantee is to be identified, and can be set to uri, email address, or id.
* Grantee_ID – Specifies the grantee based on Grantee_Type.
* uri – The group’s URI. For more information, see Who Is a Grantee?
* email address – The account’s email address.
* id – The account’s canonical ID.

aws s3 cp file.txt s3://my-bucket/ --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers full=emailaddress=user@example.com

For more details, please go through the aws official link.
https://docs.aws.amazon.com/cli/latest/userguide/using-s3-commands.html

thank you for taking your precious time to read.

Enjoy coding.:grinning::grinning:

Thanks & Best Regards,
Alok Rawat

続きを読む

【CPUの脆弱性対応】古いAmazon Linuxのkernelを最新版にアップデートする

AWSで管理しているAmazon Linuxが古いバージョンのまま放置していることありませんか?

CPUの脆弱性対応におけるAWSの公式ドキュメント

Customers with existing Amazon Linux AMI instances should run the following command to ensure they receive the updated package: sudo yum update kernel.

カーネルをアップデートして最新のカーネルにするように書いてあるのですが、
Amazon Linuxのバージョンが古いインスタンスでは、
単にカーネルのアップデートだけでは、最新のカーネルにアップデートされません。

この記事ではその際の対応手順を説明します。

※ 説明のためにコミュニティAMIにある古いAMIを使って進めます。

対象のマシンにsshし、インフォメーションをチェック

しばらくOSをアップデートされていないインスタンスにsshします。
OSが更新されていない場合は、以下のように

$ ssh hogehost

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2016.03-release-notes/
Amazon Linux version 2017.09 is available.

Amazon Linux version 2017.09 is available.
Amazon Linux version 2017.09が利用できます

と表示されていますね。
あまり意識しないと見逃しがちですが、このような情報もしっかり確認しましょう。

さらにカーネルのバージョンを確認します。

$ uname -srv
Linux 4.4.5-15.26.amzn1.x86_64 #1 SMP Wed Mar 16 17:15:34 UTC 2016

カーネルも2016年に作られたバージョンのままになっていますね。

yum.confをチェックし、AMIを特定のバージョンに固定されていないか確認する

AMIを特定のバージョンに固定するにはどうすればよいですか?

上記の公式ドキュメントに書かれているようにAmazon Linuxのマシーンイメージでは、1つのバージョンから次のバージョンへ連続的な更新を提供するように設定されていますが、過去のバージョンでは/etc/yum.confreleasever変数がなかったり、リリースバージョンが固定されていることがあります。

その場合は単にyum updateだけでは最新のパッケージに更新されません。

今回対象のyum.confを見てみると

$ cat /etc/yum.conf
[main]
cachedir=/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
distroverpkg=system-release
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=3
deltarpm=0

# by default the yum configuration will point to the latest release
# of Amazon Linux AMI. If you prefer not to automatically move to
# new releases, comment out this line.
releasever=2016.03

#  This is the default, if you make this bigger yum won't see if the metadata
# is newer on the remote and so you'll "gain" the bandwidth of not having to
# download the new metadata and "pay" for it by yum not having correct
# information.
#  It is esp. important, to have correct metadata, for distributions like
# Fedora which don't keep old packages around. If you don't like this checking
# interupting your command line usage, it's much better to have something
# manually check the metadata once an hour (yum-updatesd will do this).
# metadata_expire=90m

# PUT YOUR REPOS HERE OR IN separate files named file.repo
# in /etc/yum.repos.d

releasever2016.03に固定されています。

この状態では、カーネルをアップデートしても変更されません。

$ sudo yum update kernel
Loaded plugins: priorities, update-motd, upgrade-helper
No packages marked for update

そこで更新させるためには以下のように変更する必要があります。

releasever=latest

releaseverを「latest」に変更して、カーネルをアップデート

先ほどのyum.confのリリースサーバーを最新に更新します。

$ cp -ip /etc/yum.conf  /tmp/
$ sudo vi /etc/yum.conf
$ diff /etc/yum.conf /tmp/yum.conf
17c17
< releasever=latest
---
> releasever=2016.03
#更新しました

# kernelをアップデートする
$ sudo yum update kernel
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main/latest                                                                                                                                                                                                                 | 2.1 kB     00:00
amzn-updates/latest                                                                                                                                                                                                              | 2.3 kB     00:00
Resolving Dependencies
--> Running transaction check
---> Package kernel.x86_64 0:4.9.51-10.52.amzn1 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

========================================================================================================================================================================================================================================================
 Package                                                 Arch                                                    Version                                                               Repository                                                  Size
========================================================================================================================================================================================================================================================
Installing:
 kernel                                                  x86_64                                                  4.9.51-10.52.amzn1                                                    amzn-main                                                   17 M

Transaction Summary
========================================================================================================================================================================================================================================================
Install  1 Package

Total download size: 17 M
Installed size: 71 M
Is this ok [y/d/N]: y
Downloading packages:
kernel-4.9.51-10.52.amzn1.x86_64.rpm                                                                                                                                                                                             |  17 MB     00:01
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : kernel-4.9.51-10.52.amzn1.x86_64                                                                                                                                                                                                     1/1
  Verifying  : kernel-4.9.51-10.52.amzn1.x86_64                                                                                                                                                                                                     1/1

Installed:
  kernel.x86_64 0:4.9.51-10.52.amzn1

Complete!

先ほどは、アップデートするカーネルはありませんでしたが、今回はアップデートされました。

再起動後に再度インスタンスにアクセスして、カーネルがアップデートされたか確認します。

$ ssh hogehost
Last login: Sat Jan 20 02:10:39 2018 

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2016.03-release-notes/
No packages needed for security; 138 packages available
Run "sudo yum update" to apply all updates.
Amazon Linux version 2017.09 is available.

$ uname -srv
Linux 4.9.51-10.52.amzn1.x86_64 #1 SMP Fri Sep 29 01:16:19 UTC 2017

カーネルは4.4.5から4.9.51に上がったようですね。ところがまだ最新ではないです。

またsshを行ったら、早速情報が更新されています。

No packages needed for security; 138 packages available
Run “sudo yum update” to apply all updates.

カーネルのアップデートではなく、パッケージのアップデートを勧められています。

全てのパッケージアップデートする

そこでカーネルだけれはなく全てのパッケージをアップデートさせます。

パッケージを全てアップデートすると既存で動いているサービスに影響を与えることがあるので、バージョンの依存関係があるパッケージについては除外しましょう

今回はカーネルを最新にアップデートさせることが目標なので、上記を考慮せずに全てのパッケージをアップデートします。

$ sudo yum update
Loaded plugins: priorities, update-motd, upgrade-helper
Resolving Dependencies
--> Running transaction check
---> Package acpid.x86_64 0:1.0.10-2.1.6.amzn1 will be updated
---> Package acpid.x86_64 0:2.0.19-6.7.amzn1 will be an update
・・・(省略)

# インストールとアップデートをするパッケージが一覧されるのでチェック
========================================================================================================================================================================================================================================================
 Package                                                                Arch                                            Version                                                                Repository                                          Size
========================================================================================================================================================================================================================================================
Updating:
 acpid                                                                  x86_64                                          2.0.19-6.7.amzn1                                                       amzn-main                                           73 k
 at                                                                     x86_64                                          3.1.10-48.15.amzn1                                                     amzn-main                                           66 k
 audit                                                                  x86_64                                          2.6.5-3.28.amzn1                                                       amzn-main                                          272 k
 audit-libs                                                             x86_64                                          2.6.5-3.28.amzn1                                                       amzn-main                                           96 k
・・・(省略)
Installing for dependencies:
 libXcomposite                                                          x86_64                                          0.4.3-4.6.amzn1                                                        amzn-main                                           21 k
 libidn2                                                                x86_64                                          0.16-1.2.amzn1                                                         amzn-main                                          103 k
 libnghttp2                                                             x86_64                                          1.21.1-1.4.amzn1                                                       amzn-main                                           73 k
 libseccomp                                                             x86_64                                          2.3.1-2.4.amzn1                                                        amzn-main                                           79 k
 libunistring                                                           x86_64                                          0.9.3-6.1.amzn1                                                        amzn-main                                          419 k
 perl-Time-HiRes                                                        x86_64                                          4:1.9725-272.5.amzn1                                                   amzn-main                                           46 k
 python27-futures                                                       noarch                                          3.0.3-1.3.amzn1                                                        amzn-main                                           30 k

Transaction Summary
========================================================================================================================================================================================================================================================
Install               ( 7 Dependent packages)
Upgrade  138 Packages

Total download size: 192 M

yum updateでは、インストールを始める前に、インストールとアップデートをするパッケージが一覧されます。
その後にアップデートするか選べるので、パッケージを確認してから、動いているサービスに影響がないか確認するといいと思います。

このままアップデートを実行します。

Is this ok [y/d/N]: y

Downloading packages:
(1/145): acpid-2.0.19-6.7.amzn1.x86_64.rpm                                                                                                                                                                                       |  73 kB     00:00
(2/145): at-3.1.10-48.15.amzn1.x86_64.rpm                                                                                                                                                                                        |  66 kB     00:00
・・・(省略)
  xfsprogs.x86_64 0:4.5.0-9.21.amzn1                              yum.noarch 0:3.4.3-150.70.amzn1                            yum-metadata-parser.x86_64 0:1.1.4-10.20.amzn1                  yum-plugin-priorities.noarch 0:1.1.31-40.29.amzn1
  yum-plugin-upgrade-helper.noarch 0:1.1.31-40.29.amzn1           yum-utils.noarch 0:1.1.31-40.29.amzn1

Complete!

全てのパッケージをアップデートしました。再起動を行い、再度sshします。

$ ssh hogehost
Last login: Sat Jan 20 02:17:24 2018 

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2017.09-release-notes/
10 package(s) needed for security, out of 34 available
Run "sudo yum update" to apply all updates.

$ uname -srv
Linux 4.9.51-10.52.amzn1.x86_64 #1 SMP Fri Sep 29 01:16:19 UTC 2017

カーネルのバージョンはかわっていませんが、

10 package(s) needed for security, out of 34 available
Run “sudo yum update” to apply all updates.

バージョンアップ後、さらにセキュリティ問題のためアップデートするように警告されていますね。

すべでのパッケージをアップデートしたことで、最新のカーネルにアップデートできるようになりました。

パッケージ全てをアップデートする(2回目)

最新のカーネルにアップデートするべく、再度アップデートします。

$ sudo yum update
Loaded plugins: priorities, update-motd, upgrade-helper
Resolving Dependencies
・・・(省略)

Installed:
  kernel.x86_64 0:4.9.77-31.58.amzn1

Dependency Installed:
  libtool-ltdl.x86_64 0:2.4.2-20.4.8.5.32.amzn1                                                                                     nss-pem.x86_64 0:1.0.3-4.3.amzn1

Updated:
  aws-cfn-bootstrap.noarch 0:1.4-27.19.amzn1      aws-cli.noarch 0:1.14.9-1.48.amzn1         curl.x86_64 0:7.53.1-13.80.amzn1               docker.x86_64 0:17.09.1ce-1.111.amzn1            docker-storage-setup.noarch 0:0.6.0-1.18.giteb688d4.amzn1
  ec2-net-utils.noarch 0:0.5-1.34.amzn1           ec2-utils.noarch 0:0.5-1.34.amzn1          ecs-init.x86_64 0:1.16.2-1.amzn1               irqbalance.x86_64 2:1.3.0-1.26.amzn1             java-1.7.0-openjdk.x86_64 1:1.7.0.161-2.6.12.0.75.amzn1
  kernel-tools.x86_64 0:4.9.77-31.58.amzn1        krb5-libs.x86_64 0:1.15.1-8.43.amzn1       libcurl.x86_64 0:7.53.1-13.80.amzn1            nss.x86_64 0:3.28.4-12.80.amzn1                  nss-softokn.x86_64 0:3.28.3-8.41.amzn1
  nss-softokn-freebl.x86_64 0:3.28.3-8.41.amzn1   nss-sysinit.x86_64 0:3.28.4-12.80.amzn1    nss-tools.x86_64 0:3.28.4-12.80.amzn1          nss-util.x86_64 0:3.28.4-3.53.amzn1              openssl.x86_64 1:1.0.2k-8.106.amzn1
  python27.x86_64 0:2.7.12-2.121.amzn1            python27-boto.noarch 0:2.48.0-1.2.amzn1    python27-botocore.noarch 0:1.8.13-1.66.amzn1   python27-devel.x86_64 0:2.7.12-2.121.amzn1       python27-libs.x86_64 0:2.7.12-2.121.amzn1
  ruby20.x86_64 0:2.0.0.648-1.30.amzn1            ruby20-irb.noarch 0:2.0.0.648-1.30.amzn1   ruby20-libs.x86_64 0:2.0.0.648-1.30.amzn1      rubygem20-bigdecimal.x86_64 0:1.2.0-1.30.amzn1   rubygem20-psych.x86_64 0:2.0.0-1.30.amzn1
  rubygems20.noarch 0:2.0.14.1-1.30.amzn1         system-release.noarch 0:2017.09-0.1        wget.x86_64 0:1.18-3.28.amzn1

Complete!

再起動してアップデートを反映させます。


$ ssh hogehost
Last login: Sat Jan 20 02:21:06 2018

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2017.09-release-notes/

$ uname -srv
Linux 4.9.77-31.58.amzn1.x86_64 #1 SMP Thu Jan 18 22:15:23 UTC 2018

カーネルもCPU脆弱性の対応がされた最新のバージョンになりました。

まとめ

今回2016年のAmazon LinuxのKernelを最新版にアップデートにするには以下のような流れです。

  • yum.conf更新(リリースバージョンを最新にする)
  • yum update(1回目。2016 → 2017にコミットされたバージョンをあげる)
  • yum update(2回目。2017 → 最新の2018にコミットされたバージョンをあげる。)

何回 yum updateが必要なのかは、管理しているインスタンスのバージョンによりますが、
OSのバージョンアップは早いうちからやっていた方が明らかに楽ですし、
パッケージを全てアップデートする場合は既存のサービスに影響を与える可能性もあります。

改めてセキュリティ対策は、コツコツアップデートしていかなければという学びを得ました。

また、sshの情報にしっかり目を通すことで、どうアップデートすべきかわかるので最新のOSに保ちつつ管理していきましょう。

続きを読む

AWS Mobile Hub による react-native アプリ開発(Cloud API REST呼び出し)

AWS mobile-hub が react-native に対応したとのことなので、マニュアル (Developer Guide) を元にサンプルアプリを作成・試してみる。

今回は Dynamodb にてテーブルを作成し、CRUDアプリケーションをサンプルに沿って作成してみた。

はじめに

前回の記事から Dynamodb に登録・参照する簡単なサンプルプログラムを実行してみる。

Backend (Mobile Hub) の設定

テーブルの作成

コマンドから下記を実行

awsmobile database enable --prompt

マニュアル通りの Notes テーブルを作成

Welcome to NoSQL database wizard
You will be asked a series of questions to help determine how to best construct your NoSQL database table.

? Should the data of this table be open or restricted by user? Open
? Table name Notes

You can now add columns to the table.

? What would you like to name this column NoteId
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteTitle
? Choose the data type string
? Would you like to add another column Yes
? What would you like to name this column NoteContent
? Choose the data type string
? Would you like to add another column No


Before you create the database, you must specify how items in your table are uniquely organized. This is done by specifying a Primary key. The primary key uniquely identifies each item in the table, so that no two items can have the same key.
This could be and individual column or a combination that has "primary key" and a "sort key".
To learn more about primary key:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey


? Select primary key NoteId
? Select sort key (No Sort Key)

You can optionally add global secondary indexes for this table. These are useful when running queries defined by a different column than the primary key.
To learn more about indexes:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes

? Add index No
Table Notes saved

CRUD API の作成

下記コマンドにて cloud-api を作成

awsmobile cloud-api enable --prompt

先ほど作成した Notes テーブルから生成を選択。ログイン必須を選択した。

This feature will create an API using Amazon API Gateway and AWS Lambda. You can optionally have the lambda function perform CRUD operations against your Amazon DynamoDB table.


? Select from one of the choices below. Create CRUD API for an existing Amazon DynamoDB table
? Select Amazon DynamoDB table to connect to a CRUD API Notes
? Restrict API access to signed-in users Yes
Adding lambda function code on: 
/Users/masanao/dev/awsmobile/awsmobilejs/backend/cloud-api/Notes/
...
Path to be used on API for get and remove an object should be like:
/Notes/object/:NoteId

Path to be used on API for list objects on get method should be like:
/Notes/:NoteId

JSON to be used as data on put request should be like:
{
  "NoteTitle": "INSERT VALUE HERE",
  "NoteContent": "INSERT VALUE HERE",
  "NoteId": "INSERT VALUE HERE"
}
To test the api from the command line (after awsmobile push) use this commands
awsmobile cloud-api invoke NotesCRUD <method> <path> [init]
Api NotesCRUD saved

設定の反映

下記コマンドにて AWS のモジュールを作成

awsmobile push

作成に少し時間がかかるので、完了するのを待つ。

building backend
   building cloud-api
      zipping Notes
   generating backend project content
   backend project content generation successful
done, build artifacts are saved at: 
/Users/masanao/dev/awsmobile/awsmobilejs/.awsmobile/backend-build

preparing for backend project update: awsmobile
   uploading Notes-20180108140947.zip
   upload Successful  Notes-20180108140947.zip
done

updating backend project: awsmobile
awsmobile update api call returned with no error
waiting for the formation of cloud-api to complete
cloud-api update finished with status code: CREATE_COMPLETE

Successfully updated the backend awsmobile project: awsmobile

retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud

フロントエンドの構築

App.js を編集・CRUDのコードを書く。

モジュール import

import { API } from 'aws-amplify-react-native';

CRUDコードの記述

サンプルコードをそのまま貼り付けすることとした。

state の追加

state = {
  apiResponse: null,
  noteId: ''
     };

  handleChangeNoteId = (event) => {
        this.setState({noteId: event});
}

saveNote 関数の追加

  // Create a new Note according to the columns we defined earlier
    async saveNote() {
      let newNote = {
        body: {
          "NoteTitle": "My first note!",
          "NoteContent": "This is so cool!",
          "NoteId": this.state.noteId
        }
      }
      const path = "/Notes";

      // Use the API module to save the note to the database
      try {
        const apiResponse = await API.put("NotesCRUD", path, newNote)
        console.log("response from saving note: " + apiResponse);
        this.setState({apiResponse});
      } catch (e) {
        console.log(e);
      }
    }

getNote 関数の追加

  // noteId is the primary key of the particular record you want to fetch
      async getNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.get("NotesCRUD", path);
          console.log("response from getting note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

deleteNote 関数の追加

  // noteId is the NoteId of the particular record you want to delete
      async deleteNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.del("NotesCRUD", path);
          console.log("response from deleteing note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

rennder 関数の変更(Viewの変更)

<View style={styles.container}>
        <Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
        <Button title="Save Note" onPress={this.saveNote.bind(this)} />
        <Button title="Get Note" onPress={this.getNote.bind(this)} />
        <Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
        <TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  textInput: {
      margin: 15,
      height: 30,
      width: 200,
      borderWidth: 1,
      color: 'green',
      fontSize: 20,
      backgroundColor: 'black'
   }
});

作成したApp.js ソース

react-native のサンプルソースを元にしたため、一部不要なソースコードがありますがそのままにしています。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import Amplify from 'aws-amplify-react-native';
import { API } from 'aws-amplify-react-native';
import { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  Button,
  TextInput,
  View
} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,n' +
    'Shake or press menu button for dev menu',
});

class App extends Component<{}> {
  constructor(props) {
        super(props);
    this.state = {
      apiResponse: null,
      noteId: ''
    };
  }

  handleChangeNoteId = (event) => {
        this.setState({noteId: event});
  }

  // Create a new Note according to the columns we defined earlier
    async saveNote() {
      let newNote = {
        body: {
          "NoteTitle": "My first note!",
          "NoteContent": "This is so cool!",
          "NoteId": this.state.noteId
        }
      }
      const path = "/Notes";

      // Use the API module to save the note to the database
      try {
        const apiResponse = await API.put("NotesCRUD", path, newNote)
        console.log("response from saving note: " + apiResponse);
        this.setState({apiResponse});
      } catch (e) {
        console.log(e);
      }
    }
     // noteId is the primary key of the particular record you want to fetch
      async getNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.get("NotesCRUD", path);
          console.log("response from getting note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

     // noteId is the NoteId of the particular record you want to delete
      async deleteNote() {
        const path = "/Notes/object/" + this.state.noteId;
        try {
          const apiResponse = await API.del("NotesCRUD", path);
          console.log("response from deleteing note: " + apiResponse);
          this.setState({apiResponse});
        } catch (e) {
          console.log(e);
        }
      }

  render() {
    return (
<View style={styles.container}>
        <Text>Response: {this.state.apiResponse && JSON.stringify(this.state.apiResponse)}</Text>
        <Button title="Save Note" onPress={this.saveNote.bind(this)} />
        <Button title="Get Note" onPress={this.getNote.bind(this)} />
        <Button title="Delete Note" onPress={this.deleteNote.bind(this)} />
        <TextInput style={styles.textInput} autoCapitalize='none' onChangeText={this.handleChangeNoteId}/>
</View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  textInput: {
      margin: 15,
      height: 30,
      width: 200,
      borderWidth: 1,
      color: 'green',
      fontSize: 20,
      backgroundColor: 'black'
   }
});
export default withAuthenticator(App);

実行

react-native run-ios

スクリーンショット 2018-01-08 18.51.18.png

テキストボックス内に NoteID を入力して “Save Note” を押すと、保存されます。

内容はソースコードに書いてあるように固定文字が登録されます。
あくまでサンプルソースということで・・・。

AWS 確認

Mobile Hub の Cloud Logic には下記のように登録されています。

スクリーンショット 2018-01-08 19.01.38.png

Lambda も確認すると、NotesCRUD-xxxx という名前で登録されているのが確認できます。

スクリーンショット 2018-01-08 19.02.59.png

DynamoDB も同様にテーブルが作成され、データも登録されているのがわかります。

スクリーンショット 2018-01-08 19.24.53.png

通常のCRUD であればほぼノンプログラミングでバックエンドロジックが生成できますね。

続きを読む

AWS Mobile Hub による react-native アプリ開発(構築〜認証)

AWS mobile-hub が react-native に対応したとのことなので、マニュアル(Developer Guide) を元にサンプルアプリを作成・実行して見ます。

はじめに

当初PetStoreのようなサンプルのアプリがダウンロードできるのかと思いましたが、react-native のサンプルはまだないようです。

マニュアルもまだ不十分な気がします。本手順よりも、もう少しスマートなやり方があるのかもしれません。

前提

node がインストールされていること
react-native がインストールされていること

準備

react-native プロジェクトの作成
前述のように雛形プロジェクトが落ちてくるわけではなかったので、最初にreact-native initで初期プロジェクトを作成しました。

react-native init awsmobile
cd awsmobile

セットアップ

awsmobile-cli をインストール/設定します。

npm install -g awsmobile-cli

実行結果

└─┬ awsmobile-cli@1.0.6 
  ├─┬ aws-amplify@0.1.30
  │ └─┬ axios@0.17.1
  │   └── follow-redirects@1.3.0 
  └── aws-sdk@2.177.0 

アクセスキーの設定

AWSのシークレットキーを設定します。

awsmobile configure

awsmobile init コマンドによる Mobile Hub プロジェクトの作成

$ awsmobile init

Please tell us about your project:
? Where is your project's source directory:  /
? Where is your project's distribution directory that stores build artifacts:  build
? What is your project's build command:  npm run-script build
? What is your project's start command for local test run:  npm run-script start

? What awsmobile project name would you like to use:  awsmobile

Successfully created AWS Mobile Hub project: awsmobile

retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud

Success! your project is now initialized with awsmobilejs

   awsmobilejs/.awsmobile
     is the workspace of awsmobile-cli, please do not modify its contents

   awsmobilejs/#current-backend-info
     contains information of the backend awsmobile project from the last
     synchronization with the cloud

   awsmobilejs/backend
     is where you develop the codebase of the backend awsmobile project

   awsmobile console
     opens the web console of the backend awsmobile project

   awsmobile run
     pushes the latest development of the backend awsmobile project to the cloud,
     and runs the frontend application locally

   awsmobile publish
     pushes the latest development of the backend awsmobile project to the cloud,
     and publishes the frontend application to aws S3 for hosting

Happy coding with awsmobile!

aws console で確認すると、指定した名前awsmobileでプロジェクトが作成されます。

  • 今回はマニュアル通り cli にて作成しましたが、aws console 上で作成して awsmobile init することもできるようです。

バックエンドとの接続設定

react-native プロジェクトを AWS Mobile Hub に接続するよう設定します。

AWS Amplify for React Nativer ライブラリをインストール

npm install --save aws-amplify-react-native

公式のGet Started にあるように App.js に下記のコードを追加

  • YOUR-PATH-TO/ は適当に置き換える必要がありました。
import Amplify from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

修正した App.js のコード


/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import Amplify from 'aws-amplify-react-native';

import aws_exports from './aws-exports';

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,n' +
    'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit App.js
        </Text>
        <Text style={styles.instructions}>
          {instructions}
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

Amplify.configure(aws_exports);

実行してみます。公式にはnpm run ios とありますが、下記で動作させました。

react-native run-ios

実行結果

スクリーンショット 2018-01-08 11.26.56.png

まだ、react-nativeのサンプル画面と特に変わりはありません。

認証連携

今回はAWS Console から設定しました。
コンソール上で、 User Sing-in をクリックして設定します。
Eamil and Passwordを設定し、Sing-in必須としました。

スクリーンショット 2018-01-08 11.37.10.png

設定を取り込むため、awsmobile pull コマンドを実行します。

$ awsmobile pull

retrieving the latest backend awsmobile project information
awsmobile project's details logged at: awsmobilejs/#current-backend-info/backend-details.json
awsmobile project's access information logged at: awsmobilejs/#current-backend-info/aws-exports.js
awsmobile project's access information copied to: aws-exports.js
awsmobile project's specifications logged at: awsmobilejs/#current-backend-info/mobile-hub-project.yml
contents in #current-backend-info/ is synchronized with the latest in the aws cloud
? sync corresponding contents in backend/ with #current-backend-info/ Yes

amazon-cognito-identity-js モジュールのリンク設定

react-native link amazon-cognito-identity-js

App.js に認証モジュールを組み込みます。

import 文の追加

import { withAuthenticator } from 'aws-amplify-react-native';

最終行に export を設定

export default withAuthenticator(App);

App の定義で export default を外す。

class App extends Component<{}> {

実行結果

react-native run-ios

スクリーンショット 2018-01-08 11.41.20.png

Sing In ページが表示されました。

sing up を行い、送信メールのパスコードを入力します。

スクリーンショット 2018-01-08 11.53.08.png

スクリーンショット 2018-01-08 11.44.45.png

無事ログインできるようになりました。

スクリーンショット 2018-01-08 11.54.18.png

次は Cloud Logic を追加して見たいと思います。

参考

AWS公式ドキュメント

https://docs.aws.amazon.com/aws-mobile/latest/developerguide/aws-mobile-react-native.html?shortFooter=true

https://github.com/aws/aws-amplify

https://api.wp-kyoto.net/try-aws-amplify-for-react-project/

続きを読む

S3バケットの内容をLambda関数で読み込んでみる(Python)

Lambda関数からS3を操作する練習です。
S3にファイルをアップロードしたタイミングでLambda関数が実行されるように設定します。
アップロードされたタイミングで、バケット名やファイルの一覧を取得する関数を書いてみます。

S3バケットを作成する

「バケットを作成する」をクリックします。
S3 Management Console 2018-01-06 09-29-55.png

バケット名とリージョンを入力します。
S3 Management Console 2018-01-06 09-31-18.png

プロパティの設定は特に何もせずに「次へ」をクリックします。
次の「アクセス許可の設定」はユーザーに対する設定のみです。とりあえずそのまま「次へ」をクリックします。

S3 Management Console 2018-01-06 09-35-11.png

Lambdaにファイルをアップロードする

適当なファイルをアップロードしてみます。
S3 Management Console 2018-01-06 09-38-15.png

ドラッグアンドドロップでファイルのアップロードができます。
S3 Management Console 2018-01-06 09-39-58.png

「アップロード」をクリックします。
S3 Management Console 2018-01-06 09-41-26.png

以下のようにアップロードしたファイルが表示されます。
S3 Management Console 2018-01-06 09-42-26.png

IAMに権限を設定する

Lambdaを操作するIAMにS3へのアクセス権限を与えます。
IAM Management Console 2018-01-06 09-57-31.png

AmazonS3FullAccessポリシーはフルアクセス権を与えてしまうので、どのS3バケットにもアクセスできてしまうことには注意が必要です。今回はサンプルなのでFullAccessを与えますが、実運用に入る際は、インラインポリシーを設定してポリシーを絞るのがいいと思います。
IAM Management Console 2018-01-06 09-58-50.png

Lambda関数を作成する

設計図s3-get-object-pythonから作成します。
Lambda Management Console 2018-01-06 10-04-43.png

Lambda Management Console 2018-01-06 10-07-20.png

Lambda Management Console 2018-01-06 10-07-37.png

boto3で何ができるかは本家のドキュメントをじっくり読むのがいいと思います。
ネットの情報は古くなってしまったものも多く、単純に真似するだけでは動かないものもありました。
「Boto 3 Docs documentation」
http://boto3.readthedocs.io/en/latest/reference/services/s3.html#client

こちらのサンプルはちゃんと動いてわかりやすかったです。
https://github.com/bloomberg/chef-bcs/blob/master/cookbooks/chef-bcs/files/default/s3-example-boto3.py

以下のような関数を作ってみました。

  • S3バケット名を取得する
  • S3バケット内のファイルの一覧を取得する
  • S3バケット内のファイルの詳細情報を表示する

などを行っています。

from __future__ import print_function

import json
import urllib
import boto3

print('Loading function')

#http://boto3.readthedocs.io/en/latest/reference/services/s3.html#client
s3 = boto3.client('s3')


def lambda_handler(event, context):


    bucket_name = event['Records'][0]['s3']['bucket']['name']
    print('==== bucket information =====')
    print(bucket_name)
    print('=============================')


    #https://github.com/bloomberg/chef-bcs/blob/master/cookbooks/chef-bcs/files/default/s3-example-boto3.py
    print('==== your bucket list ====')
    buckets = s3.list_buckets()
    for bucket in buckets['Buckets']:
        print(bucket.get('Name'))

    print('==== file list in bucket ====')
    # https://github.com/boto/boto3/issues/134
    AWS_S3_BUCKET_NAME = 'example.read.000'
    s3_resource = boto3.resource('s3')
    bucket = s3_resource.Bucket(AWS_S3_BUCKET_NAME)
    result = bucket.meta.client.list_objects(Bucket=bucket.name, Delimiter='/')
    for o in result.get('Contents'):
        print(o.get('Key'))


    print('==== file details ====')
    # https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingObjects.html
    GET_OBJECT_KEY_NAME = 'sample.png'
    object = s3.get_object(Bucket=AWS_S3_BUCKET_NAME, Key=GET_OBJECT_KEY_NAME)
    print('ContentType ->' + str(object.get('ContentType')))
    print('ContentLength ->' + str(object.get('ContentLength')))


    print('==== uploaded file ====')
    for rec in event['Records']:
        print(rec['s3']['object']['key'])
    print('=============================')

    print("Received event: " + json.dumps(event, indent=2))

テストを実行すると以下のようなログが表示されます。

START RequestId: bdbee72e-f28b-11e7-a401-0d0509334621 Version: $LATEST
==== bucket information =====
sourcebucket
=============================
==== your bucket list ====
example.read.000
learnjs.benrady.com
==== file list in bucket ====
sample.png
sample2.png
==== file details ====
ContentType ->image/png
ContentLength ->82361
==== uploaded file ====
HappyFace.jpg
=============================

S3にオブジェクトをプットしても同じように関数が実行されます。

続きを読む

Route53でドメイン買ってACMでSSL証明書発行してCloudFrontでGithub Pageと買ったドメインと紐付けた

なにこれ

タイトル通りの手順です。流れなので長いのはお許しください。

0からでもうまいことやれたので備忘録として書きました。参考までにどうぞ。


事前準備

  • AWSへのアカウント登録関連は完了しておく

    • メール認証・クレカ登録などお忘れなく
  • Github Page 作成
    • 無料垢でもOK

Route53でドメインを買う

  • Route53にアクセス。
  • 「Register Domain」ボタンよりドメイン購入手続き
    Screenshot from Gyazo

  • 購入したいドメイン名を入力
  • 欲しいTLD(.com, .net, .orgなど)を選択
  • 「Check」ボタンより購入可能なドメインを検索

Screenshot from Gyazo


  • 「Add to cart」で欲しいドメインをカートに追加
  • ページ下部の「Continue」ボタンで次へ

Screenshot from Gyazo


  • 購入者入力画面で各種入力

Screenshot from Gyazo


  • 入力後、問題なければ「Continue」ボタンで確認画面に遷移
  • 「Terms and Conditions」の同意確認箇所にチェックを入れ、「Complete Purchase」ボタンで購入確定へ

Screenshot from Gyazo


  • メールにて購入完了の旨を受け取る。ドメイン購入はこれにて完了。

    • 下図は実際に買ったときのやつ
  • AWS Certificate Managerに移動

Screenshot from Gyazo


AWS Certificate Manager

  • 右上のリージョンが「バージニア北部」になっていることを確認

    • なっていなかったら選択
    • CloudFrontで使用する際に必要
      Screenshot from Gyazo

  • 「証明書のリクエスト」をクリック

Screenshot from Gyazo


  • ドメイン名の追加で先程購入したドメインを入力して「次へ」をクリック

Screenshot from Gyazo


  • 証明書のリクエスト検証はDNSにして「次へ」をクリック

Screenshot from Gyazo


  • 確認で問題なければ「確定とリクエスト」ボタンをクリック。

Screenshot from Gyazo

  • その後遷移する確定後の画面より「続行」ボタンをクリック。

  • ダッシュボードに遷移して、検証保留中になっているのを確認したらCloudFrontに移動

Screenshot from Gyazo


CloudFront

  • 「Distributions」ダッシュボードが開いているのを確認して、「Create Distribution」をクリック

Screenshot from Gyazo


  • Webの方の「Get Started」をクリック

Screenshot from Gyazo


01. Origin Settings

  • 「Origin Domain Name」に自分のgithub pageを入力

    • ここではgithub pageのURL=インデックスページという想定です

Screenshot from Gyazo


02. Default Cache Behavior Settings

  • 「Viewer Protocol Policy」をRedirect HTTP to HTTPS
  • 「Cache Based on Selected Request Headers」をWhitelist
  • 「Whitelist Headers」でHostsをAdd

Screenshot from Gyazo


03. Distribution Settings

  • 「Alternate Domain Names(CNAMEs)」 に適応させるドメインを入力
  • 「SSL Certificate」はCustom SSL Certificateを選択
    • このときAWS Certificate Managerで作成したSSL証明書が選択できると思うのでそれを選択

Screenshot from Gyazo


  • 01~03までを入力したら「Create Distribution」ボタンをクリック
  • その後生成された「Domain Name」(dから始まるやつ)のURLをコピー
    Screenshot from Gyazo
  • コピーしたURLが見れる状態になってるかを確認
  • アクセスできるのを確認したらRoute53に戻る

Route53

  • 左メニューより「Hosted Zones」を選択、ダッシュボードに購入したドメイン名あるのでクリック
  • 「Create Record Set」ボタンをクリック

Screenshot from Gyazo


  • Nameは空でOK
  • TypeはA
  • AliasはYesをチェック
    • Alias Targetに先程コピーしたURLを貼る
  • 「Save Record Set」クリックで追加

Screenshot from Gyazo


Github Page

  • GitHub Pagesのリポジトリに移動
  • 「Settings」タブより設定ページに移動

Screenshot from Gyazo


GitHub Pages

  • 「Custom domain」箇所に購入したドメインを入力、Save

    • DNSの浸透がまだだとはじかれるかもなので、10分くらい待つのとか、CloudFrontのStatusがDeployedになっているかなど確認した上でやる

Screenshot from Gyazo


反映を確認

:tada::tada::tada:

Screenshot from Gyazo


感想

  • AWS、自分で1から触るのは始めてなのでDNS浸透なり証明書が無効だったりと色々ありしんどかった。
  • ただここまでやっておけばある程度動かせる下地ができる感じなのでやっておいてよかった
  • ドメイン買うのももっと安くやる方法もあるだろうけど、AWSサービス間で設定するなら全部まとめてやるのが分かりやすいかなと思ったのでこの手法で良かったと思う

参考

続きを読む