LoginSignup
0
0

More than 3 years have passed since last update.

NTTPCの激安VPS「Indigo」のAPIで遊んでみる

Last updated at Posted at 2020-06-20

はじめに

NTTPCの激安VPS「Indigo」を使い始めたのですが、APIが用意されているということで遊んでみました。

やりたかったこと

基本的には自宅から各種実験用にVPSを使っていて、自宅からSSHでVPS上のインスタンスをに接続しています。
Indigoには、ファイアウォールが標準でついているので、

方向 プロトコル ポート IP
IN TCP 22(SSH) 自宅IPアドレス

ってな具合にやりたいわけですが、自宅IPアドレスは固定じゃないDDNS運用なので、IPアドレスが変更された場合にFWでDENYされちゃいます。

ということで、自宅IPアドレスが変わった場合にAPIでごにょごにょしてFWのルールを変更してしまいたいというわけです。

前提条件

以下の内容は、NTT PC コミュニケーションズのVPS「Indigo」上の、
- KVM Instance 1 CPU 1 GB - Ubuntu 18.04
で、動作検証しています。

やること

IndigoのAPIを使って、以下の処理を実装します。
1.DNSに問い合わせをかけて、IPアドレスが変わっていないか確認する。
2.APIキーから、アクセストークンを取得する。accesstoken
3. Firewallの一覧を取得して、対象のファイアウォールのID取得する。getfirewalllist
4. 現状のファイアウォールの設定内容を取得する。gettemplate
5. 変更前のIPアドレスになっているものを、新しいIPアドレスに置き換える。
6. ファイアウォールの設定を更新する。updatefirewall

事前準備

APIキーの取得

コントロールパネルから、API鍵を事前に取得しておく必要がありますので、API鍵とシークレットを入手しておきます。

node.jsのインストール

APIの入出力はJSONなようなので、node.jsを使います。

# apt-get install nodejs

ライブラリのインストール

node.jsで使うライブラリをnpm経由でインストールします。

# apt-get install npm
# npm install request dns log-timestamp

さぁ、つくりましょう。

とりあえず、書き殴ってみました。
実ははじめてnode.jsを使ってコードを書いたので変なとこあるかも。。

indigo-firewall.js
const IP_FILE = "/opt/indigo/ip.txt";
const FW_NAME = "FW_TEST";
const API_WAIT = 500;
const OAUTH_BASE = "https://api.customer.jp/oauth/v1";
const API_BASE = "https://api.customer.jp/webarenaIndigo/v1";
const HOST_FQDN = "hoge.hoge.com";
const API_CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const API_CLIENT_SECRET = "XXXXXXXXXXXXX";

require('log-timestamp');

main();

function main() {
    var dns = require('dns');
    dns.lookup(HOST_FQDN, function onNsLookup(err, address, family) {

        console.log("%s => %s", HOST_FQDN, address);

        var fs = require('fs');
        var oip;
        if (fs.existsSync(IP_FILE)) {
            oip = fs.readFileSync(IP_FILE, "utf8").trim();
            console.log("Previous address => %s", oip);
        } else {
            throw new Error("%s not found", IP_FILE);
        }

        if (oip != undefined && oip != address) {
            console.log("Address change detected %s => %s", oip, address);
            accessTokens(oip, address);
        } else {
            console.log("No address change detected");
        }
    });
}

function accessTokens(oip, nip) {

    var request = require('request');
    var options = {
        uri: OAUTH_BASE + "/accesstokens",
        json: {
            grantType: "client_credentials",
            clientId: API_CLIENT_ID,
            clientSecret: API_CLIENT_SECRET
        }
    };

    request.post(options, function(error, response, body) {
        if (!error) {
            console.log("Accesstoken => %s", body.accessToken);
            setTimeout(getFirewallList, API_WAIT, body.accessToken, oip, nip);
        } else {
            console.log("Failed to accesstoken. %s %s %s", error, response, body);
        }
    });
}

function getFirewallList(accessToken, oip, nip) {

    var request = require('request');
    var options = {
        uri: API_BASE + "/nw/getfirewalllist",
        headers: {
            "Authorization": "Bearer " + accessToken
        },
        json: true
    };

    request.get(options, function(error, response, body) {
        if (!error) {
            console.log("%d firewall template found", body.length);
            for (const i in body) {
                console.log("Firewall template %d => %s", body[i].id, body[i].name);
                if (body[i].name == FW_NAME) {
                    console.log("Target firewall template found");
                    setTimeout(getFirewall, API_WAIT, accessToken, body[i], oip, nip);
                }
            }
        } else {
            console.log("Failed to getfirewalllist. %s %s %s", error, response, body);
        }
    });
}

function getFirewall(accessToken, fw, oip, nip) {

    var request = require('request');
    var options = {
        uri : API_BASE + "/nw/gettemplate/" + fw.id,
        headers: {
            "Authorization": "Bearer " + accessToken
        },
        json: true
    };

    request.get(options, function(error, response, body) {
        if (!error) {
            var nfw = {
                templateid: fw.id,
                name: fw.name,
                inbound: [],
                outbound: []
            };

            for (const i in body) {
                const rule = body[i];

                console.log("Rule before => %s/%s/%s/%s", rule.type, rule.protocol, rule.port, rule.source);

                var filter = {
                    type: rule.type,
                    protocol: rule.protocol,
                    port: rule.port,
                    source: (rule.source == oip ? nip : rule.source)
                };

                if (rule.direction == "in") {
                    nfw.inbound.push(filter);
                } else if (rule.direction == "out") {
                    nfw.outbound.push(filter);
                } else {
                    throw new Error("Unknown direction found. %s", rule.direction);
                }

                console.log("Rule after  => %s/%s/%s/%s", filter.type, filter.protocol, filter.port, filter.source);
            }

            setTimeout(updateFirewall, API_WAIT, accessToken, nfw, nip);
        } else {
            console.log("Failed to gettemplate. %s %s %s", error, response, body);
        }
    });
}

function updateFirewall(accessToken, nfw, nip) {

    var request = require('request');
    var options = {
        uri: API_BASE + "/nw/updatefirewall",
        headers: {
            "Authorization": "Bearer " + accessToken
        },
        json: nfw
    };

    request.put(options, function(error, response, body) {

        if (!error && body.success) {
            console.log("Firewall %d => %s", body.firewallId, body.message);

            console.log("Update %s => %s", nip, IP_FILE);
            var fs = require('fs');
            fs.writeFileSync(IP_FILE, nip);
        } else {
            console.log("Failed to updatefirewall. %s %s %s", error, response, body);
        }
    });
}

ってな具合ですかね。
冒頭の定数を環境に合わせて設定いただければと思います。

定数 説明 設定例
IP_FILE 現在のIPアドレスを保持しておくためのファイルのパス。書き込み権限があるところに設定してください。 /opt/indigo/ip.txt
FW_NAME 設定を書き換えるファイアウォールの名前。Indigoのコントロールパネルで指定したやつ。 FW_TEST
API_WAIT 連続でAPIを実行すると怒られるので、API実行時の待ち時間(msec)500
HOST_FQDN DDNS運用している自宅のfqdn hoge.hoge.com
API_CLIENT_ID IndigoのAPI鍵
API_CLIENT_SECRET IndigoのAPI鍵のシークレット

さぁ実行しよう

スクリプトの配置

上のスクリプトをサーバ上に配置します。ここでは、/opt/indigo/indigo-firewall.jsとします。

現在のIPアドレスを書き出す。

スクリプトでIPアドレスの変更検知をするために現状のアドレスを保持するファイルを作成しておきます。

$ echo x.x.x.x > /opt/indigo/ip.txt

Indigoのファイアウォールの設定確認

IndigoのFWの設定は以下のようになっているとします。

方向 プロトコル ポート IP
IN TCP 22(SSH) x.x.x.x

IPアドレスの変更

プロバイダとの回線を一旦切るなりして、IPアドレスを変えます。
合わせて、DDNSの登録も更新します。
ここでは、x.x.x.xから、y.y.y.yに変更されたとします。

いざ実行

満を持して、スクリプトを実行します。

$ node /opt/indigo/indigo-firewall.js
[2020-06-20T09:31:01.591Z] hoge.hoge.com => y.y.y.y
[2020-06-20T09:31:01.595Z] Previous address => x.x.x.x
[2020-06-20T09:31:01.595Z] Address change detected x.x.x.x => y.y.y.y
[2020-06-20T09:31:01.860Z] Accesstoken => xxxxxxxxxxxxxxxxxxxxxxxxxxx
[2020-06-20T09:31:02.529Z] 1 firewall template found
[2020-06-20T09:31:02.530Z] Firewall template 1487 => FW_TEST
[2020-06-20T09:31:02.530Z] Target firewall template found
[2020-06-20T09:31:03.168Z] Rule before => Custom/TCP/22/x.x.x.x
[2020-06-20T09:31:03.168Z] Rule after  => Custom/TCP/22/y.y.y.y
[2020-06-20T09:31:08.932Z] Firewall 1487 => Firewall template is updated successfully.
[2020-06-20T09:31:08.932Z] Update y.y.y.y => /opt/indigo/ip.txt

Indigoのコントロールパネルから、設定が変わっているか確認します。

方向 プロトコル ポート IP
IN TCP 22(SSH) y.y.y.y

おお、見事に変わりました!

定期的に実行

1時間に1回実行されるように、cronに登録しておきます。

$ crontab -e
0 * * * * node /opt/indigo/indigo-firewall.js >> /var/log/indigo-firewall.log 2>&1

$ touch /var/log/indigo-firewall.log

あとは、logrotatedの設定もよしなに。

おわりに

始める前はめんどくさいなと思っていたけど、API自体はものすごくシンプルなので、やってみると意外とあっさりできました。それよりも、はじめてのnode.jsに戸惑ったわけですが。。

兎にも角にも、これで自宅からしかSSHできなくなったので、セキュリティ面でも安心できるようになりました!

0
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
0
0