C#によるNetwork ACL編集アプリケーションの作成

2013.02.08

プログラムからNetwork ACLを編集してみる

Amazon VPCでは従来のEC2インスタンスに対するSecurity Groupに加えて、Network ACLというセキュリティポリシーをサブネットに対して適用することができます。このNetwork ACLの設定項目数が多くなり、かつ頻繁に変更になる場合、AWS Management Consoleからの設定変更は現実的ではありません。この問題を解決するために、今回はAWS SDK for .NETを用いたNetworkACL作成アプリケーションを作成してみます。

(※Network ACLに登録可能なポリシー数には制限があり、制限緩和してもなお制限があるために、本記事の作戦が通用しなかったケースがあり、実際にNetwork ACLをコードにより制御するユースケースが存在するかはわかりません)

また、Network ACLに関しては下記ブログを参考にして下さい。

・Amazon VPCのネットワークACLについて | クラスメソッド開発ブログ
https://dev.classmethod.jp/cloud/amazon-vpc-acl/

開発環境

本記事のサンプルを実行するにはVisual Studio 2010またはVisual Studio 2012及び、AWS Toolkit for Visual Studioがインストールされている必要があります。

・AWS:AWS Toolkit for Microsoft Visual Studioをインストールしてみる
https://dev.classmethod.jp/cloud/aws-toolkit-for-visual-studio/

Network ACL一覧の取得

最初に、Network ACL一覧を取得してみます。Visual Studioで新規に「AWS Console Project」を作成し、 Mainメソッドから呼び出されているGetServiceOutput()メソッドの中を空にします。次に、ウェルカムメッセージとNetworkACL一覧を表示するメソッドを呼び出すコードを記述します。

public static void Main(string[] args) 
{ 
    Console.Write(GetServiceOutput()); 
    Console.Read(); 
}

public static string GetServiceOutput() 
{ 
    StringBuilder sb = new StringBuilder(1024);
    
    using (StringWriter sr = new StringWriter(sb)) 
    { 
        PrintWelcomeMessages(sr); 
        DescribeNetworkAcls(sr); 
    }
    return sb.ToString(); 
}

private static void PrintWelcomeMessages(StringWriter sr) 
{ 
    sr.WriteLine("==========================================="); 
    sr.WriteLine("Welcome to the AWS .NET SDK!"); 
    sr.WriteLine("===========================================");
    sr.WriteLine(); 
} 

private static void DescribeNetworkAcls(StringWriter sr)
{
    AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(RegionEndpoint.APNortheast1);
    DescribeNetworkAclsRequest ec2Request = new DescribeNetworkAclsRequest();

    try
    {
        DescribeNetworkAclsResponse ec2Response = ec2.DescribeNetworkAcls(ec2Request);
        List<NetworkAcl> networkAclList = ec2Response.DescribeNetworkAclsResult.NetworkAcls;

        foreach (var item in networkAclList)
        {
            sr.WriteLine("NetworkAclId" + "\t" + "Default" + "\t" + "VpcId");
            sr.WriteLine("--------------------------------------");
            sr.WriteLine(item.NetworkAclId + "\t" + item.Default + "\t" + item.VpcId);

            sr.WriteLine("\t" + "NetworkACL Entries:");
            sr.WriteLine("\tIn/Out" + "\t\t" + "RuleNumber" + "\t" + "RuleAction" + "\t" + "PortRange" + "\t\t" + "Protocol" + "\t" + "CidrBlock");
            sr.WriteLine("\t---------------------------------------------------------------------------------------------------------");

            foreach (var entry in item.Entries)
            {
                StringBuilder entryString = new StringBuilder(1024);
                entryString.Append("\t" + (entry.Egress ? "Out" : "In") + "\t");
                entryString.Append("\t" + entry.RuleNumber + "\t");
                entryString.Append("\t" + entry.RuleAction + "\t");
                entryString.Append("\t" + portRangeUtil(entry.PortRange) + "\t");
                entryString.Append("\t" + protocolStringUtil(entry.Protocol) + "\t");
                entryString.Append("\t" + entry.CidrBlock);

                sr.WriteLine(entryString.ToString());
            }
        }
    }
    catch (AmazonEC2Exception ex)
    {
        if (ex.ErrorCode != null && ex.ErrorCode.Equals("AuthFailure"))
        {
            sr.WriteLine("The account you are using is not signed up for Amazon EC2.");
            sr.WriteLine("You can sign up for Amazon EC2 at http://aws.amazon.com/ec2");
        }
        else
        {
            sr.WriteLine("Caught Exception: " + ex.Message);
            sr.WriteLine("Response Status Code: " + ex.StatusCode);
            sr.WriteLine("Error Code: " + ex.ErrorCode);
            sr.WriteLine("Error Type: " + ex.ErrorType);
            sr.WriteLine("Request ID: " + ex.RequestId);
            sr.WriteLine("XML: " + ex.XML);
        }
    }
}

アプリケーションを実行してみると、下記のような出力結果を得ることができます。

===========================================
Welcome to the AWS .NET SDK!
===========================================

NetworkAclId    Default VpcId
--------------------------------------
acl-041f656d    False   vpc-de9fe4b7
        NetworkACL Entries:
        In/Out          RuleNumber      RuleAction      PortRange               Protocol        CidrBlock
        ---------------------------------------------------------------------------------------------------------
        Out             100             allow           ALL                     ALL             0.0.0.0/0
        Out             32767           deny            ALL                     ALL             0.0.0.0/0
        In              100             allow           ALL                     ALL             0.0.0.0/0
        In              32767           deny            ALL                     ALL             0.0.0.0/0
NetworkAclId    Default VpcId
--------------------------------------
acl-3d1f6554    False   vpc-de9fe4b7
        NetworkACL Entries:
        In/Out          RuleNumber      RuleAction      PortRange               Protocol        CidrBlock
        ---------------------------------------------------------------------------------------------------------
        Out             100             allow           ALL                     ALL             0.0.0.0/0
        Out             32767           deny            ALL                     ALL             0.0.0.0/0
        In              100             allow           ALL                     ALL             0.0.0.0/0
        In              32767           deny            ALL                     ALL             0.0.0.0/0

プログラムのポイントとしては、EC2クライアント(AmazonEC2)を作成し、DescribeNetworkAclsRequestを行なって、レスポンスを適当に整形している点です。このプログラムの流れは以降のソースでも同じように記述されています。また、見慣れない用語としては、Network ACLのエントリのEgressプロパティがあります。これはtrueの場合はOutboundのポリシーを、falseの場合はInboundのポリシーを表します。AWS Management Consoleには出てこない用語なので、コンソールへの出力結果もIn/Outに変換しています。

Network ACLエントリの作成

次に、Network ACLのエントリを作成したいと思います。

(Network ACL自体の作成は本記事では行いません)

public static string GetServiceOutput() 
{ 
    StringBuilder sb = new StringBuilder(1024);
    
    using (StringWriter sr = new StringWriter(sb)) 
    { 
        PrintWelcomeMessages(sr); 
        DescribeNetworkAcls(sr);
        CreateNetworkAclEntries(sr);
    }
    return sb.ToString(); 
}

private static void CreateNetworkAclEntries(StringWriter sr)
{
    try
    {
        AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(RegionEndpoint.APNortheast1);

        CreateNetworkAclEntryRequest request = new CreateNetworkAclEntryRequest();

        // Sampleリクエスト
        request.NetworkAclId = "acl-0123456789";
        request.Egress = true;
        request.RuleNumber = 123;
        request.RuleAction = "allow";
        request.PortRange = new PortRange() { From = 1024, To = 65535 };
        request.Protocol = "6";
        request.CidrBlock = "0.0.0.0/0";

        sr.WriteLine("Start to create network acl entries for the network acl id:" + request.NetworkAclId);

        CreateNetworkAclEntryResponse response = ec2.CreateNetworkAclEntry(request);
        string requestId = response.ResponseMetadata.RequestId;

        sr.WriteLine("Request Id:" + requestId);
    }
    catch (AmazonEC2Exception ex)
    {
        if (ex.ErrorCode != null && ex.ErrorCode.Equals("AuthFailure"))
        {
            sr.WriteLine("The account you are using is not signed up for Amazon EC2.");
            sr.WriteLine("You can sign up for Amazon EC2 at http://aws.amazon.com/ec2");
        }
        else
        {
            sr.WriteLine("Caught Exception: " + ex.Message);
            sr.WriteLine("Response Status Code: " + ex.StatusCode);
            sr.WriteLine("Error Code: " + ex.ErrorCode);
            sr.WriteLine("Error Type: " + ex.ErrorType);
            sr.WriteLine("Request ID: " + ex.RequestId);
            sr.WriteLine("XML: " + ex.XML);
        }
    }
}

アプリケーションを実行後、AWS Management Consoleで対象のNetwork ACLを確認するとエントリーが増えていることがわかります。

networkacl

プログラムで気になる部分はプロトコルを表す数値です。プログラムの処理結果から見ると6がTCPを表していることがわかります。これはIANAが定義しているプロトコル番号です。

実際のプログラムでは下記のような簡単なユーティリティを用いてプロトコル番号をプロトコル名(またはその逆)に変換します。

/// <summary>
/// プロトコル番号をプロトコル名に変換します。
/// 参考:
/// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml
/// </summary>
/// <param name="protocol"></param>
/// <returns></returns>
private static string protocolStringUtil(string protocol)
{
    string returnString = "";

    if (protocol == "1")
    {
        returnString = "ICMP";
    }
    else if (protocol == "6")
    {
        returnString = "TCP";
    }
    else if (protocol == "-1")
    {
        returnString = "ALL";
    }

    return returnString;
}

・PROTOCOL NUMBERS
http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml

Network ACLエントリの削除

最後に、Network ACLのエントリの削除を行いたいと思います。

public static string GetServiceOutput() 
{ 
    StringBuilder sb = new StringBuilder(1024);
    
    using (StringWriter sr = new StringWriter(sb)) 
    { 
        PrintWelcomeMessages(sr); 
        DescribeNetworkAcls(sr);
        DeleteNetworkAclEntries(sr);
    }
    return sb.ToString(); 
}

private static void DeleteNetworkAclEntries(StringWriter sr)
{
    try
    {
        AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(RegionEndpoint.APNortheast1);

        DeleteNetworkAclEntryRequest request = new DeleteNetworkAclEntryRequest();

        request.NetworkAclId = "acl-041f656d";
        request.Egress = true;
        request.RuleNumber = 123;

        sr.WriteLine("Start to delete network acl entries for the network acl id:" + request.NetworkAclId);

        DeleteNetworkAclEntryResponse response = ec2.DeleteNetworkAclEntry(request);
        string requestId = response.ResponseMetadata.RequestId;

        sr.WriteLine("Request Id:" + requestId);
    }
    catch (AmazonEC2Exception ex)
    {
        if (ex.ErrorCode != null && ex.ErrorCode.Equals("AuthFailure"))
        {
            sr.WriteLine("The account you are using is not signed up for Amazon EC2.");
            sr.WriteLine("You can sign up for Amazon EC2 at http://aws.amazon.com/ec2");
        }
        else
        {
            sr.WriteLine("Caught Exception: " + ex.Message);
            sr.WriteLine("Response Status Code: " + ex.StatusCode);
            sr.WriteLine("Error Code: " + ex.ErrorCode);
            sr.WriteLine("Error Type: " + ex.ErrorType);
            sr.WriteLine("Request ID: " + ex.RequestId);
            sr.WriteLine("XML: " + ex.XML);
        }
    }
}

アプリケーションを実行後、AWS Management Consoleで対象のNetwork ACLを確認すると先ほど追加したエントリーが削除されていることがわかります。

まとめ

今回はAWS SDKを用いてNetwork ACLの操作を行なってみました。同様のテクニックはSecurity Groupや他のAWSリソースの編集にも使えますし、他の言語で実装した場合も同じような流れでプログラムを実装することができます。このように、AWSではあらゆる部分でAWS SDKを用いて作業の効率化を行うことができますので、みなさんも.NETでAWSを操作するプログラムを書いてみてはいかがでしょうか。

おしまい。