ようへいの日々精進XP

よかろうもん

IAM Role を付与した EC2 インスタンスから Amazon ES にアクセスするメモ(Python 版)

tl;dr

Amazon ES のアクセス制御を IP だけでしかやったことなかったので、実運用を考慮すると IAM role で制御する場合の方法を模索してみました。

参考

ありがとうございます。

memo

構成

  • EC2 には AmazonESFullAccess ポリシーを付与した IAM role がアタッチされている
  • Amazon ES にはポリシー未適用

この状態で Amazon ES にアクセスしてみると...

$ curl https://your_domain-xxxxxxxxxxxxxxxxxxxx.ap-northeast-1.es.amazonaws.com/
{"Message":"User: anonymous is not authorized to perform: es:ESHttpGet on resource: your_domain"}

リクエストに認証情報を埋め込んであげる必要があるようです。curl の場合にはどーやるのかな...と思ったら...

残念ながら curl で実現するのは難しいようです。

さんぷるスクリプト

ということで、参考として上げさせて頂いた記事を真似て以下のようなスクリプトをこさえてみました。

from elasticsearch import Elasticsearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth
import requests
import json, os

region      = 'ap-northeast-1'
es_endpoint = 'your_domain-xxxxxxxxxxxxxxxxxxxx.ap-northeast-1.es.amazonaws.com'
role_name   = 'your_role_name'

def get_credential():
    metadata_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' + role_name
    response     = requests.get(metadata_url)
    credential   = json.loads(response.text)

    return { 'access_key': credential['AccessKeyId'],
             'secret_key': credential['SecretAccessKey'],
             'token': credential['Token'] }

credentials = get_credential()

awsauth     = AWS4Auth(
    credentials['access_key'],
    credentials['secret_key'],
    region,
    'es',
    session_token=credentials['token']
)

es = Elasticsearch(
    hosts=[{'host': es_endpoint, 'port': 443}],
    http_auth=awsauth,
    use_ssl=True,
    verify_certs=True,
    connection_class=RequestsHttpConnection
)

print json.dumps(es.info())

pypi.python.org

requests-aws4auth という AWS リクエストに認証情報を追加する AWS 署名バージョン 4 を良しなに扱ってくれるモジュールを利用することでシンプルなコードになっていると思います。

実行してみると...

#
# 環境の確認
#
$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
$ python -V
Python 2.7.5

#
# 必要なモジュールを導入
#
$ cat requirements.txt
boto3
elasticsearch
requests
requests_aws4auth
$ sudo pip install -r requirements.txt

#
# スクリプトを実行
#
$ python test.py | jq .
{
  "cluster_name": "1234567890123:your_domain",
  "tagline": "You Know, for Search",
  "version": {
    "lucene_version": "5.5.0",
    "build_hash": "0944b4bae2d0f7a126e92b6133caf1651ae316cc",
    "number": "2.3.2",
    "build_timestamp": "2016-05-20T07:46:04Z",
    "build_snapshot": false
  },
  "name": "Rage"
}

おわり

先人の方々の努力のおかげでなんとかなりそうです。ありがとうございました。

おまけ : docker コンテナから Amazon ES にアクセスする場合

ファイル達

上記のサンプルを sample.py で保存。

$ tree .
.
├── Dockerfile
├── requirements.txt
└── sample.py

0 directories, 3 files

Dockerfile

FROM python:2.7.12-alpine
MAINTAINER inokappa

RUN apk --update add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata && \
    rm -rf /var/cache/apk/*

RUN  mkdir /app
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]

build して run

$ docker build -t es-sample-python .
$ docker run --rm es-sample-python sample.py | jq .
{
  "cluster_name": "1234567890123:your_domain",
  "tagline": "You Know, for Search",
  "version": {
    "lucene_version": "5.5.0",
    "build_hash": "0944b4bae2d0f7a126e92b6133caf1651ae316cc",
    "number": "2.3.2",
    "build_timestamp": "2016-05-20T07:46:04Z",
    "build_snapshot": false
  },
  "name": "Space Turnip"
}

IAM Role を付与した EC2 インスタンスから Amazon ES にアクセスするメモ(Ruby 版)

tl;dr

Amazon ES のアクセス制御を IP だけでしかやったことなかったので、実運用を考慮すると IAM role で制御する場合の方法を模索してみましたの Ruby 版。

参考

ありがとうございます。

memo

構成

構成は前回と同様。

  • EC2 には AmazonESFullAccess ポリシーを付与した IAM role がアタッチされている
  • Amazon ES にはポリシー未適用

さんぷるスクリプト

ということで、参考として上げさせて頂いた記事を真似て以下のようなスクリプトをこさえてみました。

require 'elasticsearch'
require 'faraday'
require 'faraday_middleware/aws_signers_v4'
require 'json'

es_endpoint = 'https://your_domain-xxxxxxxxxxxxxxxxxxxx.ap-northeast-1.es.amazonaws.com'
role_name   = 'your_role_name'
region      = 'ap-northeast-1'

def get_credential
  metadata_url = 'http://169.254.169.254'
  conn = Faraday.new(:url => metadata_url) do |faraday|
    faraday.adapter  Faraday.default_adapter
  end
  response     = conn.get '/latest/meta-data/iam/security-credentials/dev'
  credential   = JSON.parse(response.body)

  return { 'access_key': credential['AccessKeyId'],
           'secret_key': credential['SecretAccessKey'],
           'token': credential['Token'] }

end

credentials  = get_credential()
es = Elasticsearch::Client.new(url: es_endpoint) do |faraday|
  faraday.request :aws_signers_v4,
                  credentials: Aws::Credentials.new(credentials[:access_key], credentials[:secret_key], credentials[:token]),
                  service_name: 'es',
                  region: region
end

puts JSON.dump(es.info)

github.com

この Gem が肝です。前回の Python と同様に署名付きリクエストという壁が立ちはだかりますが、この gem が AWS 署名バージョン 4 を良しなに扱ってくれます。ありがたや、ありがたや。

実行してみると...

#
# 環境の確認
#
$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]

#
# 必要な gem をインストール
#
$ cat Gemfile
source "https://rubygems.org"

gem 'elasticsearch'
gem 'faraday_middleware-aws-signers-v4'
$ bundle install --path vendor/bundle

#
# スクリプトを実行
#
$ bundle exec ruby sample.rb  | jq .
{
  "name": "Space Turnip",
  "cluster_name": "1234567890123:your_domain",
  "version": {
    "number": "2.3.2",
    "build_hash": "0944b4bae2d0f7a126e92b6133caf1651ae316cc",
    "build_timestamp": "2016-05-20T07:46:04Z",
    "build_snapshot": false,
    "lucene_version": "5.5.0"
  },
  "tagline": "You Know, for Search"
}

おわり

先人の方々の努力のおかげでなんとかなりそうです。ありがとうございました。

おまけ : docker コンテナから Amazon ES にアクセスする場合

ファイル達

上記のサンプルを sample.py で保存。

$ tree .
.
├── Dockerfile
├── Gemfile
└── sample.rb

0 directories, 3 files

Dockerfile

FROM ruby:2.3.1-alpine
MAINTAINER inokappa

RUN apk --update add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata && \
    rm -rf /var/cache/apk/*

RUN  mkdir /app
COPY . /app
WORKDIR /app
RUN bundle install --path vendor/bundle && \
    bundle clean
ENTRYPOINT ["bundle", "exec", "ruby"]

build して run

$ docker build -t es-sample-ruby .
$ docker run --rm es-sample-ruby sample.rb | jq .
{
  "cluster_name": "1234567890123:your_domain",
  "tagline": "You Know, for Search",
  "version": {
    "lucene_version": "5.5.0",
    "build_hash": "0944b4bae2d0f7a126e92b6133caf1651ae316cc",
    "number": "2.3.2",
    "build_timestamp": "2016-05-20T07:46:04Z",
    "build_snapshot": false
  },
  "name": "Space Turnip"
}

PowerShell で操作する Azure メモ(3)

ども、かっぱです。

tl;dr

引続き、PowerShell で Azure を操作するメモ。

操作する環境は以下の通り。

PS C:\Users\Administrator\Downloads> [System.Environment]::OSVersion

                          Platform ServicePack                        Version                            VersionString
                          -------- -----------                        -------                            -------------
                           Win32NT                                    6.3.9600.0                         Microsoft Windows NT 6.3.9600.0

PS C:\Users\Administrator\Downloads> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.42000
BuildVersion                   6.3.9600.17400
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

引続き仮想ネットワークや仮想マシンをいじる

やること

改めて仮想マシンを作成

$_locationName  = "Japan West"
$_cloudService  = "your-service01"
$_vmName        = "vm01"
$_vmType        = "Basic_A0"
$_imageName     = @(Get-AzureVMImage | ? {$_.OS -eq "Windows" -and $_.ImageFamily -eq "Windows Server 2012 R2 datacenter"} ` | Sort-Object PublishedDate –Descending | Select-Object -First 1 ).ImageName
$_adminUser     = "oreadmin"
$_adminPassword = "YourPassword!01"
$_vnetName      = @(Get-AzureVNetSite -VNetName "Group your-service01 TestVNet01").Name
$_subNet        = "FrontEnd"

$_vmConfig = `
New-AzureVMConfig -Name $_vmName -InstanceSize $_vmType -ImageName $_imageName `
| Add-AzureProvisioningConfig -Windows -AdminUsername $_adminUser -Password $_adminPassword -EnableWinRMHttp `
| Set-AzureVMBGInfoExtension `
| Set-AzureSubnet -SubnetNames $_subNet `
| New-AzureVM -ServiceName $_cloudService  -VNetName $_vnetName -Location $_locationName -WaitForBoot

f:id:inokara:20160904162621p:plain

f:id:inokara:20160904163123p:plain

これは VM 拡張機能の一つ、BGInfo が有効になっている場合、他のホスト情報と合わせて掲載される。

仮想マシンのサブネットを変更する

  • 現在は FrontEnd で起動している仮想マシンを BackEnd に移動する
$_vm = Get-AzureVM –serviceName $_cloudService –Name $_vmName
#
# Set-AzureSubnet を実行して 仮想マシンのサブネットを指定
#
Set-AzureSubnet –SubnetNames "BackendEnd" –VM $_vm

#
# Update-AzureVM を実行して変更を反映
#
Update-AzureVM -Name "vm01" -VM $_vm.VM -ServiceName $_cloudService

Update-AzureVM による変更で VM は再起動が発生する。

  • ouput
OperationDescription                    OperationId                             OperationStatus
--------------------                    -----------                             ---------------
Update-AzureVM                          xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx    Succeeded
  • 変更が完了すると以下のような構成となる

f:id:inokara:20160904162526p:plain

  • 仮想マシンのデスクトップ壁紙に掲載されている DIP についても変更されている

f:id:inokara:20160904163248p:plain

DIP を割り当てる

  • $_vnetName 内で 192.168.2.100 が利用可能であるかを確認する
Test-AzureStaticVNetIP –VNetName $_vnetName –IPAddress 192.168.2.100
  • output
IsAvailable          : True
AvailableAddresses   : {}
OperationDescription : Test-AzureStaticVNetIP
OperationId          : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OperationStatus      : Succeeded
  • 仮想マシン $_vmName の IP アドレスを 192.168.2.100 に変更する(DIP を割り当てる)
Get-AzureVM –serviceName $_cloudService  –Name $_vmName `
| Set-AzureStaticVNetIP -IPAddress 192.168.2.100 `
| Update-AzureVM

VIP の固定予約と仮想マシンへの割り当て

  • 予約済み IP アドレスの作成
$ReservedIP = New-AzureReservedIP –ReservedIPName "OrenoReservedIP" –Label "OrenoReserved –Location "Japan West"
  • 予約済み IP アドレスの確認
Get-AzureReservedIP
  • output
ReservedIPName       : OrenoReservedIP
Address              : xxx.xxx.xxx.xxx
Id                   : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Label                : OrenoReservedIP
Location             : Japan West
State                : Created
InUse                : False
ServiceName          :
DeploymentName       :
VirtualIPName        :
OperationDescription : Get-AzureReservedIP
OperationId          : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OperationStatus      : Succeeded
  • 予約済み IP アドレスを利用して仮想マシンを作成する
$_vmConfig = `
New-AzureVMConfig -Name $_vmName -InstanceSize $_vmType -ImageName $_imageName `
| Add-AzureProvisioningConfig -Windows -AdminUsername $_adminUser -Password $_adminPassword -EnableWinRMHttp `
| Set-AzureVMBGInfoExtension `
| Set-AzureSubnet -SubnetNames $_subNet `
| New-AzureVM -ServiceName $_cloudService  -VNetName $_vnetName -Location $_locationName -WaitForBoot -ReservedIPName "OrenoReservedIP"

New-AzureVM 実行時に -ReservedIPName "OrenoReservedIP"-Location $_locationName(予約した IP アドレスと同じ地域を指定)を付与して仮想マシンを作成する。

Remove-AzureVM -Name $_vmName -ServiceName $_cloudService
  • output
OperationDescription                    OperationId                             OperationStatus
--------------------                    -----------                             ---------------
Remove-AzureVM                          xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx    Succeeded
  • Remove-AzureReservedIP を使って予約済み IP アドレスを解放
Remove-AzureReservedIP -ReservedIPName "OrenoReservedIP"
  • output
確認
Are you sure you want to remove the Reserved IP from your subscription?
[Y] はい(Y)  [N] いいえ(N)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):

OperationDescription                    OperationId                             OperationStatus
--------------------                    -----------                             ---------------
Remove-AzureReservedIP                  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx    Succeeded

以上

引続き。