AWSのIPアドレスレンジを都合よく加工してみる

 API EC2 CloudFront CloudFormation Security JavaScript
2014.11.26

先日の【AWS発表】AWSのIPアドレスレンジをJSONで提供というポスト、
私は一人感涙にむせびました。もうこういうところからコピペしなくてよいのだと。

 

とにかく、私にとっては ip-ranges.json を返してくれるAPIは画期的に便利なのです。
なぜ便利なのかはまた別途記事にするとして、今回は何はともあれAPIを蹴ってみて、
返ってきたデータをうまいこと加工してみたいと思います。

 

1. jqを使う方法

jqのインストールがまだであればぜひ。優れたツールです。
公式のダウンロードページを参考にすれば

 

Linuxなら

$ curl -o /usr/bin/jq http://stedolan.github.io/jq/download/linux64/jq
$ chmod +x /usr/bin/jq

OS Xなら Homebrew前提ですが

$ brew install jq

WindowsならexeをダウンロードすればOKです。

 
 

ではまず、どんなサービスの指定ができるのかを改めて確認してみましょう。

$ curl https://ip-ranges.amazonaws.com/ip-ranges.json | jq '[.prefixes[] | .service] | unique'

[
  "AMAZON",
  "CLOUDFRONT",
  "EC2",
  "ROUTE53",
  "ROUTE53_HEALTHCHECKS"
]

AWSブログで荒木さんが書かれている通りですね。
さてその上で、こんなことができます。

 

もし、CloudFrontのIPアドレスレンジが必要なら

$ curl https://ip-ranges.amazonaws.com/ip-ranges.json | jq '[.prefixes[] | select(.service == "CLOUDFRONT") | .ip_prefix] | sort'

[
  "204.246.164.0/22",
  "204.246.168.0/22",
  "204.246.174.0/23",
  "204.246.176.0/20",
  "205.251.192.0/19",
  "205.251.249.0/24",
  "205.251.250.0/23",
  "205.251.252.0/23",
  "205.251.254.0/24",
  "216.137.32.0/19",
  "54.182.0.0/16",
  "54.192.0.0/16",
  "54.230.0.0/16",
  "54.239.128.0/18",
  "54.239.192.0/19",
  "54.240.128.0/18"
]

もし、Route53からのヘルスチェックIPアドレスレンジが必要なら

$ curl https://ip-ranges.amazonaws.com/ip-ranges.json | jq '[.prefixes[] | select(.service == "ROUTE53_HEALTHCHECKS") | .ip_prefix] | sort'

[
  "107.23.255.0/26",
  "176.34.159.192/26",
  "177.71.207.128/26",
  "54.183.255.128/26",
  "54.228.16.0/26",
  "54.232.40.64/26",
  "54.241.32.64/26",
  "54.243.31.192/26",
  "54.244.52.192/26",
  "54.245.168.0/26",
  "54.248.220.0/26",
  "54.250.253.192/26",
  "54.251.31.128/26",
  "54.252.254.192/26",
  "54.252.79.128/26",
  "54.255.254.192/26"
]

返ってきたCIDRを眺めていると、あんなことやこんなことに使いたくなって・・きますよね!

 

2. Node.jsを使う方法

AWS SDKがあり、AWS Elastic Beanstalkで簡単に動かせるので、Node.js。
こんな server.js を書いて、nodeを起動してみます。

var https = require('https'),
    http = require('http');

http.createServer(function (request, response) {
  switch (request.url) {
  default:
    var target = request.url.substring(1).toUpperCase();

    // API呼び出し
    var options = {
      method: 'GET',
      host: 'ip-ranges.amazonaws.com',
      path: '/ip-ranges.json',
      port: 443
    };
    https.request(options, function (res) {
      var body = '';
      res.on('data', function (chunk) {
        body += chunk;
      });
      res.on('end', function () {

        // 必要なデータのみ抽出
        var result = [];
        JSON.parse(body).prefixes.map(function (item) {
          if (target == '') {
            result.push(item.service);
            return;
          }
          if (item.service == target)
            result.push(item.ip_prefix);
        });

        // データを整形
        result.sort();
        if (target == '') {
          result = result.filter(function (elem, pos) {
            return result.indexOf(elem) == pos;
          });
        }
        response.writeHead(200, {'Content-Type': 'application/json'});
        response.end(JSON.stringify(result));
      });
    }).end();
    break;

  case '/ping':
    response.writeHead(200, {'Content-Type': 'text/plain'});
    response.end('pong');
    break;
  }
}).listen(8081, '127.0.0.1');
console.log('Server running at http://127.0.0.1:8081/');

これでjqを使わなくても、ほしい値が取れるようになりました。

$ curl http://127.0.0.1:8081/

[
  "AMAZON",
  "CLOUDFRONT",
  "EC2",
  "ROUTE53",
  "ROUTE53_HEALTHCHECKS"
]
$ curl http://127.0.0.1:8081/cloudfront

[
  "204.246.164.0/22",
  "204.246.168.0/22",
  "204.246.174.0/23",
  "204.246.176.0/20",
  "205.251.192.0/19",
  "205.251.249.0/24",
  "205.251.250.0/23",
  "205.251.252.0/23",
  "205.251.254.0/24",
  "216.137.32.0/19",
  "54.182.0.0/16",
  "54.192.0.0/16",
  "54.230.0.0/16",
  "54.239.128.0/18",
  "54.239.192.0/19",
  "54.240.128.0/18"
]

そして、server.js をちょっと書き換えれば、こんなものが返せたりするわけです。

"Sg80CloudFront": {
  "Type": "AWS::EC2::SecurityGroup",
  "Properties": {
    "VpcId": {
      "Ref": "VPC"
    },
    "Tags": [
      {"Key" : "Name", "Value": "80 CloudFront"}}
    ],
    "GroupDescription": "Allows inbound HTTP access from CloudFront.",
    "SecurityGroupIngress": [
      {
        "IpProtocol": "tcp",
        "FromPort": "80",
        "ToPort": "80",
        "CidrIp": "204.246.164.0/22"
      },
      {
        "IpProtocol": "tcp",
        "FromPort": "80",
        "ToPort": "80",
        "CidrIp": "204.246.168.0/22"
      },
      ...
      {
        "IpProtocol": "tcp",
        "FromPort": "80",
        "ToPort": "80",
        "CidrIp": "54.240.128.0/18"
      }
    ],
    "SecurityGroupEgress": [
      {
        "IpProtocol": "tcp",
        "FromPort": "80",
        "ToPort": "80",
        "CidrIp": "0.0.0.0/0"
      }
    ]
  }
}

ここではSecurityGroupを例にしましたが、当然NACLやバケットポリシーにも応用できます。
このようにして生成したCloudFormationテンプレートを使って、AWS SDKと組み合わせ、
cronで定期的に動かせばメンテフリーでセキュアな状態が保てる・・?
 
便利そうですね!