概要
本記事はルータでIPアドレス/ポートを開放するための申請処理を作成します。
既に稼働しているPythonで作成したサービスをServiceNowに移植します。
仕様
IPアドレス/ポート開放サービスの仕様は次の通りです:
- 既存サービスは決められたIPアドレスの範囲のIPアドレスであればユーザが何時でも申請を行い、自動でIPアドレス/ポートを開放するACLをルータに追加します。また、既存サービスではルータに接続する前に既にIP/ポートが開放されているかを確認する。
- 一つの申請は1IPアドレスに限りますが、10ポートまで受け付けます。複数のポートはコンマ区切り又は「-」を利用して範囲指定が可能(例:80,85,8080-8085)。
- 複数の人が同時に申請を行えるため、ルータ接続は非同期にする。
- ユーザは自分が申請した申請一覧を表示することができます。
- 管理者はすべての申請を確認することができる。
- 全員は開放されているIP/ポート一覧を表示できる。
- IPアドレス/ポートを制御しているルータは東京と大阪にある。
- ルータのACLを処理する申請の内容(申請者、申請日時、IPアドレス/ポート)をSlackチャネルに投稿する。
- 管理者は決められたIPアドレス以外のIPアドレスの申請も行える。
- 管理者は代理申請を行うことができる。
- 申請者は自分が申請したIP/ポートを取消すことがでる。
- 管理者は指定したIPアドレス、ACL、ルータのACL設定を確認することがでる。
- 管理者はWebブラウザからACL行(ACL番号を指定、ACL一覧の最後からの行数)を一覧表示することができる。
- 管理者は東京と大阪のACLに差があるか確認できる。
- 管理者はWebブラウザから直接ルータのACLを挿入、削除することができる
- 定期的に利用されていない(該当パケットが適用されていない)ACLを削除する
- 監査用にユーザ及び管理者の処理をログに書き出す。
今回はServiceNowでIPアドレス/ポート開放の申請フォームのみを作成する。
ロールの作成
IPアドレス/ポート管理用のロールを作成します。ここれはロール名を「infra_itiservice_admin」を作成します。
- アプリケーション・ナビゲータから「role」を検索して、「User Administration」下の「Roles」を選択します。
2.新しいロールを作成するので「New」ボタンを押下します。
3.Nameに「infra_itiservice_admin」を入力して「Submit」ボタンを押下します。これでロール「infra_itiservice_admin」は作成されました。
フォームの作成
次に申請フォームを作成します。
1.アプリケーションナビゲータから「maintain」を検索して、Service Catalog下の「Maintain items」を選択します。
2.新しいフォームを作成するので「New」ボタンを押下します。
3.次の値を入力します。
No. | 項目 | 値(英語) | 値(日本語) |
---|---|---|---|
1. | Name | IP Port Open Request | IPアドレス/ポート開放申請 |
2. | Short description | Open ip port on router request | 外部からアクセスできるようにIPアドレス/ポートを開放する |
4.ポータルページで価格などを非表示にする。
「Portal Settings」タグを選択して次のように設定する。
フェールドの作成
フォームの入力フィールドを定義します。
1.フォームを保存するとページの下部分にタグが表示します。「Variables」タブを選択して「New」ボタンを押下します。
2.IPアドレスの検証をスキップするかのチェックボックスを作成します。
次のように設定した後に「Submit」ボタンを押下します。
No. | 項目 | 値(英語) | 値(日本語) |
---|---|---|---|
1. | Type | Label | ラベル |
2. | Order | 10 | 10 |
3. | Question | Validation | IPアドレスチェック |
4. | Name | infra_ipo_validation | infra_ipo_validation |
![]() |
|||
3.次のフィールドを定義します。「New」ボタンを押下します。 | |||
![]() |
|||
4.次のように設定します。 |
No. | 項目 | 値(英語) | 値(日本語) |
---|---|---|---|
1. | Type | Label | ラベル |
2. | Order | 70 | 70 |
3. | Question | Routers | ルータ |
4. | Name | infra_ipo_routers | infra_ipo_routers |
![]() |
|||
22.東京ルータ用のチェックボックスを定義します。 | |||
「New」ボタンを押下します。 | |||
![]() |
|||
23.次のように設定します。 |
クライアントスクリプトの作成
表示するフィールドと入力チェックを行うためにクライアントスクリプトを作成します。
今回は4つのスクリプトを作成します。
表示フィールドの制御
IPアドレスの検証のスキップとルータの選択は管理者の場合のみ表示します。一般ユーザでは非表示にします。
1.「Catalog Client Scripts」タブを選択します。新しいスクリプトを作成するため「New」ボタンを押下します。
2.次のように設定します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infra_ipo_cs_initialization |
2. | UI Type | All |
3. | Type | onLoad |
スクリプト |
function onLoad() {
var itiserviceAdmin = g_user.hasRole('infra_itiservice_admin'); // ログインしているユーザが管理者か確認
g_form.setDisplay('infra_ipo_routers', itiserviceAdmin); // ルータフィールを表示/非表示
g_form.setDisplay('infra_ipo_validation', itiserviceAdmin); // IPアドレスチェックを表示/非表示
}
IPアドレスチェック・スキップフィールドのチェック
1.「New」ボタンを押下します。
2.次のように設定します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infra_ipo_cs_skip_validation |
2. | UI Type | All |
3. | Type | onChange |
4. | Variable name | infra_ipo_skip_validation |
スクリプト |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue == '') {
return;
}
try {
g_form.hideFieldMsg('infra_ipo_ip_address');
var ajax = new GlideAjax('infraIpoSIValidateIpPorts');
ajax.addParam('sysparm_name', 'validateIp');
ajax.addParam('sysparm_ip_address', g_form.getValue('infra_ipo_ip_address'));
ajax.addParam('sysparam_ports', g_form.getValue('infra_ipo_ports'));
ajax.addParam('sysparm_skip_validation', newValue);
ajax.getXMLAnswer(function(answer) {
if (answer && answer.length > 0) {
g_form.setValue('infra_ipo_ip_address', '');
g_form.showFieldMsg('infra_ipo_ip_address', answer, 'error');
}
});
} catch (e) {}
}
IPアドレスフィールドのチェック
1.「New」ボタンを押下します。
2.次のように設定します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infra_ipo_cs_validate_ip |
2. | UI Type | All |
3. | Type | onChange |
4. | Variable name | infra_ipo_ip_address |
スクリプト |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue == '') {
return;
}
try {
var ajax = new GlideAjax('infraIpoSIValidateIpPorts');
ajax.addParam('sysparm_name', 'validateIp');
ajax.addParam('sysparm_ip_address', newValue);
ajax.addParam('sysparam_ports', g_form.getValue('infra_ipo_ports'));
ajax.addParam('sysparm_skip_validation', g_form.getValue('infra_ipo_skip_validation'));
ajax.getXMLAnswer(function(answer) {
if (answer && answer.length > 0) {
g_form.setValue('infra_ipo_ip_address', '');
g_form.showFieldMsg('infra_ipo_ip_address', answer, 'error');
}
});
} catch (e) {}
}
ポート番号フィールをチェックします。
1.「New」ボタンを押下します。
2.次のように設定します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infra_ipo_cs_validatePorts |
2. | UI Type | All |
3. | Type | onChange |
4. | Variable name | infra_ipo_ports |
スクリプト |
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue == '') {
return;
}
try {
var ajax = new GlideAjax('infraIpoSIValidateIpPorts');
ajax.addParam('sysparm_name', 'validatePort');
ajax.addParam('sysparm_ip_address', g_form.getValue('infra_ipo_ip_address'));
ajax.addParam('sysparam_ports', newValue);
ajax.addParam('sysparm_skip_validation', g_form.getValue('infra_ipo_skip_validation'));
ajax.getXMLAnswer(function(answer) {
if (answer && answer.length > 0) {
g_form.setValue('infra_ipo_ports', '');
g_form.showFieldMsg('infra_ipo_ports', answer, 'error');
}
});
} catch (e) {}
}
スクリプトインクルードの作成
クライアントスクリプトから呼び出されるサーバ側スクリプトを作成します。
今回は2つのサーバ側スクリプトを定義します。
1.アプリケーションナビゲータから「script include」を検索してSystem Definition下のScript Includesを選択します。
2.新しいスクリプトインクルードを作成するので「New」ボタンを押下します。
3.次のように設定します。設定した後に「Submit」ボタンを押下します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infraIpoSIValidateIpPorts |
2. | Client callable | チェック |
スクリプト |
var IPv4_PTN = new RegExp('^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$');
var PORT_RANGE_PTN = new RegExp('^[0-9]{1,5}([,|-][0-9]{1,5})*$');
var PORT_MIN = 1;
var PORT_MAX = 65535;
var infraIpoSIValidateIpPorts = Class.create();
infraIpoSIValidateIpPorts.prototype = Object.extendsObject(AbstractAjaxProcessor, {
validateIp: function() {
var ipAddress = this.getParameter('sysparm_ip_address');
var portString = this.getParameter('sysparam_ports');
var skipValidation = this.getParameter('sysparm_skip_validation');
if (!ipAddress) {
return '';
}
ipAddress = String(ipAddress).trim();
portString = String(portString).trim();
if (!IPv4_PTN.test(ipAddress)) {
return 'Please enter IP v4 address in xxx.xxx.xxx.xxx format.';
}
return new infraIpoSIValidateIpPortsUtils().validateIpPorts(ipAddress, portString, skipValidation);
},
validatePort: function() {
var ipAddress = this.getParameter('sysparm_ip_address');
var portString = this.getParameter('sysparam_ports');
var skipValidation = this.getParameter('sysparm_skip_validation');
if (!portString) {
return '';
}
ipAddress = String(ipAddress).trim();
portString = String(portString).trim();
if (!PORT_RANGE_PTN.test(portString)) {
return 'Please enter port number delimited by comma. Port number should be an integer value between ' + PORT_MIN + ' and ' + PORT_MAX + '.';
}
if (ipAddress == '' && portString == '') {
return '';
}
return new infraIpoSIValidateIpPortsUtils().validateIpPorts(ipAddress, portString, 'true');
},
type: 'infraIpoSIValidateIpPorts'
});
4.共有ユーティリティクラスを作成します。
「New」ボタンを押下します。
5.次のように設定します。設定した後に「Submit」ボタンを押下します。
No. | 項目 | 値 |
---|---|---|
1. | Name | infraIpoSIValidateIpPortsUtils |
2. | Client callable | チェック |
スクリプト |
var infraIpoSIValidateIpPortsUtils = Class.create();
infraIpoSIValidateIpPortsUtils.prototype = {
initialize: function() {},
validateIpPorts: function(ipAddress, portString, skipValidation) {
// check if ip address is valid
if (ipAddress && skipValidation != "true") {
var isIpAddressOK = isOKIpAddress(ipAddress); // check if allowed ip address
if (isIpAddressOK.length > 0) {
return isIpAddressOK;
}
}
// check if port numbers is valid and convert it into an array
var portArray;
if (portString) {
if (portString.length > 0) {
portArray = portString2PortArray(portString);
if (!Array.isArray(portArray)) {
return portArray;
}
}
}
// check if ip:port already is open
if (ipAddress && portArray && portArray.length > 0) {
var existResult = existsIpPort(ipAddress, portArray);
if (existResult.length > 0) {
return existResult;
}
}
return '';
// check if ip address is within allowed network
function isOKIpAddress(ipAddress) {
for (var i = 0; i < SERVER_ADDRESS_PATTERN_LIST.length; i++) {
if (SERVER_ADDRESS_PATTERN_LIST[i].test(ipAddress)) {
return '';
}
}
return 'IP ' + ipAddress + ' is not in allowed network';
}
// convert input port string to array of ports
function portString2PortArray(portString) {
var portArray = [];
var portFinalArray = [];
if (portString && portString.length > 0) {
portArray = portString.split(',');
for (var i = 0; i < portArray.length; i++) {
var portRange = portArray[i].split('-');
if (portRange.length > 2) {
return 'Please separate range with a comma';
} else if (portRange.length == 2) {
var startPort = parseInt(portRange[0]);
var endPort = parseInt(portRange[1]);
var validatePortResult = validatePortRange(startPort, endPort);
if (validatePortResult.length > 0) {
return validatePortResult;
}
if ((endPort - startPort) > 10) {
return 'Only 10 ports may be specified at once.';
}
for (var j = 0; j <= endPort - startPort; j++) {
portFinalArray.push(startPort + j);
}
} else {
var validateResult = validatePort(portArray[i]);
if (validateResult.length > 0) {
return validateResult;
} else {
portFinalArray.push(portArray[i]);
}
}
}
if (portFinalArray.length > 10) {
return 'Only 10 ports may be specified at once.';
}
return portFinalArray;
}
return '';
}
function validatePortRange(startPort, endPort) {
var validateResult = validatePort(startPort);
if (validateResult.length > 0) {
return validateResult;
}
validateResult = validatePort(endPort);
if (validateResult.length > 0) {
return validateResult;
}
if (startPort > endPort) {
return 'Invalid range. Start range value should be less than end range value.';
}
return '';
}
function validatePort(port) {
if (isNaN(port)) {
return 'Invalid port number. Please enter port number between ' + PORT_MIN + ' and ' + PORT_MAX;
}
if (port < PORT_MIN) {
return 'Port number should be greater than ' + PORT_MIN;
}
if (port > PORT_MAX) {
return 'Port number should be less than ' + PORT_MAX;
}
return '';
}
// check if ip/port is already opened
function existsIpPort(ipAddress, portString) {
return '';
}
},
type: 'infraIpoSIValidateIpPortsUtils'
};
// 許可するIPアドレスすの正規表現
var SERVER_ADDRESS_PATTERN_LIST = [
new RegExp("^192.168.0.([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4])$"),
];
var PORT_MIN = 1;
var PORT_MAX = 65535;
テスト
正しく動作するか確認します。
確認する必要があるのは次の点になります。
- すべての項目が正しく表示するか
- 管理者ユーザ以外の場合はIPアドレスチェックスキップとルータ項目は非表示
- IPアドレス及びポート番号がチェックされるか
- IPアドレス制限が正しくチェックされるか
- サービスポータルページでも正しく動作するか
UI環境
UI環境で正しく表示され、動作するか確認します。
1.アプリケーションナビゲータから「maintain ite」を検索してService Catalog > Catalog Definitions > Maintain Itemsを選択します。
2.一覧から今回作成した「IP Port Open Request」を選択します。
3.「Try It」ボタンを押下します。
4.正しく作成された場合は次のようなページが表示されます。
5.IPアドレス及びポート番号フィールドに値を入力して正しくチェックされるか確認します。
6.間違えた値を入力した場合は入力した値が初期化されてエラーメッセージが表示します。色々な値を入力してテストを行います。
7.IPアドレスフィールド及びポート番号フィールドのテストの後は管理者以外でのテストを行います。
管理者以外のユーザを作成します。既に管理者以外のユーザがいる場合は手順11までスキップします。
アプリケーションナビゲータから「users」を検索して、User Administration下のUsersを選択します。
8.「New」ボタンを押下します。
9.User IDフィールドとPasswordフィールドに値を入力します。
10.ヘッダ領域を右クリックしたSaveを選択します。それでユーザは作成されました。
11.ページ右上のユーザ名をクリックして「Impersonate User」を選択します。
12.作成したユーザ名を入力します。
13.ユーザが変更されたことを確認します。
14.再び作成したフォームを開きます。
IPアドレス検証フィールドとルータフィールドが表示されていないことを確認します。
15.ログインユーザ名をクリックして「End Impersonation」を選択します。
16.元の管理ユーザに戻されたことを確認します。
17.infra_itiservice_adminロールのテストを行います。
上で作成したユーザを開き、Rolesタブを選択して「Edit」ボタンを押下します。
18.「infra_itiservice_admin」ロールを付与します。
「infra」を検索して「infra_itiservice_admin」を選択した後に「>」ボタンを押下します。
19.右側に表示されます。「Save」ボタンを押下します。再びフォームを表示してIPアドレス検証フィールとルータフィールドが表示されていることを確認します。
サービスポータル
サービスポータルでも正しく表示されるか確認します。
先ずはサービスポータルにフォームを登録します。
1.アプリケーションナビゲータから「maintain ite」を検索してService Catalog > Catalog Definitions > Maintain Itemsを選択します。
2.一覧から作成した「IP Port Open Request」を選択します。
3.Catalogsフィールド右の南京錠アイコンをクリックします。
4.虫眼鏡アイコンをクリックします。
5.「Service Catalog」を選択します。
6.Categoryフィールドの右にある虫眼鏡アイコンをクリックします。
7.「Services」を選択します。
8.Catalogsの右に「Service Catalog」が表示され、Categoryの値は「Services」になります。
9.設定を保存します。
「Update」ボタンを押下します。
10.サービスポータルを開き、「Request Something」を選択します。
11.Office > Serviceを選択して「IP Port Open Request」を選択します。
12.次のようなページが表示されます。このページからの入力チェックやロールのチェックを行います。
以上