nfsのソフトマウント
自室の Linux サーバでは、自分が管理している Azure 上のサーバのデータバックアップを取っているが、ハードディスク容量が巨大な訳でもないので、iMac に接続された Drobo (複数ディスクの改良RAID?) に保存させている。
このネットワーク越しのバックアップでのディスクアクセスになるが、iMac を時々再起動させると、その間に icinga のディスクチェック処理が走った際に、アクセスできないために df プロセスが大量に残るトラブルが発生していた。リトライするため CPU 負荷も高い状態で、一番簡単なのは サーバの再起動。(iMacを再起動したら、その後にLinuxサーバを再起動….無駄や…)
最初は、check_disk 処理で余計なところを見させないために、特定マウント先を無視するオプションを加えていたが、df がマウント先をしつこくアクセスをリトライするのが原因。
よく考えたら、ネットワーク越しのマウントだから、ハードマウント(ディスクアクセスに失敗したら何度もリトライ)ではなく、ソフトマウント(複数回リトライしたら諦める)にする必要がある。
ということで、automount オプションに、”soft” オプションを加えて解決。
福井高専のドメイン名
そろそろ前期期末の成績締め切り。学生さんがレポート課題提出で先生にメールを送ることも多いが、メールアドレスの書き間違いでメールがエラーになって届かないトラブルがちらほら。
メールアドレスをどう書き間違えているのか確認すると、hoge@fukui-nct–ac.jp と書いていたりする。高専機構のドメイン名は、hoge@fukui.kosen-ac.jp で、ac の前がハイフンに間違えてるんだろうな。
まずは結論
高専機構(福井)のメールアドレスは、hoge@fukui.kosen–ac.jp です。
福井高専のメールアドレスは、hoge@fukui–nct.ac.jp です。
ということで、以下解説。
ドメイン名の一般ルール
ちなみに、日本のドメイン名は古くは、組織ドメイン.種別ドメイン.国ドメイン の形式。
組織ドメイン=fukui-nct , 種別ドメイン=ac(教育機関) , 国ドメイン=jp(日本)
種別ドメインには、.co.jp(会社) , .ne.jp(ネットワークサービス) , .or.jp(団体) , .go.jp(政府機関) といったものがあるが、企業のサービスだと、.co.jp なのか .ne.jp なのか曖昧だったりするので、最近は省略したものを申請できるようになっている。
国ドメインは、アメリカはネットを作った所なので、国ドメインは省略され、.us(アメリカ) を使うことはめったに無い。一方、国ドメインも、.jp(日本) , .uk(イギリス) , .ch(中国) とかあっても、世界中に拠点を持つ企業では、.jp なのかよくわからないので、国ドメインを持たない種別ドメイン .com (企業) を取得することも多い。
高専機構のドメイン名
日本では最近様々な形式の学校が出てきたため、.ac.jp のドメインを取る時の審査は厳しくなっている。
このため、高専機構では、kosen.ac.jp を取得したいが審査が通らず、しかたがないので kosen–ac.jp を取得している。
組織ドメイン=kosen-ac , 種別ドメイン=なし , 国ドメイン=jp(日本)
ちなみに、”-ac.jp” といった変則的なドメイン名は、教育機関を偽装したドメイン名と勘違いされやすく、kosen-ac.jp のドメイン名を見て「怪しい…」と思う人も多い。
福井高専のドメイン名
一方、高専機構ができた後の福井高専の英語の正式名称は、“National Institute of Technology, Fukui College” であり、福井高専のドメイン名としては、本来 “nit-fukui.ac.jp” を取得したい。
組織ドメインの綴りのルールは無いので、大学でも 大学名-u.ac.jp だったり u-大学名.ac.jp など色々あるが、最近は後者が主流となっている。(福井大学も以前は、fukui-u.ac.jp だったが、最近は u-fukui.ac.jp に変更されている)
しかし、.ac.jp の審査が厳しく、高専機構の1組織っぽい nit-fukui.ac.jp は審査が通らない可能性が非常に高く、どの高専も以前のドメイン名をそのまま使用しているのが現状である。
ログ解析とSOC演習(in 石川高専)
高専機構の情報セキュリティ人材育成イベントK-SECにて、主管の石川高専さんにて、「ログ解析とSOC演習」の講習会がありました。
ログ解析
セキュリティ機器の設定や組織内のルールにて、防衛を行うことはできるが、完全に脅威を検知・防御することは難しい。通信ログやアクセス履歴などの取得・蓄積しログから脅威を検出することが重要。
ログ解析演習
Raspberry-Pi のサーバが学生1人毎に準備してあり、演習用のログデータから目的となる情報を探す演習を行なった。
$ ls -R ~pi/log/event1-5/pcap/sqlinjection.pcap ~pi/log/event1-5/web-log/access_log_yyyymmdd.log
まずは基本コマンド
$ cd log/event1-5/web-log/ $ ls -al access_log_yyyymmdd.log $ cat access_log_yyyymmdd.log | more
ユーザーエージェントの確認
$ cat access_log_yyyymmdd.log | cut -d ' ' -f 12- | more # cut 特定の項目を抜き出すコマンド # -d ' ' データの区切り文字は ' ' 空白 # -f 12- 12項目以降を出力 # more 出力をページ単位で出力 # 大量のLOGだと、長時間かかるよぉ…
ディレクトリパストラバーサルの確認
$ grep "¥.¥./" access_log_yyyymmdd.log # grep 特定の文字を含む行を抜粋して表示 # アクセス履歴のPATHを確認 $ grep "¥.¥./" access_log_yyyymmdd.log | awk '{print $7}' # awk '{print $7}' 各行の7番目を出力 ( cut -d ' ' -f 7 と同じ ) # アクセス成功の確認 $ grep "¥./" access_log_yyyymmdd.log | grep "¥" 200" # Webサーバのアクセスログには、データ取得成功=200 が記載されている行を抜粋 # 特殊なアクセスPATHを確認 $ grep /proc/cpuinfo access_log_yyyymmdd.log # 脆弱なphp,cgiだとアクセスのURLに、アクセスしたいPATHが含まれる # /proc/cpuinfo は、CPUの種別などの情報が取れる # 攻撃者のIPアドレスを確認 $ grep -h "¥./" access_log_yyyymmdd.log | awk '{print $1}' | sort | uniq -c # アクセスログの先頭 $1 には、IPアドレスが書いてある。 # sort 出力をソートする(同じ行を集めるためにソート) # uniq -c 同じ行が繰り返す行数をカウント
OSコマンドインジェクションの確認
脆弱性のあるメール送信ページへの攻撃の確認 # addressを含む行で@を含まないものを検索 $ grep -h address access_log-yyyymm*.log | grep -v @ # grep # -h 複数ファイルの処理で、ファイル名を出力させない # -v 含まない行を出力する。 $ grep -h address access_log-yyyymmdd.log | grep -v @ ¥ | awk '{print $7}' | nkf -w --url-input # nkf 漢字などの文字コードを変換 # -w UTF-8 で出力 # --url-input %20みたいなURLエンコードを復号 この実行結果には以下のようなものがあるかもしれない。 "address=;/bin/echo Permit_RootLogin yes >> /etc/ssh/sshd_config"
SQLインジェクションの確認
- 不正ログイン、情報漏洩、完全性損失、可用性損失 の可能性
SOC演習
大量のログでは、unixコマンドで解析するにしても、コマンド組み合わせを考えるのは大変。企業では、ログ解析専用ソフトを用いて解析を行う。ただし、専用の解析ソフトは高価。今回は、K-SEC事業で一時的な借り物で演習。
MySQLでトラブル対応
仕事で動かしているサーバが動いていないとの連絡が入り、システムが動いていない。
他の方との共同のネタなので、サーバーの更新は慎重に行っているけど、今回は MySQL が落ちているのが原因。サーバーの定期的な更新作業後あたりから動かなくなっているので、最近更新されたパッケージを確認し、MySQL が含まれていたのでパッケージを一つ前のバージョンに落とそうと対応を行った。
しかしダウングレードでパッケージの不整合が出る中、無理やりダウングレードをおこなって MySQL を起動させようとするが、”mysqld got signal 11 ; This could be because you hit a bug.” といったメッセージで起動しない。
四苦八苦するも原因がつかめず、以前の状態に戻そうとバックアップ時に作成しておいたパッケージのバージョン情報を確認すると、パッケージが更新されて動かなくなったのではなく、パッケージが消されていたことが判明。原因を誤解していた。
OSの更新作業の中で、MySQL のメジャー更新で使っていたバージョンが標準パッケージから外されたみたい。メジャー更新すればいいのだろうけど、運用不安もあるので、MySQL の本家で公開しているパッケージを入れて、無事復旧。
長期運用とはいえ、更新は慎重に作業せねば。
福井高専の対外接続の速度
福井高専ではSINETによって接続されているけど、次の更新の際に十分な性能があるかのために、ネットワーク利用状況の確認。
瞬間最大風速で、150Mbps 。ムーアの法則に従うとすれば、2年で2倍と考えると、2022年で約500Mbps, 2025年で1.2Gbps 程度となる。特に今後 学内でBYODが拡大すると、さらなる通信速度が必要と考えられる。
gcj (GNU Compiler for Java) の使い方
いつもは Java を使わないけど、久々に java のプログラムの動作実験と思い、サーバ機に gcj (GNU Compiler for Java)を入れて使おうとしたら、コンパイラ javac とバイトコードインタプリタ java のつもりでコマンドを探すけど、見つからない。
gcj を見つけて “gcj HelloWorld.java” ってやってみたけど動かない。あらためて確認したら、バイトコード生成もできるし、直接機械語も生成できるけど、色々とオプション指定がいるみたい。なるほど。
// HelloWorld.java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } }
# 機械語を直接生成する場合 $ gcj -o HelloWorld --main=HelloWorld HelloWorld.java $ ls HelloWorld HelloWorld.java $ ./HelloWorld Hello World! # バイトコードを生成して動かす場合 $ gcj -C HelloWorld.java $ ls HelloWorld.class HelloWorld.java $ gij HelloWorld Hello World!
緊急連絡システムでrblsmtpd
メモ:緊急連絡システムで、迷惑メールが届いている。変なリレーはされていないけど、エラーメールが配送できずに溜まってる。
設定を改めて確認して、迷惑メール対策が弱いのでブラックリストなメールサーバからの受信をしないように、rblsmtpd を導入。rblsmtpdの設定は、以前の運用経験からすぐに設定できるかと思ったけど qmail が systemd 用に設定ファイルが変わっているので、修正場所を探すのに手間取った。
プロセス状態 I (Idle Kernel Thread)
自宅のサーバの状態を見ていたら、青:sleep 灰:total の間の差が大きい。
変なプロセス走っていないかと心配したら、ps ax の出力でのステータスがIとかI< となっている項目。日本語のマニュアルを見ても意味不明。
英語のマニュアルを見たら、”Idle Kernel Thread”らしい。
Office365のOneDriveの設定
学生の皆さんも BYOD でパソコンを活用しているけど、Windows10 を使っているなら、OneDrive を使うとデスクトップのパソコンと持ち運びのノートパソコンでファイル共有ができて便利。すでに、自分のパソコンで Microsoft ID を登録していれば、すでに標準の OneDrive が使えているはず。
この状態で、高専機構の OneDrive も登録すると、高専の学生さんであれば、1TB/人が使えるので、容量を気にせず使える。設定は以下の通り。
高専機構のOneDriveの追加設定
最初にタスクバーに表示されている、OneDrive のアイコンの上で、右ボタンでメニューを表示し、設定。
アカウントのタブを選び、「アカウントの追加」を選び、
サインインの入力欄に、高専機構のメールアドレスを入力し、サインインを押してパスワード設定をすれば、タスクバーに青色の OneDrive が追加されます。
ただし、このままこの設定では、すべてのファイルが同期されるため、情報処理センターのパソコンを起動すると、大量のファイル転送が発生してログインに時間がかかる。
このため、OneDrive のアカウントタブの、「フォルダの選択」を選び、情報処理センターでは不要のファイルは同期しないように設定をすること。
GASを使ってLINEボットを作る
高専の KOSEN ハッカソン LINE にて、学生さんがいろいろと作品を作る間、自分でも LINE メッセージAPIで遊んでみた。
LINEボットのオーム返しの例
参考にしたサイトのプログラムを若干修正してある。
// line developersに書いてあるChannel Access Token var access_token = "xxxxxx"; // pushしたいときに送る先のuser_id or group_idを指定する var admin = "Uxxxx"; // postされたログを残すスプレッドシートのid var spreadsheet_id = "xxxx"; // スプレッドシートの"user-db"シートにユーザIDと名前の対応表を保存 // 通常のサーバ処理なら、データベースに保存なんだろうけど、 // すべてをGoogle Driveで完結させたいので、Google Spredsheed を // データベースのように使う。 var user_db_size = 10 ; function get_user_name(source) { // スプレッドシートのuser-dbシートに、IDと名前の一覧を記録 // Uxxxxxxxx なまえ var array = SpreadsheetApp.openById(spreadsheet_id) .getSheetByName('user-db') .getRange(1,1,2,user_db_size).getValues() ; for( var i = 0 ; i < array.length ; i++ ) { if ( array[i][0] == "" ) // 無記入欄で終了 return source.userId ; else if ( array[i][0] == source.userId ) return array[i][1] ; // 対応するものを見つけた } return source.userId ; } /** * 指定のuser_idにpushをする */ function push(text,to) { var url = "https://api.line.me/v2/bot/message/push"; var headers = { "Content-Type" : "application/json; charset=UTF-8", 'Authorization': 'Bearer ' + access_token, }; var postData = { "to" : to, "messages" : [ { 'type':'text', 'text':text, } ] }; var options = { "method" : "post", "headers" : headers, "payload" : JSON.stringify(postData) }; return UrlFetchApp.fetch(url, options); } /** * 管理者宛にメッセージを送る */ function push_admin(text){ return push(text,admin); } /** * reply_tokenを使ってreplyする */ function reply(event,text) { var url = "https://api.line.me/v2/bot/message/reply"; var headers = { "Content-Type" : "application/json; charset=UTF-8", 'Authorization': 'Bearer ' + access_token, }; var postData = { "replyToken" : event.replyToken, "messages" : [ { 'type':'text', 'text':text , } ] }; var options = { "method" : "post", "headers" : headers, "payload" : JSON.stringify(postData) }; return UrlFetchApp.fetch(url, options); } function do_reply(event) { // リプライの処理 var text = '' ; switch( event.message.type ) { case 'text' : // メッセージ text = event.message.text ; break ; case 'sticker' : // ステッカー text = 'すてっかーID=' + event.message.id ; break ; case 'image' : // 画像 default : text = 'event.message.type=' + event.message.type ; break ; } return reply(event,'りぷらい:'+text); } /** * push api で beacon 受信時の処理 */ function do_beacon(event) { var text = '' ; var user = get_user_name( event.source ); var bcn = event.beacon ; switch( bcn.type ) { case 'enter' : // ビーコン領域に入った text += 'ENTER' + bcn.hwid ; break ; case 'leave' : // ビーコン領域を抜けた(実験したけど通知は実質来ない!?) case 'banner' : // ビーコンバナーをタップした default : text += bcn.type + '=' + bcn.hwid ; break ; } return push_admin( 'びーこん=' + text + '(' + user + ')' , admin ) ; } /** * postされたときの処理 */ function doPost(e) { var json = JSON.parse(e.postData.contents); // 利用者のuseridを取得するためのlog var data = SpreadsheetApp.openById(spreadsheet_id) .getSheetByName('log') .getRange(1, 1).setValue(json.events); // 1回のPostで、メッセージと画像が複数のイベントで来る場合がある for( var i = 0 ; i < json.events.length ; i++ ) { var event = json.events[i] ; switch( event.type ) { case 'message' : // Message API の処理 do_reply(event); break ; case 'beacon' : // Beacon API の処理 do_beacon(event); break ; default: // デバッグのため push_admin( 'それ以外=' + event.type ) ; break ; } } } /** * pushをしてみる */ function test() { push_admin('test()の実行'); } /** * getされたときの処理 * URL?message=テキスト */ function doGet(e) { // var data = SpreadsheetApp.openById(spreadsheet_id) // .getSheetByName('log') // .getRange(2, 1).setValue(e.parameter.message); // https://script.google.com/macros/s/..../exec?message=メッセージ if ( "message" in e.parameter ) push_admin(e.parameter.message); }