【SSO】ShibbolethでSAML認証シングルサインオンの設定にチャレンジしてみた
あれば便利だけど
どうしてもないと困るわけでもない
むしろ初期から導入されていた場合、
存在すら気づかれないかもしれない
その割には設定がえらく煩雑
だったりする
そんなシングルサインオンを
設定したときの話
シングルサインオンとは
今回に限らずこの手のイタズラ試行は
細かい学習をすっ飛ばして
実際のアプリケーションの
Configファイルをいじったりと
手を動かすことから入る傾向がある
そのほうが覚えるのが早いこともあるけど
最低限のポイントは事前にわかっていると
より効率的に理解できる気がする
SSOの本当に基礎的な構成
IdP - SSOで認証するためのユーザーIDやらの情報を送る側 ユーザーIDとパスワードを入力する
SP - SSOで認証するためのユーザーIDやらの情報を受ける側 IdPで入力されたユーザーIDでログインする
最低限このくらいの情報が
事前にわかっていれば
だいぶ違った気がすると今さらながら…
Shibboleth
細かいことはよくわからないけど
既存の仕組みで使われていた
アプリケーションを利用
オープンソースの代物で
ケチって有償のものを買わなかった様子
IdP(送り側)にもSP(受け側)にも
なれるので当初違いがわからず戸惑う
今どきのSSOのサービスは
Web上で提供されてたりして
操作画面もGUIでわかりやすく
なってたりするけど
こいつはConfigファイルや
XMLファイルをつらつら
書いていく必要がある
SSOを実現したいサービスを追加する
IdPとしてShibbolethを使っている場合に
ログイン情報を新たなSP側に送るための設定
具体的にはShibbolethを使った
独自の認証システムで
例えばGoogleなどに認証情報を送って
自動ログインさせたいといった
シチュエーション
認証情報を共有できるのはSAMLという規格が
決まっているからなのだけど
この辺の小難しい話は省略
まずメタデータを登録する
/どこかのフォルダ/shibboleth-idp/metadata/
※Linux環境
配下に受け側のサービスから提供された
xml形式のメタデータを格納する
メタデータが提供されていればいちばん
手っ取り早いのだけど、ない場合も多い
その場合、ログイン先のURLが提供
されていたりするのでその情報を登録する
Shibbolethの場合は親切機能はないので
メタデータを最低限のかたちで自分で作って
上記のフォルダに格納することになる
こんなふうに最低限の情報を入れて
任意の名前でxml形式で保存すればいいみたい
<?xml version="1.0"? encoding="UTF-8"?> <EntityDescriptor entityID="受け側のサービスから提供されるエンティティID" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"> <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat> <AssertionConsumerService index="1" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="受け側のサービスから提供されるログインURL"/> </SPSSODescriptor> </EntityDescriptor>
まだまだ先は長いので次回に続く
【Linux】ハードディスクを増設したら起動できなくなった話
Linuxはディスクの増設も
一筋縄ではいかないらしい
手順を間違えたら
起動しなくなって
大変だったときの話
ドライブのマウント
WindowsでHDDを増設すると
Dドライブやらが勝手に増えて
分かりやすいけどLinuxでは
このへんのことは手動で
やらないといけない
Linuxの仕組みだと
フォルダごとに
どのドライブを使うのか
設定することになっていて
これをマウントと呼ぶらしい
(正確にはちょっと違うっぽい
けどドライブを増設したら
この設定が必要)
まずは増設されたディスクの確認
fdisk -l
で一覧が表示されるので
追加されたディスクを
/folderにマウント
mount -t ext3 /dev/xxxxx /folder
指定しているらしい
これでいったん追加したディスクが
使えるようになる
再起動後も有効にするために
が、mountで効果があるのは
OSが起動している間だけのようで
再起動したら再度同様の設定が
必要になる様子
あらかじめこの設定を記録しておき
起動時に読み込むようにすることで
毎回設定しなくても済むようにするには
fstabにこの情報を記載する必要がある
fstabにはfdiskで表示された情報でなく
各ディスク?にはUUIDという番号が振られて
おり、それを指定する必要があるらしい
以下のコマンドでUUIDを表示
blkid
ここででてきたUUIDを /etc/fstab に記載
UUID=yyyyyyyyyyyyyyyyyyyy /folder ext3 defaults 1 2
再起動
上記の設定が首尾よくされているか
確認するには再起動が必要なわけで
いったんreboot
サーバーって基本遠隔操作で
作業することが多いと認識している
今回もご多分にもれずそのケース
なので再起動すると
いったんセッションが切れる
そしてしばらくして
再度つなぎにいこうとすると
…待てども待てどもつながらない
【Webサーバー(Apache)】【Linux】ログが肥大化してえらいことになったのでローテーションを見直す
事の発端はとあるLinuxサーバーのフォルダが
容量ピンチ、とアラートがとんできたこと
調べたらログファイルがマックス近くまで
ディスク容量を圧迫していたことが判明
よくよく考えたらデータを発生させる
ような処理は基本的にはないWebサーバー
なんだから容量増えるのはログが
最有力じゃんという話
ログの場所と内容
容量がいっぱいになったのは
/var
だいたいログは
/var/log
にいることが多い
今回は
/var/log/httpd
のフォルダのログがやたら大きな
容量を誇っていた
ということで
Apacheのログが原因
accsess.logが肥大化していた
ということはアクセスが
多くなるほどこの事象が起きる
可能性が高くなるということか
となると、けっこうよくあるパターン
なのではなかろうか
見てみるとログは1週間1ファイルで
5週間分保管されており
1ファイルあたりの容量は2GB
フォルダの容量が12GBだから
そりゃマックス値近くまでいく計算
ログローテーションの設定
古いログを消すとか圧縮するとか
移動するとかスクリプトを組めば
できるんだろうけど、と思っていたら
ログローテーションの専用の
設定がちゃんとあるらしい
/etc/logrotate.d/httpd
基本的にlogrotate.d配下のファイル名が
サービス名と対応しているみたい
中身:
/var/log/httpd/*.log{ missingok //ログファイルが存在しない場合にエラーを出力しない notifempty //ログファイルが空の場合ローテーションしない sharedscripts //ログファイルを複数指定した場合、それぞれpostrotate、prerotate内のコマンドを実行 delaycompress //1世代目は圧縮されない設定、だがcompressと組み合わせないとうまく動かないらしい postrotate //endscriptとの間に書いたコマンドをローテーション後に実行 /bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true endscript }
1週間ごとのローテーションを
指定しているわけではない
ので、上位設定の
/etc/logrotate.conf
を見ているんだと思うけど
細かいことは忘れてしまった
以下のように必要事項を追記
/var/log/httpd/*.log{ daily //毎日ローテーション rotate 31 //31日分 compress //圧縮する missingok notifempty sharedscripts delaycompress postrotate /bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true endscript }
週ごとではなく毎日ログファイルを
作成して圧縮することで
容量不足を解消
導入時はこんなにアクセスが
増えるとは想像していなかったのだろうなぁ
【Oracle】DBの処理が遅延する問題について
Oracleで先月までは順調に動いていた
プロシージャが急に動作が遅くなった
ときの話
トラブル対応がいちばんの技術向上
とちょくちょく聞き、その通りだと思うけど
やっぱり自分で手を動かしてこそのこと
だとも思うのであって
今回直接トラブル対応したのは委託先
逐一報告を受けていたものの
内容を把握しきれたわけではなかったりして
(まだまだ知らないことがたくさんあるなぁ…)
たぶん、ほっとくと
このまま記憶が流されてしまいそうなので
覚えた専門用語をを書き留めておこう
統計情報と実行計画と共有プール
あまりわかってないが故に
かいつまんで言うと
Oracleデータベースは
SQLを流したときに
いちばん早く処理できる
方法を自分で考えてくれるらしい
「統計情報」に早く処理できる方法を
探しに行って、考えたパターンが「実行計画」
ここで見つけたこれは早いぜ、っていう
「実行計画」のパターンをいくつか
ストックしておいて
似たSQLがきたときに早く処理できるように
しておく、このストックを
「共有プール」と呼んでいるらしい
statspackのこと
実行計画の履歴を取っておくことができる機能
過去の実行計画を復旧したいときに必要
でもデフォルトで設定されているわけではなく
今回のような問題が起きたときに
あわてて設定しても時すでに遅し
導入時など先人たちが残してくれていればラッキー
なければ終了、、ということ
テーブルロックについて
読んで字のごとくと思うけど
いまいちイメージがわかない
で調べると
CommitやRollbackをするまでは
テーブルがロック状態になるらしい
つまりアプリ側で
何かを入力する画面を開く
⇒テーブルロックを意図的にかける
入力画面で保存ボタンを押す
⇒Commitでロック解除
ということなのだろう
月末の締め処理などの際に
ロックをかけて編集できないように
することもあるとのこと
今回の処理遅延はまさしく
月末処理だったので
テーブルロックの解除を試行
結果、効果なし
あとからよく考えたらそりゃそうだ
初期化パラメータ
これも文字通りOracleを起動すると
いちばん最初に読み込まれるもの
メモリをどれくらいまで使っていいとか
キャッシュの制限値とかを指定できる
のでこの辺の数値を最大まで許可するようにして
改善を試みる、がこれも効果なし
DB再起動
統計情報の再取得、共有プールの実行計画削除
などなど考えられる手段を試したのち
最後の手段、再起動
これでキャッシュ系に起因する問題なら
ほとんど解決するようだけど
残念ながら効果なし…
これでダメだとなかなかしんどい
結局
遅延している原因を追究して
とあるViewの読み込みに時間が
かかっていることが判明
なのでViewの不要な項目を除外することで
処理の改善が図れて結果的にはOKだけど
直接原因に対する対策ではないので
なにか釈然としない
実は前回の同様の処理に比べて処理元の
データが極端に少なくなっている部分があり、
データを抽出するときって
1件でも検索結果があるときに比べて
検索結果が0件のときにえらく時間がかかることが
あるような気がする
ので、プロシージャのなかで
ループで検索結果0件の抽出を
繰り返していたりしたら
積もり積もって全体の処理が遅くなるのでは
とにらんでいるのだが
真相はたぶんうやむやになって消えていくのだろう
【Linux】シェルスクリプトを書いてみる 死活監視の延長でPingが死んでたらサービスを止めてみよう
シェルスクリプトを覚えたい、と
常々思ってはいたものの、
これをこうしたい!という具体的な
目的がないとなかなかすすまない
というのはよくあるパターンと思う
そこで、今回のお題
シェルを組んでサービスを自動的に止めたい
みたいな目標があると
覚えるいい機会になるなと
Pingが死んでたらサービスを強制的に止めるシェルスクリプト
どいういうことかと言うと
前回のブログに書いた通り
とあるサーバーが別のサーバーに
データを常に取りにいってて
通信断にでもなったら
エラーが蓄積されて最後は
ハングアウトするという
わけのわからない仕様
なので通信断となったら
通信しているサービスを止めよう
という発想
まぁだいぶレアケースだろうけど
こういうのをシェルスクリプトで
対処しようと創作意欲がわく
、ということで
コード
#!/bin/sh status1=`curl -I http://localhost/ping | grep HTTP` status2=`echo $status1 | grep HTTP` if [ "`echo $status2 | grep 200`" ]; then result1=1; else result1=0; fi echo $status2 sleep 3s status1=`curl -I http://localhost/ping | grep HTTP` status2=`echo $status1 | grep HTTP` if [ "`echo $status2 | grep 200`" ]; then result2=1; else result2=0; fi echo $status2 sleep 3s status1=`curl -I http://localhost/ping | grep HTTP` status2=`echo $status1 | grep HTTP` if [ "`echo $status2 | grep 200`" ]; then result3=1; else result3=0; fi echo $status2 active=`ps -ef | grep ○○○ | grep -v grep | wc -l` echo "ping1=" $result1 echo "ping2=" $result2 echo "ping3=" $result3 echo "activate=" $active if [ $result1 -eq 1 ] && [ $result2 -eq 1 ] && [ $result3 -eq 1 ] && [ $active -eq 0 ]; then sudo service ○○○ start && echo tomcat started; fi if [ $result1 -eq 0 ] && [ $result2 -eq 0 ] && [ $result3 -eq 0 ] && [ $active -eq 1 ]; then sudo service ○○○ stop && echo tomcat stoped; fi
解説
1行目に書く決まりごと
shとかbashとか他にもいろいろあるらしいけど
実はよくわかってない
#!/bin/sh
通信断となっているかをPingで確認
せっかくなので前回作成した
Javaのプログラムをデプロイして
…/ping/ でアクセスできるようにし
curl の結果を返すようにしている
curl の結果のHTTPの文字が入っている行を
抜きだしてるわけだけど
ふつうにPingコマンドにすることも可
JavaのプログラムでPingの対象のIPを
別ファイルにしておりIPの変更があった場合
そちらを変更するだけでいいように
このような仕様にしている次第
status1=`curl -I http://localhost/ping/ | grep HTTP`
curl の結果を抜き出したHTTPの行には
httpステータスのコードが入っている
200(通信OK)なら結果フラグを1にする
status2=`echo $status1 | grep HTTP` if [ "`echo $status2 | grep 200`" ]; then result1=1; else result1=0; fi echo $status2
Pingを3回打って結果をトリガーにする
仕様にしてみた
だけど立て続けにするとよくないようで
少し間を空けるとうまくいった
sleep 3s
ps -ef で実行中のプロセスを表示
grep で該当のサービスを抜き出す
そのままだと"grep"自身も
プロセスのひとつとして表示されて
しまうので grep -v grep で
"grep"を除外する
wc -l で結果の行の数を出力
⇒こうすると変数"active"に
該当のサービスが起動していなければ0
起動していれば1以上が入るようになる
active=`ps -ef | grep ○○○ | grep -v grep | wc -l`
Pingの結果が3回ともOKかつ
サービスが起動していない状態なら
サービスを起動する
if [ $result1 -eq 1 ] && [ $result2 -eq 1 ] && [ $result3 -eq 1 ] && [ $active -eq 0 ]; then sudo service ○○○ start && echo tomcat started; fi
Pingの結果が3回ともNGかつ
サービスが起動していれば
サービスを停止する
if [ $result1 -eq 0 ] && [ $result2 -eq 0 ] && [ $result3 -eq 0 ] && [ $active -eq 1 ]; then sudo service ○○○ stop && echo tomcat stoped; fi
【Java】JavaからPingを打ってNGならエラーページを返すプログラム(死活監視の一環)
背景
・AサーバーとBサーバーが存在
・AサーバーはBサーバーへの通信が
できないと徐々にエラーを蓄積し
最後にはハングアウトする問題を
抱えている
・なのでAサーバーからBサーバーへ通信
できなくなった時点で検知したい
・監視サーバーが存在
・監視サーバー⇒Aサーバー
監視サーバー⇒Bサーバー
の通信は監視できるものの
Aサーバー⇒Bサーバーへの
監視はできない
・監視サーバーにはWebページ監視の
機能があり指定されたWebページに
アクセスして200(正常)以外のコード
が返ってきたらアラートを出す
・AサーバーにはApacheとtomcatが
搭載されている
↓
Aサーバーに、BサーバーへPingを打って
NGならエラーページを返すプログラムを
Javaで作成してWebページとして置くことで
既存の監視をそのまま活用できるようにした
プログラム
単純なプログラムなので
Javaのコードとしてはだいぶすっきりしている
と思われる
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Ping extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { boolean success = false; String buffer; String result = null; String TIMEOUT = "255"; FileInputStream FileInputStream = new FileInputStream("/webapps/Ping/Host_IP.txt"); BufferedReader BufferReader1 = new BufferedReader(new InputStreamReader(FileInputStream)); String target = BufferReader1.readLine(); String[] command = {"ping", "-c", "1", "-t", TIMEOUT, target}; InputStream InputStream = new ProcessBuilder(command).start().getInputStream(); BufferedReader BufferReader2 = new BufferedReader(new InputStreamReader(InputStream)); int i = 0; while((buffer = BufferReader2.readLine()) != null) { if(i == 1) { if(!buffer.contains("Unreachable")) { success = true; } result = buffer; } i = i + 1; } PrintWriter out = response.getWriter(); if(success) { out.println("<html><head><title>OK</title></head><body>" + result + "</body></html>"); } else { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } out.close(); }
自然言語に翻訳
Webページを表示させるので
当然ながらサーブレットクラスを継承
public class Ping extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
変数部分の宣言
boolean success = false; //PingがOKのときにtrueにする String buffer; //Pingの結果を格納する文字列 String result = null; //Pingの結果をWebページに表示するための文字列 String TIMEOUT = "255"; //ping のタイムアウト(ms)
Pingの対象となるIPはプログラムを修正しなくても
IPの変更ができるよう外部ファイルに記載
FileInputStream FileInputStream = new FileInputStream("/webapps/Ping/Host_IP.txt"); //外部ファイルの指定 BufferedReader BufferReader1 = new BufferedReader(new InputStreamReader(FileInputStream)); //ファイルを読み込む String target = BufferReader1.readLine(); //ファイル内の文を読み取る
Pingのコマンドを変数に入れる
WindowsとLinuxでコマンドが微妙に違う
// Windows の場合 String[] command = {"ping", "-n", "1", "-w", TIMEOUT, target}; //-n 1 でPingを1回だけ -w xxx でタイムアウトを指定
//Linux の場合 String[] command = {"ping", "-c", "1", "-t", TIMEOUT, target}; //-c 1 でPingを1回だけ -t xxx でタイムアウトを指定
変数に入れたPingコマンドを実行する
InputStream InputStream = new ProcessBuilder(command).start().getInputStream(); //コマンドを実行して変数に入れる BufferedReader BufferReader2 = new BufferedReader(new InputStreamReader(InputStream)); //コマンドの実行結果を変数に入れる
Pingの結果を変数に格納する
while((buffer = BufferReader2.readLine()) != null) { //Pingの結果を行ごとに見ていく if(i == 1) { //2行目で if(!buffer.contains("Unreachable")) { //接続できません的な文字がなければ success = true; //フラグをON } result = buffer; //結果表示用変数にPingの結果を格納 } i = i + 1; }
PingがOKなら通常ページを表示
PingがNGならエラーページを表示
PrintWriter out = response.getWriter(); //画面出力用 if(success) { //フラグがONなら out.println("<html><head><title>OK</title></head><body>" + result + "</body></html>"); //申し訳程度にPing結果を表示するページ } else { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); //OFFなら500エラーを返すページ } out.close(); //クローズが必要らしい
【MySQL】DBをOracleからMySQLへ移行する件について ~その3~
成り行きでOracleをMySQLに
移行してみたときの話の続き
TO_CHAR関数⇒DATE_FORMAT関数
Oracleで使われているTO_CHAR関数
MySQLには存在しないので
DATE_FORMAT関数をつかう
例えば日付を8ケタで格納したい場合
20200205
SELECT TO_CHAR(SYSDATE, 'YYYYMMDD') FROM DUAL;
select DATE_FORMAT(now(),'%Y%m%d');
ビューなんかで使われているSQLを
書き換えるだけの対応で済むので
他のややこしいのに比べるとわかりやすい
SQLの外部結合の書き方
SELECT A.NO, A.NAME, B.NAME FROM TABLE1 A, TABLE2 B WHERE A.NO = B.NO (+)
↓と同じ意味
SELECT A.NO, A.NAME, B.NAME FROM TABLE1 A LEFT OUTER JOIN TABLE2 B ON A.NO = B.NO
上の(+)を使った分は
MySQLで使えないので
これが使われていた場合には
下の分に書き換える必要がある
なんとなく今は主流じゃない
古くさい書き方なのかなぁ、と
感じていたけど
LEFT JOIN等の書き方は
Oracle9iからサポートされたらしい
納得
DECODE関数のエンコード
またもやOracleでは使えるけど
MySQLでは使えない関数 DECODE
DECODE( COLUMN, '1', 'A', 'B' )
CLUMNの値が1ならA、それ以外ならCを返す
という意味
幸いにもこのケースでは
単純に条件分岐が1つだけなので
MySQLでは
if(column=1,'a','b')
と、シンプルに再現できる
条件分岐が複数だと
CASE文で対応するのが妥当だろうか
それぞれのDBには
それぞれの生い立ちがあるだろうから
使用できる関数等がちがうのも
理解はできるけど
どのDBでも同じものが使えるよう
共通化されているといいなぁと
思ったり思わなかったり