メールサーバの移行

イメージ
 自前のメールサーバを停止 これまで、自ドメインのメールサーバはVPS上に構築していた。 ・Ubuntu Server ・Postfix ・Dovecot ・spamAssassinと、BlackListの利用 Spam対策を行ってきたし、サーバ上でメールをトリガーにして各種プログラムを動かしたりしてきた。 メールサーバのメンテナンスは結構面倒くさくて、  ・Disk容量のチェック  ・不正アクセスのチェック  ・各種セキュリティパッチの適用 など、手間がかかる。 そこで、外部のサーバを利用することに…。 結構安くて使い勝手の良さそうなのが、「さくらのメールボックス」 3年契約で、3070円とな…。  メールアドレスは自分のだけなので、20GBまで利用可能!  (Gmailより大きいねぇ) ということで、早速契約。 アカウント設定を行って、既存のDNSを書き換える。WHOISも書き換えて完了。 SMTPとIMAPが利用できればOK。 ちょうど、GoogleがSPF設定していないと受信しないし、DKIMおよびDMARCに対応していないメールを弾くようになったので、対応しているのを確認。 さくらサーバ自体は、これまでお客さんのサーバとして何件も利用しているので、利用方法も難しくはない。  Webメールにも対応しているので、いざという場合にもありがたい。 ということで、各メールソフトの設定を変更。  PC(常時使用する3台)とタブレット、スマートフォンと台数は多いがそれ程手間はかからない。 問題は、旧サーバで送受信したメールの履歴だけれど、これはThunderbrdを使ってローカルに保存することで回避。  本当は、サーバtoサーバでMailboxに残そうとも考えたんだけど、古いメールはそれ程必要ないし、ローカルにバックアップしてあれば凌げるので、良しとする。 移行時にDNSの反映で若干時間がかかったものの、問題なく送受信できるのを確認して、作業完了。  これでメンテナンスの手間が減るので、安いもんです。

ログの整理 #3

PowerShellでは遅すぎて…

実際問題、ログからcsvに切り出すことは可能になったものの、10MBのログから200kb前後のcsvファイルを生成するのに前回のスクリプトだと約2時間かかるんですねぇ。
そりゃ、1レコードを生成するのに、ログオンIDで再度全部ログを舐めて、PCの名前とIPを拾っているわけで…。
単純に考えればO(n^2)というわけですね。
ログのローテーションというか上限を5MB(半分)にすれば、多分処理速度は1/4程度にまで下がるとは思われるけれど、120分が30分になって、ファイル数が倍なので実質1時間かかるわけで…。orz。

何が遅い?

実際、何に時間がかかっているのかを調べ始めたのだけれど、まずwhere-Objectが結構な時間を消費している。その後foreachで回す処理は激遅い。
結局、イベントログからオブジェクトとして扱っているのが、遅さの原因ぽい。

最適化しようにも、PowerShell自体の扱いに慣れていないので、とっても疲れるです。

他に良い方法は無いものだろうか?と思案し始めた時に、以前オライリーの本で見た記憶があったので、ちょいっと検索してみる。

方針変更!

perlのモジュールにW32::EventLogが有るじゃないですか。
早速、マニュアルを参考にテスト用のコードを書いてみる。
さすがに、サーバ上での作業は心配なので、VM上のWindows7にActivePerlを入れてテストすることに。

細かい仕様を決めようか、汎用的に作ろうかと迷いつつも、現状の作業をサックリ行えることを目的に作成した。約1日かけて、コーディング終了。前回までのスクリプトと同じ結果を返すようにするところで、少々苦労したわ。

なので、若干コードは汚い(perlなのでさらに汚いww)とは思うのだけれど、これが素晴らしい性能を発揮してくれた。

コードは以下全体を載せておくので、もし万が一使いたい人は、自己責任にてお願いします。

さて、実際に同じデータを食わせてみると、なっなんと、2時間かかっていた処理を1分以内に終えてくれる!!!

あぁ、これで今後のログの整理が快適になる。
1週間分、いや1ヶ月分まとめて処理だって怖くない!
あとは、コマンドラインでイベントログを引数として渡せば良い仕様にしたので、バッチでも動かせるし…。

もう少し、バグ取りなんかも必要かもしれないし、エラー時の処理なんかは、全くしていないレベルなので、暇な時にでもやろうとは思っている。

この手間は、大きな差を産んでくれる! 120倍速の処理完了〜。


use Win32::EventLog;
$Win32::EventLog::GetMessageText = 1;
$DEBUG=0;
sub make_time {
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @_;
  return sprintf("%04d/%02d/%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec);
}
sub event_hash4624{
my($strings)=@_;
my @event=('SubjectUserSid','SubjectUserName'    ,'SubjectDomainName'      ,'SubjectLogonId',
  'TargetUserSid' ,'TargetUserName'     ,'TargetDomainName'         , 'TargetLogonId',
  'LogonType'     ,'LogonProcessName'   ,'AuthenticationPackageName','WorkstationName',
  'LogonGuid'     ,'TransmittedServices','LmPackageName'            ,'KeyLength',
  'ProcessId'     ,'ProcessName'        ,'IpAddress'                ,'IpPort');
my @list = split(/[\0]/, $strings);
my %h;
for(my $idx=0;$idx<$#event;$idx++){
$h{$event[$idx]}=$list[$idx];
}
return %h;
}
sub event_hash4663{
my($strings)=@_;
my @event=('SubjectUserSid','SubjectUserName','SubjectDomainName' ,'SubjectLogonId',
  'ObjectServer'  ,'ObjectType'     ,'ObjectName'        ,'HandleId',
  'AccessList'    ,'AccessMask'     ,'ProcessId'         ,'ProcessName');
my @list = split(/[\0]/, $strings);
my %h;
for(my $idx=0;$idx<$#event;$idx++){
$h{$event[$idx]}=$list[$idx];
}
return %h;
}
sub convert_acl{
my ($access_list)=@_;
my %acl = ('%%1537'=>"Delete"                 ,'%%1538'=>"Read_CONTROL"  ,'%%1539'=>"Write_DAC",
  '%%1540'=>"Write_OWNER"            ,'%%1541'=>"Synchronize"   ,'%%4416'=>"ReadData (or List Directory)",
  '%%4417'=>"WriteData (or Add File)",'%%4418'=>"AppendData (or AddSubdirectory or CreatePipeInstance)",
  '%%4419'=>"ReadEA"                 ,'%%4420'=>"WriteEA"       ,'%%4421'=>"ExecuteFile",
  '%%4422'=>"DeleteChild"            ,'%%4423'=>"ReadAttributes",'%%4424'=>"WriteAttributes");
$access_list =~ s/^\s*(.*?)\s*$/$1/;
if($acl{$access_list}){
return $acl{$access_list};
}else{
return "Other";
}
}
sub make_db_id {
my ($eventlogname,$event_id) = @_;
  my $handle = Win32::EventLog->new($eventlogname) or die "can't open event log.\n";
  $handle->GetNumber($recs) or die "Can't get number of EventLog records\n";
  $handle->GetOldest($base) or die "Can't get number of oldest EventLog record\n";

  my $x=0;
  while ($x < $recs) {
    $handle->Read(EVENTLOG_FORWARDS_READ|EVENTLOG_SEEK_READ,
                  $base+$x,
                  $hashRef) or die "Can't read EventLog entry #$x\n";
    if($hashRef->{EventID} eq $event_id){
%eventdata = event_hash4624($hashRef->{Strings});
$workpc    = $eventdata{'WorkstationName'}?$eventdata{'WorkstationName'}:"";
$logonname = $eventdata{'TargetUserName'}?$eventdata{'TargetUserName'}:"";
    $logonid   = $eventdata{'TargetLogonId'};
$logonip   = $eventdata{'IpAddress'}?$eventdata{'IpAddress'}:"";
      if(!($logonname =~ /\$/)){
        $db_id{$logonid}=$workpc . "," . $logonip;
}
    }
    $x++;
  }
return %db_id;
}
my $archivelogname = $ARGV[0];
my %id_list = make_db_id($archivelogname,"4624");
my $csvfilename;
$csvfilename = $archivelogname;
$csvfilename =~ s/\.evtx$/\.csv/;
$csvfilename =~ s/^.+\\//;
open(FH,">$csvfilename") or die("Can't open csv file\n");
print FH "日時,ユーザ名,ドメイン名,ログオンID,コンピュータ名,IPアドレス,ファイル名,操作,アプリケーション";
    my $event_id="4663";
  my $handle = Win32::EventLog->new($archivelogname) or die "can't open event log.\n";
  $handle->GetNumber($recs) or die "Can't get number of EventLog records\n";
  $handle->GetOldest($base) or die "Can't get number of oldest EventLog record\n";

  my $x=0;
  while ($x < $recs) {
    $handle->Read(EVENTLOG_FORWARDS_READ|EVENTLOG_SEEK_READ,
                  $base+$x,
                  $hashRef) or die "Can't read EventLog entry #$x\n";
    if($hashRef->{EventID} eq $event_id){
%eventdata = event_hash4663($hashRef->{Strings});
            $csv = make_time(localtime($hashRef->{'TimeGenerated'})) . ",";
            $csv .= $eventdata{'SubjectUserName'} . ",";
            $csv .= $eventdata{'SubjectDomainName'} . ",";
            $csv .= $eventdata{'SubjectLogonId'} . ",";
if($id_list{$eventdata{'SubjectLogonId'}}){
$csv .= $id_list{$eventdata{'SubjectLogonId'}} . ",";
}else{
$csv .= ",,";
}
            $csv .= $eventdata{'ObjectName'} . ",";
            $csv .= &convert_acl($eventdata{'AccessList'}) . ",";
            $csv .= $eventdata{'ProcessName'};
            print FH $csv."\n";
    }
    $x++;
  }
close(FH);

コメント

このブログの人気の投稿

WPS Officeで日本語入力ができない

VirtualBoxでUSBから起動

Virtualboxとnvidiaドライバ