SSHポートフォワードを悪用する
セキュリティの関係でアクセスしたいサーバーに通信が通らないケースは結構多いと思いと思う
今回はSSHポートフォワードを利用して一時的に直接通信不可能なセグメントへのアクセスを可能にする方法紹介する
状況:ローカルPCセグメントからターゲットサーバーにアクセスしたいが、アクセスができない
このような状態でもターゲットや中継ホストからはローカルPCへアクセス可能であるケースは多い。 というのFWはステートフルインスペクションという機能で特定のNWからアクセスを開始した時にのみ通信を許可/遮断する設定であることが多いからである
FWルールの非対称性を突いて中継サーバーからローカルPCにコネクションを張ってFWを抜けた後にポートフォワードをする方法をとってみる
[shell]
中継ホストで実行
ssh -p -R ローカルPCのポートフォワード対象Port:ターゲット:ポートフォワード先Port ローカルPCユーザー名@ローカルPC
コマンド例
ssh -p -R 80:172.16.0.3:8000 root@192.168.0.2
自己責任の範囲でお使いください
PythonでIPアドレスを簡単に扱う方法
IPアドレスってプログラムから扱おうとするとめんどくさいですよね
数字だけならいいのですが、たいてい192.168.0.2
とか間にドットが入ってくる
またサブネットもオクテット刻みであればドットごとに分けて数字として計算すれば加算、減算できますが/23
とか
半端な値になると結構めんどくさいです。
2点間のIPレンジの一覧が欲しい時、以下のように書いていました
# 2IPアドレス間の一覧を列挙する # start_ip '192.168.0.' end_ip '192.168.2.5' subnet '255.255.254.0' def generate_ipaddr_list(start_ip,end_ip,subnet): start_ip_list = [int(i) for i in start_ip.split('.')] end_ip_list = [int(i) for i in end_ip.split('.')] subnet_list = [int(i) for i in subnet.split('.')] int_mask = ip_to_binip(subnet).count('1') result_ips = [] try: for oct1_ip in range(start_ip_list[0],end_ip_list[0]+1): for oct2_ip in range(start_ip_list[1],end_ip_list[1]+1): for oct3_ip in range(start_ip_list[2],end_ip_list[2]+1): for oct4_ip in range(start_ip_list[3],end_ip_list[3]+1): ip_l = [oct1_ip,oct2_ip,oct3_ip,oct4_ip] ip_str = '.'.join([str(i )for i in ip_l]) if ip_l <= end_ip_list and ip_to_binip(ip_str)[:int_mask] == ip_to_binip(start_ip)[:int_mask]: result_ips.append(ip_str) else: raise Exception except Exception: pass finally: return result_ips # 10進数のIPをバイナリ形式に変換 def ip_to_binip(str_ip): str_ip_list = [int(i) for i in str_ip.split('.')] b_str_ip = '' for s in str_ip_list: b_str_ip += format(s,'b').zfill(8) return b_str_ip
一度バイナリ形式に変換してから計算する仕組みです。 Pythonでバイナリ型はIP計算で使いたいような算術演算がないので使いにくいです
計算しにくいなーと思っていたら、そもそもPythonにipaddressってモジュールがありました
使い方
import ipaddress as ip start_ip = ip.IPv4Address('192.168.0.100') end_ip = ip.IPv4Address('192.168.3.100') # 特定のIPアドレス間の一覧を表示 i = 0 while start_ip + i <= end_ip: now_ip = start_ip + i print(now_ip.exploded) i += 1 # =< 192.168.0.100 ,192.168.0.101 ... ,192.168.3.100 # ネットワークセグメントのIP一覧 for addr in ip.IPv4Network('192.168.0.0/23'): # 255.255.254.0でも可 print(addr.exploded) # IPアドレスがプライベートか否か start_ip.is_private # =>True # 2進数へ変換 start_ip.packed # => True test_ip = ip.IPv4Interface('192.168.2.100') # IPv4Networkオブジェクトを生成 IPを指定していないと/32 test_ip.network # => IPv4Network('192.168.2.100/32') # /XXの形 test_ip.with_prefixlen # => '192.168.2.100/32' # /255.255 ~の形 test_ip.with_netmask # => '192.168.2.100/255.255.255.255'
IPv4InterfaceクラスはIPv4Addressクラスのサブクラスのため、特に問題なければIPv4Interfaceクラスでオブジェクト生成したほうがいいかもしれません
Railsで簡単にMarkdownを使えるようにする
たまにRailsアプリだけど、静的ページだから手間をかけずにメンテしたいとか、 Railsわからない人にもメンテしてほしいページとかがあります そんなときにはMarkdown形式でページが作成されていると便利です
今回はRubyのMarkdown用Gem「redcarpet」を利用したRailsアプリでのMarkdown記法利用方法について説明します
今回試した環境
ディレクトリ構成
- top:今回のviewに対応するコントローラー。top#index => rootとなるように設定
方法
- redcarpet gemを追加する
- application_helperにredcarpetを利用するメソッドを作成する
# helpers/application_helper.rb module ApplicationHelper # markdown形式のファイルをhtmlに変換 # 例ファイル名:"index.md" controller:Top # = "#{APP_ROOT}/app/views/top/index.md" # def render_md(md_path) #ファイル名のみ:controller名フォルダ配下を参照 #ディレクトリ+ファイル名:Viewsルートからのパスを参照 if File.basename(md_path) == md_path path = "#{File.expand_path(Dir::getwd)}/app/views/#{controller.controller_name}/#{md_path}" else path = "#{File.expand_path(Dir::getwd)}/app/views/#{md_path}" end return nil unless File.extname(path) == ".md" text = File.open(path,"r") do |f| f.read end # redcarpetのオプション類 options = { filter_html: true, hard_wrap: true, space_after_headers: true, } extensions = { autolink: true, no_intra_emphasis: true, fenced_code_blocks: true, } renderer = Redcarpet::Render::HTML.new(options) markdown = Redcarpet::Markdown.new(renderer, extensions) markdown.render(text).html_safe end end
- controllerのerbテンプレートにMarkdown用のrenderを記載する
#views/top/index.html.erb <%= render_md "index.md" %> <%= render_md "shared/shared.md" %>
できあがり
pysmbの設定とSMBバージョン差異について
pythonでSMB共有をするときは「pysmb」ライブラリが有名かと思います。
使い方はこんな感じです
import platform from smb.SMBConnection import SMBConnection if __name__ == "__main__": user = '' pw = '' remote_host = '' ip = '' domain_name='' #workgroupの場合はなしorWORKGROUPでOK # 設定1 conn = SMBConnection( user, pw, platform.uname().node, remote_host, domain=domain_name, use_ntlm_v2=True ) conn.connect(ip, 139) # 設定2 conn = SMBConnection( user, pw, platform.uname().node, remote_host, domain=domain_name, use_ntlm_v2=True, is_direct_tcp=True) conn.connect(ip, 445)
⇛これでTrueが返ってくれば接続OK
ただ使ってみると接続エラーがたくさん出てうまくいかないことがあります
なぜつながらないのか
SMBには様々なバージョンが存在し、オプション設定がどのような役割を果たすのか理解していないと共有サーバーの設定と不一致となり、ID、PASSが適切であっても接続できないケースがあります
SMBで使うポート
137/UDP 137/TCP 138/UDP 139/TCP 445/UDP 445/TCP
SMBの動き(ざっくり)
SMB1/CIFS Netbios名で宛先を解決し、データを送信(Port 137,138,139) SMB1のみ SMB Direct(Port 445)をサポート(CIFSは対象外) SMB2以降 SMB Direct(Port 445)を利用
アクセス時には上位のプロトコル(SMB3)から順に試行してクライアント、サーバー双方が利用できるVerを利用します。
最近のSMB事情
Windows2012R2/Window8.1以降はデフォルトでSMB1が無効になっています →Port139でのアクセスは不可
ただ、古い機器等所有している企業などはSMB1系しか対応していない機器もあるためSMB1の対応を設定することも多いようです
- Windowsの設定画面
ですので、 アクセス対象のサーバーがCIFSにしか対応していない場合 →SMB Directが使えない
SMB1機能を無効化している場合 →CIFS/SMB1の機能であるNetbios名ファイル共有が使えない
結論
pysmb等で繋ぐ場合は 古い機器の場合
- is_direct_tcp=False または書かない(Default:False)
- connectポート指定は139
新しい機器の場合
- is_direct_tcp=True
- connectポート指定は445
仕様を読む限り、is_direct_tcp=Trueのほうが新しいプロトコルを使って共有アクセスするので性能が出るはず。なので設定しておいたほうがいいかと思います
参考 http://www.atmarkit.co.jp/ait/articles/0010/07/news002.html http://www.atmarkit.co.jp/ait/articles/1501/19/news092.html http://www.atmarkit.co.jp/ait/articles/1507/02/news026.html
DockerでMacVlanを使う際に気を付けること
本稼働のDockerを入れようとしたときにちょっとはまったのでメモ。
Dockerに外部からアクセスさせるときに標準ではブリッジネットワーク+ホストIPへのポートフォワードをするが あまりパフォーマンスがよくないとの話を聞き、本稼働用DockerへはMacvlanを試してみることにしました
そもそもDocker のMacvlanとは?
Dockerの外部ネットワーク設定方法の1つ。別にDocker専用の機能ではなく実態はLinux標準実装の仮想インターフェース機能を用いて物理NICにサブIPを持たせている
イメージ的にはジャンル全然違いますがWEBサーバーのVirtualhostやDNSのCNAMEみたいなものと認識しています
で、この仮想IFってのが問題。今時Docker入れるようなところはまず間違いなくDocker on ハイパーバイザだ。
実際に遭遇したのはVMware環境だが、仮想スイッチはデフォルトで大体こんな設定になっている
仮想IFのMACアドレスはスイッチ側で認識されていないMACアドレスのため通常のARPテーブルを使ったスイッチングではパケットが送られない。そのためMacVlanを使う場合には無差別モードというスイッチが受け取ったパケットは配下のポートへMACアドレス見ずに垂れ流しってモードにする必要がある
私の時には遭遇しませんでしたが、無差別モードはVMのNICに対しても同様の設定ができ、e1000の場合は明示的に指定、VMNET2,3系NICは無差別モードにしなくても必要に応じて切り替えてくれるともどこかのサイトに書いてありました。
vSANの性能について
最近のサーバー周りではHCI(ハイパーコンバージドインフラストラクチャ)が流行っています。
今までストレージで処理していた冗長性(RAID)部分を筐体間でRAIDを作るような仕組みです
ベンダー資料やメーカーの説明を聞く限り、パフォーマンスに問題がないということだが実際はどうなのだろうか。 気になったので試してみました
検証構成
今回は評価版で試せるvSANで実施しています 詳しい構成は省きますが、自宅サーバー組み換えのタイミングでやったのでこんな感じです
物理ホスト1
物理ホスト2
物理ホスト3 Withess用 適当なNUC
ディスク構成
ネットワーク設定はこんな感じ
10GbpsのNICがないのでとりあえずあるだけNIC突っ込んでNICチーミングで負荷分散できないかとこの構成にしました
結果と考察
vSAN上のWindows仮想マシンでCrystal Disk Infoを回した結果がこちら
Writeが遅い。。。。
なお画像は取り忘れましたが、同じ構成をSSDオンリーのローカルストレージで回したらWriteは500MB/sくらい出ていました。
ここから考察すると以下と考えられる
- vSANのReadは分散ディスクのうちどちらか一方からのみ応答を行っている
- Writeは整合性担保の関係で対向ストレージからの応答を待ってからWrite完了を返している
- NICの負荷分散は物理ホストが1対1である限り意味をなさない。おそらくチーミングやスイッチ側リンクアグリゲーションいずれを使っても、NIC振り分けがPortやIP,Macアドレスに縛られる限り分散はしない。
結論
vSAN用のNICは10G必須みたいです。
今回の検証前にKVMwith oVirt+Glusterfsも試してたのですがうまく構築できませんでした。環境構築がうまくいったら試してみようと思います