30代半ばの事務職員がITエンジニアになった話

おおよそタイトルのとおり、30代半ばにしてITエンジニアのタマゴになってしまった会社員の話

【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



サーバーって基本遠隔操作で
作業することが多いと認識している
今回もご多分にもれずそのケース


なので再起動すると
いったんセッションが切れる


そしてしばらくして
再度つなぎにいこうとすると




…待てども待てどもつながらない



UUIDの記載ミス

原因はfstabに記載した
UUIDが増設したディスクではなく
既存のディスクのものを誤って
記載していたことで


OSが立ち上がる前にエラー、
BIOS(?)の状態で止まっていたため
つなぎに行けなかった
というもの



結局、サーバーを直接操作して
fstabを修正、再起動して
事なきを得たものの



遠隔で操作している以上
SSHがつながらないと
どうにもならないのは自明の理で


いちばん出くわしたくないパターン
な気がする

【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サーバーにはApachetomcat
 搭載されている


     ↓


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のコマンドを変数に入れる
WindowsLinuxでコマンドが微妙に違う

// 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~

成り行きでOracleMySQL
移行してみたときの話の続き


TO_CHAR関数⇒DATE_FORMAT関数


Oracleで使われているTO_CHAR関数
MySQLには存在しないので
DATE_FORMAT関数をつかう


例えば日付を8ケタで格納したい場合

20200205


Oracle

SELECT TO_CHAR(SYSDATE, 'YYYYMMDD') FROM DUAL;


MySQL

select DATE_FORMAT(now(),'%Y%m%d');




ビューなんかで使われているSQL
書き換えるだけの対応で済むので
他のややこしいのに比べるとわかりやすい




SQLの外部結合の書き方


Oracleでは↓の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でも同じものが使えるよう
通化されているといいなぁと
思ったり思わなかったり