LoginSignup
2
0

More than 5 years have passed since last update.

ASP.NET WebAPIで Port Knockingを作ってみた。

Last updated at Posted at 2018-01-31

Port Knocking?

システム管理すると、遠隔地のサーバーに管理者としてアクセスする必要があるが往々にある。sshやhttpsで暗号化された通路を使用するが、外部に常時さらされている管理ポートは、brute-force攻撃を受け常だ。

特にsshの場合は、ネット上にtcp/22万うろついボットが存在程度のリスクだが、fail2banやsshguardのような一定回数以上失敗した場合、自動的にシステムのファイアウォール(iptables、pf、firewalld...)を利用して、一定時間ブロックする手法が必要である。

もし常時サービスが必要としていない、すなわち、管理目的のサービスに見合った手法が「ポート - ノッキング( Port Knoking)」である。クライアントは、デーモンの事前約束した文字列を渡して、予め約束されたポートを少しの間だけ開いておく式である。こうするとポートが常に開放される必要はない。

Basic API

提供するコマンドは以下の通り。

request:
POST -d '{action:"allow", ip:"127.0.0.1", port:8080}' /api/portkock
POST -d '{action:"deny", ip:"127.0.0.1", port:8080}' /api/portkock

response:
when OKay, {"result":"OK"}
when NG, {"result":"NG", "message":"bla bla ..."}

具現

referencesに "C:\Windows\System32\FirewallAPI.dll" を追加する必要がある。以下のようなシンプルなControllerを作ってみた。


using PortKnockService;
using System;
using System.Runtime.Serialization;
using System.Web.Http;

namespace PortKockWebApi.Controllers
{
    public class PortKnockController : ApiController
    {

        [HttpGet]
        public PortKnockResponse DoGet(PortKnockRequest req)
        {
            return new PortKnockResponse()
            {
                Result = "NG",
                Message = "unknown method"
            };
        }


        [HttpPost]
        public PortKnockResponse DoPost(PortKnockRequest req)
        {

            // need to log here.

            if (req.Action.ToLower() == "allow") {
                try
                {
                    FirewallUtils.AllowAddressPort(req.Ip, req.Port);
                    return new PortKnockResponse()
                    {
                        Result = "OK",
                        Message = null
                    };

                }
                catch (Exception e)
                {
                    return new PortKnockResponse()
                    {
                        Result = "NG",
                        Message = e.Message
                    };
                }
            }
            else if (req.Action.ToLower() == "deny")
            {
                try
                {
                    FirewallUtils.CloseAddressPort(req.Ip, req.Port);
                    return new PortKnockResponse()
                    {
                        Result = "OK",
                        Message = null
                    };

                }
                catch (Exception e)
                {
                    return new PortKnockResponse()
                    {
                        Result = "NG",
                        Message = e.Message
                    };
                }
            }

            return new PortKnockResponse()
            {
                Result = "NG",
                Message = "unknown error"
            };
        }
    }

    [Serializable]
    [DataContract(Name = "")]
    public class PortKnockRequest
    {
        [DataMember(Name = "action", IsRequired = true)]
        public string Action { get; set; }

        [DataMember(Name = "ip", IsRequired = true)]
        public string Ip { get; set; }

        [DataMember(Name = "port", IsRequired = true)]
        public int Port { get; set; }
    }

    [Serializable]
    [DataContract(Name = "")]
    public class PortKnockResponse
    {
        [DataMember(Name = "result")]
        public string Result { get; set; }

        [DataMember(Name = "message", IsRequired =false)]
        public string Message { get; set; }

    }
}

具現したもの

動作確認

実行する時にはFirewallを操作するため、管理者権限が必要だ。
image.png

curlで実行して
image.png

Powershellで結果を確認した。
image.png

改善点

  • proper listing feature current opened destinations

    list opened destinations
    request:
        GET /api/portkock[?items=50&page=1]
    response:
        {items:[
            {id="", requested_at="", expired_at="", destination={action:"allow", ip:"127.0.0.1", port:8080}, result:{}},
            ...
        ]}
    
  • proper getting request specific item details feature

    request:
        GET /api/portkock/<id>
    response:
        ...
    
  • proper logging feature
  • proper persistent to records
  • proper scheduling to automated expiration
  • not to show IIS error page
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0