こんにちは。GMOアドマーケティングadcloud開発チームのN.Sです。
サーバへのアクセスを分析する中で、アクセス元がクライアント端末かサーバかで処理を分けたいというニーズが発生しました。
まずは試しにIPアドレスが公開されているAWSからのアクセスを調べてみようということで、今回はAWSのIPアドレス一覧の作成から判定までの流れをまとめようと思います。
▼流れは以下の通り
①AWSのWebページから取得
②JSONをパースし、整数値のレンジに変換
③実際のアクセスをレンジ内か判定
①AWSのWebページからの取得方法
AWSのIPアドレスはWebページに掲載されています。
https://ip-ranges.amazonaws.com/ip-ranges.json
JSON形式でIPv4、IPv6ごとにIPアドレスをCIDR表記でまとめられています。
②JSONをパースし、整数値に変換
ローカルに持ってきたはいいものの、JSON形式なのでこのままでは使えません。 ここではひとまずIPv4のIPアドレスを取得し、整数値のレンジとしてリストを作成します。
IpManager.java
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
import lombok.Getter; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.annotate.JsonDeserialize; import org.codehaus.jackson.map.annotate.JsonSerialize; import java.io.File; import java.util.*; public class IpManager { List ipList = new ArrayList(); private static ObjectMapper mapper = new ObjectMapper(); public IpManager(String inputFilePath) throws Exception { try { // 今回はIPv4のみ抽出(JSONのprefixesにIPv4アドレスが格納されている) IpAddressDataList ipAddressDataList = mapper.readValue(new File(inputFilePath), IpAddressDataList.class); for (IpAddressData ipAddressData : ipAddressDataList.getPrefixes()) { // すべての IP アドレス範囲を取得するには"AMAZON"を指定する。 // (たとえば、EC2 サブセットの範囲は、AMAZON サブセットにも含まれます) if (!ipAddressData.getService().equals("AMAZON")) { continue; } String cidr = ipAddressData.getIp_prefix(); CidrNode cidrNode = new CidrNode(cidr); ipList.add(cidrNode); } } catch (Exception e) { throw e; } } public boolean exists(String remoteAddr) { try { CidrNode cidrNode = new CidrNode(remoteAddr); for (CidrNode ipNode : ipList) { if (ipNode.contains(cidrNode)) { return true; } } return false; } catch (Exception e) { throw e; } } @Getter @JsonDeserialize @JsonSerialize @JsonIgnoreProperties(ignoreUnknown = true) static class IpAddressDataList { @JsonProperty("prefixes") private List prefixes; } @Getter @JsonDeserialize @JsonSerialize @JsonIgnoreProperties(ignoreUnknown = true) static class IpAddressData { @JsonProperty("ip_prefix") private String ip_prefix; @JsonProperty("service") private String service; } } |
CidrNode.java
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 |
public class CidrNode { private long start; private long end; /** * @param cidr CIDR形式。209.222.44.0/29 */ public CidrNode(String cidr) { String[] addr = cidr.split("/"); int length = addr.length == 1 ? 32 : Integer.parseInt(addr[1]); start = ipToLong(addr[0]); end = start + cidrLength(length) - 1; } private long cidrLength(int bit) { return (1 << (32 - bit)); } private long ipToLong(String ipAddress) { String[] ipAddressInArray = ipAddress.split("\\."); long result = Long.parseLong(ipAddressInArray[0]) << 24; result += Long.parseLong(ipAddressInArray[1]) << 16; result += Long.parseLong(ipAddressInArray[2]) << 8; result += Long.parseLong(ipAddressInArray[3]); return result; } public boolean contains(CidrNode n) { return start <= n.start && n.end <= end; } } |
③実際のアクセスをレンジ内か判定
さて、これでようやくアクセスが来た際に比較するためのリストができました。
あとはアクセスが来た際にIPアドレスをIpManager#existsで判断してやるだけです。
1 2 3 4 |
IpManager ipManager = new IpManager("test.txt"); if (ipManager.exists("13.57.50.51")) { // AWSの場合 } |
これでAWSからのアクセスを判別することができるようになります。
最後に
主にIPアドレス判定用リスト作成の話になってしまいましたが、いかがだったでしょうか。
数あるクラウドサービスの中でもIPアドレスを公開しているのは自分の知る中ではAWSくらいなのですが、他サービスも公開してくれるとアクセスを分析しやすくなるため、非常に助かります。
(セキュリティへの懸念もあるとは思いますが。)