« 2015年9月 | メイン | 2016年2月 »

2015年11月

2015年11月18日 (水)

AWS EC2 "悪い" EIPにご用心

エンジニアの佐藤です。こんにちは。
今回は「EIP(Elastic IP Address)は、払い出されたら外部接続を確認しよう」という注意喚起の話題を書かせていただきたいと思います。

EIPを設定したらGitHubに接続できない

ある日のこと、システムのリリースのためにEC2インスタンスを起動し、EIP(仮にxx.xx.xx.xx)を設定して、GitHubからcloneしようとすると、コネクションエラーになりました。おや?と思ってパケットキャプチャで調査すると、名前解決は成功するのですが、TCP SYNパケットに対する応答がありません。
EIPを外し、自動設定のグローバルIPアドレスが設定されると、接続できるようになりました。また、他のEIPを設定しても問題現象は再現しません。xx.xx.xx.xx固有の問題のようでした。
不思議なことに、問題のEIPを設定した場合でも「www.yahoo.com」には接続できます。しかし「msdn.microsoft.com」や「login.salesforce.com」には接続できません。接続先によって成功不成功があるようでした。

AWSのサポートに問い合わせる

AWSに質問したところ、初回の回答は順当なものでした。

  • AWS では特定のサイトやアドレスに関して制限をかけるといったことはしていない。
  • 接続先がアクセス制限を設定していた場合はAWSでは対応できない。

GitHubのブラックリスト

この段階で筆者が考えたのは、このxx.xx.xx.xxがブラックリストに掲載されており、GitHub(または中継ルーター)でブロックされている可能性でした。GitHubにはipset-blacklistというレポジトリがあり、有名ブラックリストを収集してくれます。早速実行してみました。
しかしながら、収集したブラックリストにxx.xx.xx.xxは入っていませんでした。

GitHubに問い合わせる

仕方がないのでGitHubに問い合わせてみることにしました。意外なことに1時間ほどで回答が来ました。「制限していない」と、明確な回答でした。

```

(筆者からGitHubへの質問) If I assign an IP address "xx.xx.xx.xx" to my computer, any request to GitHub is blocked. No connection can be established. That IP address isn't listed in "ipset-blacklist" blacklist. Does any additional filtering block that IP address?

(GitHubからの回答) There's no ban on that IP from our end. It could be an issue with your network environment of ISP. There's nothing we can do about it though.

```

再び、AWSのサポートに問い合わせる

いよいよ迷宮入りです。とりあえずこのGitHubからの回答をAWSサポートに追加情報として提供しましたが、解決に時間を要する可能性が高いと思いました。システムに設定しているEIPは変更し、AWSサポートの回答を待つことにしました。

同僚T氏から「IIJでこの種の問題が確認された場合は、中継ISPに順番に連絡してブラックリストを確認しているらしい。」という恐ろしい情報も伝えられました。ドツボにはまったのでしょうか。

原因はAWS内部のブラックリスト

そしてAWSサポートへの初回質問から約1ヶ月が経過したある日の晩、AWSサポートから以下のような回答が来ました。

  • EIP xx.xx.xx.xxは外部への接続を制限した状態だった。
  • 制限を解除したので、今後は通常通り使用できる。

急いで動作確認したところ、確かに問題現象は解消していました。

ふりかえると

インターネットは接続保証が無い世界ですから、今回のような問題現象は一般的には不自然な出来事ではありません。しかしパブリッククラウドサービスを提供する AWSの内部に原因が隠れており、アマゾンウェブサービス自身その存在を最初気付かず、AWSの利用者が原因を確認する手段もなかったというところに、今回の問題の恐ろしさがあります。問題に気付かないままIPアドレスを遠隔機器に設定したりしていたら、その修正は大変面倒なことになっていたでしょう。

もしかしたら、現在運用しているEIPも、接続制限されているかもしれません。接続先は無数にありますから完全な確認は不可能ですが、EIPが払い出されたら主要接続先のいくつかは試してみた方がいいかもしれません。
AWSサポートの皆さまのご尽力に感謝します。

2015年11月16日 (月)

AWS Lambdaを使ってRedshift料金を節約する

エンジニアの佐藤です。こんにちは。 前回AWS Lambdaを使ってEC2料金を節約するで、監視サーバを待機させずにEC2夜間自動停止を実装する手法をお話させていただきましたが、今回は同様の手法をRedshiftに適用してみましょう。
Redshiftは、カラム駆動型のデータ配置、圧縮、PostgreSQLに近いSQL言語などが特徴の高速分散DBMSと筆者は理解しているのですが、その課金体系はCPUとストレージのセット(ノード)の利用時間の従量課金です(長期事前予約による割引適用もありますが)。EC2と同様、利用が無い時間帯がある場合はリソースの自動解放・復旧を仕掛けることにより利用料金の節約が期待できます。

いくつかの問題と対処

ただしEC2のように単純にはいきません。以下の問題に対処する必要があります。

  • ノードを解放するとデータも消去されてしまうので、事前にスナップショットを作成する必要がある。復旧のときは、スナップショットからリソースを復元する。
  • 再起動した場合はエンドポイントが変更になる可能性がある。

幸い、いずれの場合についても少々の工夫で対処できました。前回同様にNodeJSとAWS SDK for Javascriptを使って実装してみましょう。今回もサンプルコード全体は以下の弊社公開リポジトリで公開しています。

https://github.com/FLECT-DEV-TEAM/cloudblog20151110

スナップショットの使用

稼働中のRedshiftをスナップショットにエクスポートして削除するには、以下のようにクラスターIDとスナップショットIDを指定してdeleteClusterを呼び出します。

var params = {
    ClusterIdentifier:              ctx.ClusterId,
    FinalClusterSnapshotIdentifier: ctx.SnapshotId
};

REDSHIFT.deleteCluster(
    params, 
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of deleteCluster

このスナップショットから復元するには、クラスターIDとスナップショットのIDを指定してrestoreFromClusterSnapshotを呼び出します。(今回はVPC内部にRedshiftを設定しますので、設定先サブネットやセキュリティグループも設定する必要があります。)ノード数やマスターユーザ設定などは、特に指定しなければスナップショット作成時の設定が引き継がれます。

var params = {
    ClusterIdentifier:      ctx.ClusterId,
    SnapshotIdentifier:     ctx.SnapshotId,
    AvailabilityZone:       ctx.AvailabilityZone,
    ClusterSubnetGroupName: 'default',
    VpcSecurityGroupIds:    ctx.VpcSecurityGroupIds,
    PubliclyAccessible:     false,
    AutomatedSnapshotRetentionPeriod: 0
};

REDSHIFT.restoreFromClusterSnapshot(
    params, 
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of restoreFromClusterSnapshot

エンドポイント(接続先)

Redshiftのクラスターを起動すると、以下のようなエンドポイントが払い出され、このエンドポイントにPosgreSQLプロトコルで接続することでクラスタを利用することができます。

<クラスターID>.<ランダム文字列>.<リージョン>.redshift.amazonaws.com:<ポート番号>

このエンドポイントは「クラスターIDやノード数を変更した場合に変更される」とSDKでは説明されていますが、「そうしない限り、保存される」とは書いてありません。つまりスナップショットの保存と復元の前後で同じエンドポイントが使える保証は無いようです。このままでは、エンドポイントの都度調査という面倒なことになります。
最も簡単な解決方法はElastic IP AddressでグローバルIPアドレスを設定してしまうことでしょう。しかし今回はVPC内での利用を想定しており、本来グローバルIPアドレスは不要です。
筆者が選択した方法は、「Route 53の内部DNSを設定する」というものです。Route 53ではVPCに接続された内部用のHosted Zoneを設定することができます。このHosted ZoneにCNAMEレコードをAPIで設定し、アプリケーションからはCNAME名でアクセスしてもらいます。具体的には、以下のようなレコードを設定します。

redshift.internal. 60 CNAME cluster-id01.aaaabbbbcccc.ap-northeast-1.redshift.amazonaws.com.

実装にあたっては、最初にクラスターのエンドポイントをクエリします。

var params = {
    ClusterIdentifier: ctx.ClusterId
};
REDSHIFT.describeClusters(
    params,
    function(err, data) {
        var clusters = data.Clusters;
        var c = clusters[0];
        ctx.Endpoint = c.Endpoint.Address;
        cb(null, ctx);
    }
); // end of describeClusters

次にこのエンドポイントを内部DNSのCNAMEレコードとして設定します。

var params = {
    HostedZoneId: ctx.HostedZoneId,
    ChangeBatch: {
        Changes: [{
            Action: 'UPSERT',
            ResourceRecordSet: {
                Name: 'redshift.internal',
                Type: 'CNAME',
                TTL: 60,
                ResourceRecords: [{Value: ctx.Endpoint}]
            }
        }] // end of Changes
    } // end of ChangeBatch
};
ROUTE53.changeResourceRecordSets(
    params,
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of changeResourceRecordSets

Route 53 Hosted Zoneは以下のようになっています。

Screen_shot_20151116_at_155423


Screen_shot_20151116_at_155700


タイミングの問題

実は筆者はこの段階で一つの壁にぶつかってしまいました。 Redshiftクラスターをスナップショットへ保存する処理も、またスナップショットから復元する処理も、時に長い処理時間を要します。この処理時間は課金対象になりませんが(課金はクラスターが「available」になってから)、長いと復元処理を開始してからエンドポイントが参照可能になるまでの所要時間が、AWS Lambdaの実行時間上限(5分)を超えてしまうのです。また、Lambdaを5分も待機させるのは、そもそもナンセンスにも思えます。
筆者はこの内部DNS設定処理を、スナップショットからの復元処理が完了しそうな時間帯に10分おきに繰り返し実行することにしました。何回かは空打ちとなりますが、エンドポイントが利用可能になってから10分以内にはCNAMEレコードが設定されるはずです。1回の試行時間はわずかで待機中は課金されませんので、リーズナブルです。
最終的には以下のように3つのLambda Functionをスケジュールしました。

  • スナップショットからの復元:午前7時
  • DNS設定試行:午前7時から8時まで、10分おきに実行
  • スナップショット作成とクラスター削除:午前1時

Lambda Functionの設定方法については前回投稿をご参照ください。なお、Lambda Functionの設定にあたっては、前回同様Description項目に設定情報をJSON形式で記載することを前提にしています。以下はこのDescription文字列の例です。

{ "ClusterId": "xxxxxxxx", "SnapshotId": "xxxxxxxx", "AvailabilityZone": "ap-northeast-1c", "VpcSecurityGroupIds": ["sg-xxxxxxxx"], "HostedZoneId": "XXXXXXXXXXXXX" }

まとめ

実装が若干複雑ですが、Redshiftという重量級コンポーネントについても「夜間自動停止」が実装できました。今回ご紹介した手法は、RDSにも容易に転用可能でしょう。

2015年11月10日 (火)

AWS Lambdaを使ってEC2料金を節約する

エンジニアの佐藤です。こんにちは。
毎月のAWS(Amazon Web Service)の請求書を眺めていると、気が付くことがあります。請求金額のかなりの部分が「CPU料金」なのです。
EC2として直接買っているCPUだけでなく、Redshift、ElastiCache、RDSなどや、DynamoDBの帯域予約分もこれに該当するでしょう。何にせよ「CPUを構える」ことは、ストレージを構えるのに比べて高くつくのです。
一方で、こうして構えたCPUがどの程度仕事をしているのかというと、結構「暇」していることが多いのではないでしょうか。暇であろうとなかろうと、料金はかかります。
そんな折、AWS Lambda(以下Lambda)にスケジュール実行機能が追加されました。つまり「定時ジョブ」を、CPUを待機させることなく仕掛けることが可能になったのです。今回はこのLambdaを使って、EC2「夜間自動停止」機能を実装する手法をご紹介したいと思います。深夜早朝に全く利用がないと前提できるサーバは結構あるのではないでしょうか?今日からEC2料金を25%は節約できます。

基本的なアイディアは以下のようなものです。

  • LambdaからAWS APIを実行してEC2インスタンスの起動・停止を実行する。Lambdaの実行権限には必要な権限を設定する。
  • 起動と停止それぞれLambda Functionを登録し、開始時刻と停止時刻にLambda Functionが実行されるように設定する。
  • 起動・停止するインスタンスのIDは、設定変更できるようにLambda Functionの設定情報としてDescriptionに記載する。

今回のソースコードですが、GitHubの以下の公開レポジトリに掲載しています。
https://github.com/FLECT-DEV-TEAM/cloudblog20151110

(以下はお使いの環境にgitとNodeJS、zipコマンドがインストールされているものとして説明します。)
最初にこのレポジトリをクローンしてください。

$ git clone https://github.com/FLECT-DEV-TEAM/cloudblog20151110

次に依存パッケージのインストールを行います。

$ npm install

次にLambda Functionとして設定するアーカイブを作成します。

$ source package.sh

scheduletask-yyyyMMddhhmmss.zipというファイルができます。

これで準備は完了です。AWSマネジメントコンソールにログインしてLambda Functionを設定しましょう。今回はEC2インスタンスの起動用に「testStart」、停止用に「testStop」という2つのLambda Functionを作成します。

Lambda Functionの作成では以下のように設定します。(赤文字で注記を入れています。)

Screen_shot_20151110_at_125307


「Role」は、Lambda Function実行された際、AWSのリソースに対してどれだけの実行権限を有しているかを設定するものです。ここでは簡単に、以下のポリシーを設定したroleを設定します。(実運用上は広すぎる権限ですので、適宜調整してください。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*",
                "ec2:*",
                "lambda:*"
            ],
            "Resource": "*"
        }
    ]
}

以上でLambda Functionの設定は完了です。次にこのLambda Functionをスケジュール起動するイベントの設定を行います。

作成したLambda Functionを選択し、Event sourcesタブで「Add event source」を選択します。

Img01


Event source typeに「Scheduled Event」を選択します。

Img02


Schedule expressionに「cron(0 22 * * ? *)」などと入力します。

Img03


時刻はUTCタイムゾーンで指定するのでこの場合は「毎日午前7時」の意味になります。表記法はcrontabに沿っていますが、5つ目の曜日指定のワイルドカードが「?」であること、crontabには存在しない6つ目の年指定ができることなどが異なっています。なお、最短時間間隔は5分となっています。

これで仕掛けは完了です。あとは指定時間になったらインスタンスの起動停止を確認し、CloudWatchのログを確認します。インスタンスの起動に成功した場合は以下のようなログがLambda Functionのロググループに書き残されるはずです。

START RequestId: XXX Version: $LATEST
2015-11-09T22:00:23.210Z XXX ["i-xxxxxxxx"]STARTED
2015-11-09T22:00:23.211Z XXX FINISH
END RequestId: XXX
REPORT RequestId: XXX Duration: 1502.21 ms Billed Duration: 1600 ms Memory Size: 128 MB Max Memory Used: 36 MB

この実行でいくら課金されるのでしょうか?AWS Lambdaの課金は回数と実行メモリ容量×実行時間の2つあり、 回数については以下のようになっています。

  • First 1 million requests per month are free
  • $0.20 per 1 million requests thereafter ($0.0000002 per request)

使用メモリが128MBの場合の実行課金は以下のようになっています。

  • 3,200,000 Free tier seconds per month
  • $0.000000208 per 100ms

今回は1インスタンスあたり起動と停止が1日に1回ずつと考えて、1日2回実行で128MB、3200msだとすると、1か月での料金は、 3200 / 100 x 31 x 0.000000208 = $0.0002 たったの0.02セント(約0.024円)です。 EC2のt2.microインスタンスなら36秒間で使い切ってしまう金額です。

筆者には、このAWS Lambdaという名の「コードフラグメント・ホスティング」は、物理サーバ → 仮想サーバ → コンテナと続く一連の流れの最先端のように思えます。今後はVPC対応が実施される予定でVPC内のRDSやElastiCacheとも連携しやすくなります。実装やデプロイの難しさはありますが、急速に広まっていくのではないでしょうか。

2015年11月 6日 (金)

Webアプリケーションセキュリティについての社内勉強会を開いてみた(解答編)

今年も残り2ヶ月ですがこれから本気出します。エンジニアの小川(mitsuruog)です。
9/3にWebアプリケーションセキュリティというテーマで社内勉強会を開きましたが、解答編の記事を書いてませんでしたね。
すみません。

前回の記事はこちらです。
フレクトのクラウドblog(New): Webアプリケーションセキュリティについての社内勉強会を開いてみた

早速ですが、課題にどんな脆弱性が含まれていたのか?発表します。

回答編

架空のSNSサービスの設計に潜む脆弱性を探すというテーマでした。今回の脆弱性がある設計箇所を分類すると次のようになります。

  • システム全体設計に潜む脆弱性
  • 認証設計に潜む脆弱性
  • WebAPI設計に潜む脆弱性


システム全体設計に潜む脆弱性

システムの構成、サイト設計などSNSサービス全体に関わる設計に潜む問題です。

Img_20150908_002202

  • 通信経路がHTTP。
    • 新規Webサービスであれば、HTTPS前提にするべきです。
  • 管理者ページURLが推測しやすい。
    • 管理者ページが「/admin」となっていて非常に推測しやすいです。
    • 最近では、WordPressやWebアプリケーションジェネレータ(Yeoman)など、便利なツールが増えてきましたが、デフォルト設定を利用する場合も同様の注意が必要です。


認証設計に潜む脆弱性

システムのセキュリティの要、認証設計に潜む問題です。

Img_20150908_002221

Img_20150908_002232

  • HTTP...orz
    • 以下略
  • パスワード忘れ機能の設計ミス
    • 「ユーザーID」と「秘密の質問」を入力するとパスワードが変更できてしまう仕様です。最近であれば、登録済みのメールアドレスにメールを送信するなど、ワンクッション入れたいところです。
    • また、秘密の質問が「好きな色は?」となっており、万人が好きそうな色が予測できます。好きな色を固定して「リバースブルートフォースアタック」が成立しやすいです。
  • Cookieの設計について
  • セキュアな情報をクライアント側で保管している
    • 認証後の情報をlocalstorageにて保存しています。中に「管理者フラグ」が含まれていることもあり、容易に管理者へ昇格できます。


WebAPI設計に潜む脆弱性

WebAPIの設計も細かな注意点があります。

Img_20150908_002306

Img_20150908_002316

  • ユーザーIDの採番ルール
    • もし連番で発番した場合、容易に他人のユーザーIDが推測できると思われます。
  • リクエストパラメータの設計ミス
    • リクエストパラメータに「管理者フラグ」「ユーザーID」が含まれており、容易になりすましが可能でした。
    • メッセージ取得APIのレスポンスで投稿者のユーザーIDが取得できる仕様であるため、他人になりすまして投稿が容易に可能でした。


まとめ

もう、ダメダメですね。。。
設計のミスで重大なセキュリティ事故が発生した場合、システム開発会社の責任が問われることが多くなってきました。
そのため、特に認証周りの設計する際は次のようなことも留意するようにしています。

  • 独自認証設計をしない。OAuth2.0など標準的なものを利用する。
  • 秘密の質問自体をユーザーに選択させる。(秘密の質問の設計が甘いと設計責任が問われ兼ねない。)

実際に受けたメンバーからは「簡単すぎる」との意見が多かったのが反省点ではありますが、セキュリティについて再復習できたいい機会だったのではないかと考えています。

このような活動を地道に続けていく事で、少しずつ強いエンジニア組織にしていきたいですね。

ではではー

2015年11月 2日 (月)

express実践入門ついての社内勉強会を開いてみた

みかんが美味しい季節になりましたね。エンジニアの小川(mitsuruog)です。
10/29にexpress実践入門というテーマで社内勉強会を開きましたので、その話を紹介します。

発表した資料はこちらです。

express実践入門 · GitHubexpress実践入門 · GitHub

なぜ、やろうとしたのか?

Expressは、node.jsのWebアプリケーションフレームワークです。
事実上、node.jsでのデファクトスタンダートとなっており、既に利用している方や、これから始めようと興味を持っている方も多いのではないかと思います。

弊社でも、IoTでの導入が最近増えてきました。

巷には、express入門用の記事が多くあるのですが、なぜ「express実践入門」という形で再整理したかと言うと、
私が実務でexpressを利用してきた上で、「express入門用の記事の多くに」次のような不満があったからです。

express3系で作成されている古い記事が多い

expressの最新バージョンは4です。expressの3系と4系ではAPIに互換性がない変更があります。
現在、日本語で読める記事や書籍のほとんどがexpress3系より前の内容で書かれています。

expressの「Getting started」をやってみただけの内容が多く、深みに欠ける

「express入門用の記事の多く」はexpressの「Getting started」と同様の内容であることが多いです。 やはり、Webアプリケーションを作成するためには、もう少し踏み込んだ内容が必要です。

Webアプリケーションを作成するという内容に欠ける

Webアプリケーションを作成する上でexpressの全機能を利用することはまずありません。
また、expressだけではなく、様々なnpmモジュール(認証、DBなど)を組み合わせてWebアプリケーションは構成されています。

時にexpressの細部に踏み込んだ内容が必要なケースはありますが、ほとんどの場合、expressをある程度使いこなした後に出くわす場合が多いのではないでしょうか。
立ち上がりとしては、「expressでWebアプリケーションを構築する」という立ち位置での、まとまったインプットが欲しかったりします。

このような中、初学者がexpressを攻略する上でのつまづくポイントと、中規模開発をターゲットにWebアプリケーションを構築する上でのベストプラクティスを私の経験ベースでまとめました。

実施風景

昼食の時間帯に行いました。
会社支給の弁当を片手に真剣にexpressについて学びます。

456a2398


当日は業務が忙しい中、20名程度の参加がありました。

456a2402


「よく噛むと“記憶力”がアップする!?」という話を信じているのかは定かではないのですが、弊社では隔週ペースでランチ勉強会を開催して、社員やパートナーの皆さんの技術力向上に努めています。

まとめ

「私が考える最強のexpress実践入門」でした。
初めてexpressやる方で、この辺り悩んでいる方がいましたら、参考にしていただければ。。。

弊社でもIoT関連でどんどん実践投入していきます。

ではではー

採用情報

株式会社フレクトでは、事業拡大のため、
・Salesforce/Force.comのアプリケーション開発
・HerokuやAWSなどのクラウドプラットフォーム上での
Webアプリケーション開発
エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみたい方、
是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、セールスフォースパートナーです。
また、heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。

2017年10月

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
ブログ powered by TypePad