Ruby
Network
Cisco
gotty

GoTTYでブラウザからルータを操作してみた

More than 3 years have passed since last update.


はじめに

ネットワークプログラマビリティ勉強会 #8の「ブラウザからルータを操作してみた」の関連記事です。GoTTYでWebブラウザのターミナルからルータへTELNETやRubyスクリプトの実行をしてみました。

Webブラウザで「ルータへのTELNET」と「コンフィグ一括保存スクリプト」の実行デモ

Web Terminal.gif


動機

QiitaのGoTTY 良さそうを見て、Webブラウザ上にターミナルを表示する方法を知りました。Go言語のバイナリをダウンロードし、gottyコマンドで非常に簡単にWebブラウザでターミナルを表示でき、通常のターミナルと遜色なく利用できたため、ルータを操作するために活用しようと考えました。

最初はルータへのTELNETのみを目的としていました。しかしながら、PerlやRubyで作成したCLIスクリプトの実行も可能である気づき、今まで作っていたスクリプトをWebブラウザ上で実行しようと考えました。

今回は、WebブラウザからルータへのTELNETと、Rubyスクリプトの実行を目的として作成しました。

GoTTYの使い方については、Qiitaの投稿のGoTTY 良さそうgottyに感動したをご参照ください。


仕組み

WebブラウザはGoTTYサーバのターミナルを、HTMLのiframeまたは直接アクセスで表示します。

GoTTYサーバはリクエストのたびに、TelnetコマンドやCLIスクリプトを実行します。リクエストにはルータのIPアドレスなどのパラメータが渡されます。それを元にルータにTelnetしたり、ルータをCLIスクリプトから操作します。

仕組み.png


環境



  • yudai/gotty 0.0.12 : GoTTYサーバ

  • Ubuntu 14.04

  • Ruby 2.2.4


    • sinatra 1.4.7 : Webフレームワーク

    • slim 3.0.6 : HTMLテンプレートエンジン

    • thor 0.19.1 : コマンドラインツールのフレームワーク

    • expect4r 0.0.11 : Expectライブラリ



  • Google Chrome 48 : Webブラウザ

  • Cisco VIRL 0.10.22.3 : Ciscoルータの仮想実行環境


スクリプト

ファイル一覧。gottyバイナリはGithubからダウンロードし、同じディレクトリに配置します。その他のファイルは下記に示します。

.

├── Gemfile Rubyライブラリ
├── Rakefile 起動用スクリプト
├── cli.rb CLIスクリプト、GoTTYから実行
├── gotty GoTTYのバイナリ
├── views
│   └── index.slim HTMLテンプレートファイル
└── web.rb Webサーバ


Rakefile

タスクとして下記を記述してます


  • server : Webサーバの起動

  • gotty : GoTTYサーバの起動


Rakefile

task :server do

ruby 'web.rb'
end

task :gotty do
sh './gotty -w --permit-arguments ruby cli.rb'
end



Gemfile

使用しているRubyライブラリ

source 'https://rubygems.org'

gem 'sinatra'
gem 'sinatra-contrib'
gem 'slim'
gem 'expect4r'
gem 'thor'


CLIスクリプト - cli.rb

GoTTYから起動する実行ファイルです。RubyとThorで各種コマンドを記述しています。

GoTTYから各サブコマンドを呼び出して、ターミナル上で表示しています。

Ruby/ThorはCLIアプリケーション用のフレームワークです。gitコマンドのようなサブコマンドを簡単に定義できます。今回はGoTTYから各種のサブコマンドを実行しています。

telnetサブコマンドが呼び出されると、直接tenletコマンドを実行します。

saveサブコマンドが呼び出されると、expect4rを利用して各ルータにTELNETし、copyコマンドでコンフィグを保存します。

#!/usr/bin/env ruby

require 'thor'
require 'expect4r'

HOSTS = %w(172.16.1.158 172.16.1.162 172.16.1.161).freeze

# TermCLI
class TermCLI < Thor
desc 'telnet IPADDRESS', 'telnet to IPADDRESS'
def telnet(ipaddr)
exec "telnet #{ipaddr}"
end

desc 'bash', 'execute bash shell'
def bash
exec 'bash'
end

desc 'save', 'save config'
def save
HOSTS.each do |host|
puts '#' * 60
puts "#{host}の保存"
ios = Expect4r::Ios.new_telnet(host: host, user: 'cisco', pwd: 'cisco')
ios.login
puts ios.putline("copy running-config startup-config\r", no_trim: true)
puts "#{host}の保存完了!"
puts
end
STDIN.gets
end

desc 'route', 'show ip route'
def route
HOSTS.each do |host|
puts '#' * 60
puts "#{host}のルート情報"
ios = Expect4r::Ios.new_telnet(host: host, user: 'cisco', pwd: 'cisco')
puts ios.show_ip_route
puts
end
STDIN.gets
end
end

TermCLI.start(ARGV)


Webサーバ - web.rb

Ruby/SinatraのWebサーバです。

GOTTY_BASEはGoTTYが動作するURIです。@terminals変数にHTMLテンプレートに渡すパラメータを直接埋め込んでいます。

#!/usr/bin/env ruby

require 'sinatra'
require 'sinatra/reloader'
require 'slim'
require 'yaml'

set :bind, '0.0.0.0'

GOTTY_BASE = 'http://172.30.21.18:8080'.freeze

get '/' do
@terminals = YAML.load(<<EOL)
- :name: R1
:type: Router
:desc: Telnet 172.16.1.158
:uri:
#{GOTTY_BASE}/?arg=telnet&arg=172.16.1.158
- :name: R2
:type: Router
:desc: Telnet 172.16.1.162
:uri:
#{GOTTY_BASE}/?arg=telnet&arg=172.16.1.162
- :name: R3
:type: Router
:desc: Telnet 172.16.1.161
:uri:
#{GOTTY_BASE}/?arg=telnet&arg=172.16.1.161
- :name: Bash
:type: Script
:desc: シェル実行
:uri:
#{GOTTY_BASE}/?arg=bash
- :name: Save
:type: Script
:desc: コンフィグ一括保存
:uri:
#{GOTTY_BASE}/?arg=save
- :name: Route
:type: Script
:desc: ルート確認
:uri:
#{GOTTY_BASE}/?arg=route
EOL

slim :index
end


HTMLテンプレート - views/index.slim

テンプレートエンジンのslimと、JavaScriptのjQueryを利用しています。

ターミナル一覧を表示します。ルータへのTELNETやCLIスクリプトの選択し、実行します。ターミナルの実行結果はiframeで埋め込みます。iframeで埋め込む処理はjQueryで記述してます。

「ターミナルを開く」をクリックすると、同じWebページ内にiframeでGoTTYを呼び出しターミナルが表示されます。

「新しいウィンドウで開く」をクリックすると、別ページとして、GoTTYのターミナルが表示されます。

doctype html

html lang="ja"
head
meta charset="utf8"
meta http-equiv="X-UA-Compatible" content="IE=edge"
meta name="viewport" content="width=device-width, initial-scale=1"
link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"
script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"
script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"
title
| Web Terminal
body
table.table
thead
th TYPE
th NAME
th DESCRIPTION
th
th
tbody
- @terminals.each do |terminal|
tr
td = terminal[:type]
td = terminal[:name]
td = terminal[:desc]
td
a.terminal_link href="#" data-name="#{terminal[:name]}" data-uri="#{terminal[:uri]}"
span.glyphicon.glyphicon-log-in
| &nbsp;ターミナルを開く
td
a href="#{terminal[:uri]}" target="_blank"
span.glyphicon.glyphicon-new-window
| &nbsp;新しいウィンドウで開く

#terminal_area
#terminal_template style="display:none"
.terminal_window
.panel.panel-default
.panel-heading
span.title
button.close type="button"
| &times;
.panel-body
.embed-responsive.embed-responsive-16by9
iframe.embed-responsive-item src=""
javascript:
$(function(){
$(document).on('click', '.close', function(){
$(this).parents(".terminal_window").remove();
});
$("a.terminal_link").on('click', function(){
$('#terminal_area').append($('#terminal_template').html())
var el = $('#terminal_area > div:last')
el.find('iframe').attr('src', $(this).data("uri"))
el.find('.title').text($(this).data("name") + ' Terminal')
});
})


実行結果

WebサーバとGoTTYサーバをrakeで起動します。その後、WebブラウザでWebサーバのIPアドレスをポートを指定して開きます。今回はWebサーバの「http://172.30.21.18:4567」になります。

$ rake server

/home/kooshin/.rbenv/versions/2.2.4/bin/ruby web.rb
[2016-02-29 00:39:28] INFO WEBrick 1.3.1
[2016-02-29 00:39:28] INFO ruby 2.2.4 (2015-12-16) [x86_64-linux]
== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from WEBrick
[2016-02-29 00:39:28] INFO WEBrick::HTTPServer#start: pid=4801 port=4567

$ rake gotty

./gotty -w --permit-arguments ruby cli.rb
2016/02/29 00:39:49 Permitting clients to write input to the PTY.
2016/02/29 00:39:49 Server is starting with command: ruby cli.rb
2016/02/29 00:39:49 URL: http://127.0.0.1:8080/
2016/02/29 00:39:49 URL: http://172.30.21.18:8080/


ルータへTELNET

ルータR1にTELNETして、ルータ上でshowコマンドの実行結果です。

Web Terminal1.png


RubyのCLIスクリプト実行

CLIスクリプトに定義したsaveコマンドを実行しています。今回は3台のルータのコンフィグを保存しています。

Web Terminal2.png


おわりに

GoTTYによってWebブラウザで簡単にCLIスクリプトの実行が可能になりました。LinuxサーバにSSHでログインして実行や、Windows上で各種ライブラリを集めて実行環境を作成することから解放されます。複数のユーザで同じ実行環境を作ることが可能になると思います。

既存資産のスクリプトを有効活用できます。運用で使っているちょっとしたスクリプトを実行するには最適な環境と言えます。

実際の運用で利用するためには、セキュリティには十分注意する必要があります。bashの直接実行ができてしまい、踏み台にされてしまう可能性があります。ユーザ認証やDocker上で起動させるなど、更に作りこみが必要になります。


参考文献