2018年12月19日 星期三

壓力測試 siege

環境: AWS的centos

安裝檔: http://download.joedog.org/siege/

先下載
wget http://download.joedog.org/siege/siege-4.0.4.tar.gz
tar -zvxf siege-4.0.4.tar.gz

安裝
sudo ./configure
sudo make
sudo make install


使用siege -h查看可用參數

壓測指令範例
siege -c 100 -t 1  -f list.txt -i -b > siege.log
c: 使用者數
t: 執行時間,單位分鐘
f: 要攻擊的網址清單
i: 隨機取網址
b: 不延遲

執行完畢後會把結果顯示出來,這就可以拿來做報告的數據。
Transactions: 30 hits ## 完成的要求數 Availability: 100.00 % ## 可用,成功率100% Elapsed time: 10.67 secs ## 運作時間 Data transferred: 3.07 MB ## 數據傳輸量 Response time: 0.50 secs ## 回應時間 Transaction rate: 6.42 trans/sec ## 每秒完成幾個請求 Throughput: 0.01 MB/sec ## 吞吐量 Concurrency: 3.21 ## 最高連接數 Successful transactions: 30 ## 成功完成次數 Failed transactions: 0 ## 失敗次數 Longest transaction: 1.25 ## 每次傳輸最常時間 Shortest transaction: 0.55 ## 每次傳輸最短時間

2018年10月9日 星期二

微信支付 by APP 備忘

首次接內地的金流,恩恩...看來跟呆灣差不多? 總之這個案例又有個整合的服務,WEB請求APP支付所以於是這般有了這篇...

參考文件
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

關係圖


幾個陷阱註記一下:
1. 開通前要先接sandbox這裡面就存在呼叫XML裡面的key得改用sandbox簽署的而不是跟微信申請下來的"支付API秘钥"。
參考文件: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1

2. sign簽名檔的生成得將"所有要傳送的資料"都帶入計算,依照文件說明的做會得到簽名錯誤的回覆。
參考文件:  https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3

3. micropay是信用卡付費的,接APP付費得帶unifiedorder,prepay_id在micropay是沒有回傳的。
https://api.mch.weixin.qq.com/sandboxnew/pay/micropay
https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder

4. 呼叫時SSL關掉會比較單純(不然會有憑證上的錯誤回傳),微信會給證書檔記得匯入到WEB SERVER環境內。
$url = $weixin_url;
$header[] = "Content-type: text/xml"; 
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlData);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);

5. 交換格式是使用XML,所以接收時這邊的接法是...
$post_data = file_get_contents("php://input");
libxml_disable_entity_loader(true);
$msg = json_encode(simplexml_load_string($post_data , 'SimpleXMLElement', LIBXML_NOCDATA));
$rtn_data = json_decode($msg, true);

6. 驗證回傳值(key需先進行排序,以免因順序不同而造成sign不一致)真偽後,告知微信已收到。
ksort($rtn_data);
$buff ='';
foreach($rtn_data as $k=>$v){
  if ($k != 'sign'){
$buff .= $k.'='.$v . '&';
  }
}
//若是沙箱模式則$weixin_key須重新透過getsignkey來取得,不然SIGN算出來還是錯的。
$stringSignTemp = $buff . 'key='. $weixin_key;
$sign=strtoupper(md5($stringSignTemp));
if ($sign == $rtn_data['sign']){
  $err_code = '00';
  echo '<xml>
              <return_code><![CDATA[SUCCESS]]></return_code>
              <return_msg><![CDATA[OK]]></return_msg>
          </xml>';
}

7. APP要導頁進行付費,透過JS進行FUNC呼叫,這部分若是要使用 $(document).ready(function(){});進行呼叫時,務必Include jquery-XXX.js 不然不會動作。

echo '<script type="text/javascript" language="javascript">
window.onload = function() {
window.weixin.startWechatPay('".$prepay_id."','".$nonce_str."','".$saletime."','".$sign."')";
    }
  </script>';


2018年9月18日 星期二

Codeigniter使用OracleDB 備忘

1. 下載 Oracle Instant Client,建議下載32位元的貌似使用在Windows環境下的PHP的php_oci12c.dll是編譯在32位元的,當然自行下載source code編譯的就依照自己設定的為主。

※ 透過phpinfo()看所安裝的PHP是x86 or 64然後下載相對應的Oracle Client版本回來,不過通常是32位元的就是了。

2. 將下載回來的Oracle Client (建議:Instant Client Package - Basic )解壓縮後的路徑位址( N:\instantclientXX\ ) 新增到系統環境變數內,請記得重新開機以利生效。

3. 至php.ini將php_oci12c.dll前面的分號";"拿掉,然後重啟web server(ex. IIS or Apache)。

4. 在CI的config\database.php
$db['default'] = array(
'dsn' => '',
'hostname' => '(IP or DomainName)/(SID)',
'username' => 'account',
'password' => '******',
'database' => '',
'dbdriver' => 'oci8',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);

5. 若依舊是顯示oci_connect() 未定義的錯誤,那麼就再下載含有Net Manager 的 Oracle Client。之前碰到Oracle Client裝64位元但:Instant Client裝x86,這樣PHP是沒法啟用oci8的套件。

備忘:
1. 改Oracle後table & column name都變大寫,好用的AI沒了得改sys_guid() ... 還有很多很多 =.=
2. CI在Sql Query會加雙引號,若使用Oracle須改system\db_driver.php 把雙引號拿掉。
3. CI2使用Oracle是悲劇的(ex. rownum()),後來直接改使用CI3就順多了。

2018年7月19日 星期四

aws EC1 設定 samba 備忘

最近又在搬家了,從自建機房->Azure->Jcloud到現在的AWS,貌似GCP, alibabacloud評估中~這~ =.= 工程師是無敵的吧? 每個環境都有些小眉角,放在這邊貌似比存在硬碟來的方便,所以就備忘一下。

安裝服務
yum install samba samba-client
chkconfig smb
service smb start/restart/stop

得在AWS的儀表版Security Group 設定Inbound PORT
open TCP 139 如果有windows設備要連線,則加開445
open UDP 137-138

設定分享資料夾位置
vim /etc/samba/smb.conf
---------------------------------------------------------------
security = user             #使用者驗證

[smb_folder]
valid users = @smbgrp       #群組授權
...

---------------------------------------------------------------

測試設定檔
testparm

新增使用者,將該使用者群組設定到smbgrp
useradd smb_account -G smbgrp
passwd smb_account
smbpasswd -a smb_account

測試連線
本地: smbclient //localhost/smb_folder -U smb_account
遠端: smbclient //smb-server-ip/smb_folder -U smb_account


2018年7月17日 星期二

json_decode 將json轉成陣列或object


再配合WebAPI時,常使用JSON來作為交換的格式,不過在多維陣列下的JSON組合就多了些眉角,把從資料庫取回的JSON紀錄轉換成ARRAY若成object再進行encode,此時JSON的地方將會有array("\"", "''", ":',", "\ufeff")這類的DATA跑出來造成還原解析上的異常,所以加個true成array在轉換就單純多了,做個備忘。
$json '{"a":1,"b":2,"c":3,"d":4}';
var_dump(json_decode($json));var_dump(json_decode($jsontrue));
Output:
object(stdClass)#1 (5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
}

array(5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
}

2018年6月22日 星期五

Centos 查詢對外IP位址

租用雲端空間的虛擬機器,使用ifconfig往往看到的都是內網IP,要查詢實際外網IP可使用下列指令

curl -s checkip.dyndns.org | sed -e 's/.*Current IP Address: //' -e 's/<.*$//' 
or shorter
curl ipinfo.io/ip 

2018年6月7日 星期四

jenkins執行shell script中無法使用sudo的解法

再導入DotNet Core專案在CentOS環境,欲利用Jenkins進行發布時發現在建置時尚需重啟DotNET Core Project.service,不過這服務jenkins原生的權限不足,所以加上sudo前墜後出現錯誤"sudo: no tty present and no askpass program specified",改善方式如下:

1. 在終端輸入 sudo visudo

2. 於文件最後增加 jenkins ALL=(ALL) NOPASSWD: ALL

3. 重啟jenkins

2018年3月30日 星期五

asterisk 與 fail2ban小記

下午用戶反映說總機掛了不能用,檢查SIP註冊是OK的,不過FreePBX記憶體顯示紅燈,SERVER看fail2ban的process CPU用量逼近100%,記憶體大多也是被這服務吃掉。ASTERISK的Console顯示大量的register fail事件。

先使用iptables去擋掉頻繁try account/pass的request IP後,在檢查設定發現問題出在logpath,設定成/var/log/asterisk/security這個設定不能說錯,不過ASTERISK的LogFile得同步設定,改成本來就有的messages就擋到了。

不過在過濾的正則表示式原使用的版本式asterisk 1.8設定,恩恩...不太符合所使用版本的格式,後又看到較進階版本(更精簡)設定,還是量身訂造的設定比較好(如下所示),做個備忘囉。

後記: 當初架設好的服務搭配fail2ban也是測試OK的,用久了版本更新沒再回來檢查,所以bot就再次發會打不死的精神進來,疏忽不得。

fail2ban-client status asterisk-iptables
Status for the jail: asterisk-iptables
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     34
|  `- File list:        /var/log/asterisk/messages
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   XXX.XXX.XXX.XXX


vim /etc/fail2ban/jail.local

[asterisk-iptables]
enabled  = true
filter   = asterisk
action   = iptables-allports[name=ASTERISK, protocol=all]
           sendmail[name=ASTERISK, dest=myaccount@xxx.xxx, sender=srvaccount@xxx.xxx.]
logpath  = /var/log/asterisk/messages
maxretry = 6
findtime = 21600
bantime = 86400


vim /etc/fail2ban/filter.d/asterisk.conf

# Fail2Ban configuration file
# Author: Xavier Devlamynck
 
[INCLUDES]
 
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
 
[Definition]
 
# Option:  failregex
# Notes.:  regex to match the password failures messages in the logfile.
# Values:  TEXT
#
log_prefix= \[\]\s*(?:NOTICE|SECURITY)%(__pid_re)s:?(?:\[\S+\d*\])? \S+:\d*
 
failregex = ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - Wrong password$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - No matching peer found$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - Username/auth name mismatch$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - Device does not match ACL$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - Peer is not supposed to register$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - ACL error \(permit/deny\)$
            ^%(log_prefix)s Registration from '[^']*' failed for '<HOST>(:\d+)?' - Not a local domain$
            ^%(log_prefix)s Call from '[^']*' \(<HOST>:\d+\) to extension '\d+' rejected because extension not found in context 'default'\.$
            ^%(log_prefix)s Host <HOST> failed to authenticate as '[^']*'$
            ^%(log_prefix)s No registration for peer '[^']*' \(from <HOST>\)$
            ^%(log_prefix)s Host <HOST> failed MD5 authentication for '[^']*' \([^)]+\)$
            ^%(log_prefix)s Failed to authenticate (user|device) [^@]+@<HOST>\S*$
            ^%(log_prefix)s (?:handle_request_subscribe: )?Sending fake auth rejection for (device|user) \d*<sip:[^@]+@<HOST>>;tag=\w+\S*$
            ^%(log_prefix)s SecurityEvent="(FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword)",EventTV="[\d-]+",Severity="[\w]+",Service="[\w]+",EventVersion="\d+",AccountID="\d+",SessionID="0x[\da-f]+",LocalAddress="IPV[46]/(UD|TC)P/[\da-fA-F:.]+/\d+",RemoteAddress="IPV[46]/(UD|TC)P/<HOST>/\d+"(,Challenge="\w+",ReceivedChallenge="\w+")?(,ReceivedHash="[\da-f]+")?$
 
# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
ignoreregex = 


後記:
最近又發現一堆機器人Try Password的LOG,結果fail2ban GG惹...原因在於message file太大造成記憶體太小就服務終止。後來在Message LOG 設定只儲存NOTICE跟SECURITY就好,因為FULL LOG已經都包含其他的,這樣設定也OK

另外fail2ban的Log儲存預設是SYSLOG,這樣在檢視時會造成混淆也不好找,因此修改
fail2ban.conf的logtarget設定。

搜尋:logtarget = SYSLOG
改為:logtarget = /var/log/fail2ban.log


2018年3月20日 星期二

夯名詞:新零售, 區塊鏈, 人工智慧有感

最近聽得多CI BI AI,這類高來高去的名詞,身為一位相對務實的開發人員,對於這些總是環繞在怎麼做到~做到了又如何應用的死循環中。

先有需求再建構/配合模型,自所擁有的管道(軟體服務)蒐集(CI)有用的RAW DATA,送入模型後得到BI(商業智慧),進送入機器學習建構資源庫未來依此來判別(AI),感覺上好像是在消滅自己工作一樣,一但有了AI那麼系統開發作業不就會被代勞嗎(=.= 真是想太多了,不過工程師有個好處,沒有理論支援下產出的工人智慧,對某些人來看也會被美化成人工智慧)。

AI在課堂中最早是從類神經網路的建構,透過訓練方式來歸納出許多的 pattern,這些在依照所配得權重以計算出最合適的結果,恩恩...這樣看來如何取代工作呢? 不過市場上所有產品都是需要包裝的,當軟體要搭上這股熱潮再不打算大改的情況下,只好再透過重新包裝來應付,但這些也是得燒掉多的腦細胞。

新零售也是如此,貌似"無人商店"也得沾上一點邊,亞馬遜所提的概念店面東改西湊(東施效顰的概念),別人有的都加進來沒有的就是優勢? 不過所看的到的都是表象,技術這類的潘朵拉之盒卻得不到;在區塊鏈上因bitcoin而廣受討論,兩個相加起來就是如何去中間話在新零售上,生產->配銷->消費者的概念把中間的"經銷商"由軟體系統取代掉,讓消費者能快速取得新鮮品優的產品(當然能做到價廉就更不可思議了),可現實真是能如此嗎? 大家都覺得經銷商賺得飽飽的(Ex. 專門剝削生產端宰殺消費者的菜蟲?),但是許多事情都有他不為人知的專業在,各個環節所需承擔的風險成本往往是被忽視的,用軟體系統+AI來處理掉中間人學術討論上總是誘人的,回歸現實就傷腦筋囉...

拜讀21世紀資本論(超厚)比起看富爸爸窮爸爸系列來的有感,資本市場講求的看中的或許底蘊有那麼1%重要的,其他99%就不是腦袋硬梆梆只會硬幹的人能去求的,當創造話題的大老們衝在前頭,後頭收尾的工程師們...除了爆些新鮮個肝賺取微薄的OO之外,就只能幻想"有夢最美希望相隨"不是口號的一天,呵呵。

~~有段順口溜流傳許久,繼續做自我看著笑笑即可 ~~
東混西混,一帆風順;  苦幹實幹,撤職查辦;
盡職負責,卻遭指責;  任勞任怨,永難如願;
推拖栽贓,滿排勳章;  屢建奇功,打入冷宮;
不拍不吹,狗屎一堆;  全力以赴,升遷耽誤;
苦苦哀求,互踢皮球;  看緊國庫,馬上解僱;
會捧會現,傑出貢獻;  會鑽會溜,考績特優。

2018年2月9日 星期五

summernote 上傳檔案處理

summernote是個很輕量的編輯器,往常都使用CKEditor來提供服務,不過青菜蘿蔔各有所好...其中最常調整的莫過於檔案上傳處理,備忘一下囉...

HTML Header
include id_summernote.css & id_summernote.js
#語系要更換的話,得多include該文本 summernote-zh-TW.js

只需要將DIV放置在愈顯示處
HTML:
<div id="id_summernote" class="summernote"></div>

如果上傳檔案不特別處理的話,預設式以Base64編碼方式存放於編輯區中,這樣存入資料庫將會大大的增加儲存量,於未來存取時可能耗費更多伺服器資源。改成存放圖檔於伺服器中,這樣瀏覽上路由器/交換器/瀏覽器都可能暫存該圖檔資源,而減輕WEB SERVER吞吐頻寬負擔。

JavaScript
<script type="text/javascript">
jQuery(document).ready(function() {
      //載入
              initEditor();
});

  /* 編輯器初始化 */
  function initEditor() {
    $('.summernote').summernote({
        lang: 'zh-TW',       // default: 'en-US'
        toolbar: [
                ["style", ["style"]],
                ["font", ["bold", 'italic', "underline", "clear"]],
                ["fontname", ["fontname"]],
                ['fontsize', ['fontsize']],
    ["color", ["color"]],
                ["para", ["ul", "ol", "paragraph"]],
                ["table", ["table"]],
                ["insert", ["link", "picture", "video", "hr"]],
                ['height', ['height']],
                ["view", ["fullscreen", "codeview", "undo", "redo", "help"]]
            ],
        height: 200,         // set editor height             
        minHeight: null,    // set minimum height of editor
        maxHeight: null,    // set maximum height of editor
        focus: true,         // set focus to editable area after initializing summe
        tabsize: 2,
            dialogsInBody: true,
        callbacks: {
            onImageUpload: function (files) { //the onImageUpload API
                img = sendFile(files[0], $(this));
            }
        }
     
    });  
  }  
  function sendFile(file, editor) {
            data = new FormData();
            data.append("upload", file);         
            $.ajax({
                data: data,
                type: "POST",
                url: "<?=site_url('controler/function'); ?>",
                cache: false,
                contentType: false,
                processData: false,
                success: function (datas) {
                    var data = $.parseJSON(datas);
                    if (data.uploaded == '1')
                      // the insertImage API
                      editor.summernote('insertImage', data.url, 'image name');
                    else
                      console.log(data.error);
                },
error:function(data){
      console.log(data);
}
            });
      }

</script>

至於回傳的資料中,透過JSON編碼方式回傳,這樣有利於處理檔案儲存狀態的追蹤處理。

Nginx return 405 error

最近搬移伺服器,WEB伺服器也同步將Apache改成Nginx,客戶回報標籤列印軟體出現異常無法同步更新,Debug後看到回傳405錯誤,可該URL直接呼叫卻是能取得資料的,爬了些文看到解法...

於conf檔中新增error_page 405 =200 $request_uri;後重啟nginx排除掉問題。

location / {
        error_page 405 =200 $request_uri;
        try_files $uri $uri/ /petel/index.php;
 }

至於後來想想為什麼要用POST方式而不使用GET方式取呢? 往常POST請求也可以做到GET請求的回覆,所以從程式碼共用/使用的方向來想也挺合理的,只不過Apache沒出這種問題吧。


2018年2月2日 星期五

透過CKEditor或Summernote上傳iphone拍的圖片顯示被轉向問題

Bootstrap的RWD設計很方便,不過開發狀況上要考慮的點就挺多的,客戶反映說手機上圖時顯示是反的,但是圖下載下來是正的,乍聽之下挺神奇的不是事實卻是如此。

爬文得知在iphone手機拍攝照片時,會取決於拍照按鈕位置來存檔,所以在圖片的Orientation的狀態上就可能是旋轉的,不過在PC檢視上通常會自動轉回來可是瀏覽器就...參考網誌

另外因為手機的鏡頭跟效能越來越強,所以照片的解析度SIZE變超大,看起來是很舒服不過在WEB應用上就挺占頻寬的,簡單的壓縮功能就一併去實現囉。

//圖檔壓縮與旋轉
function image_resize($targetFile){
if (file_exists($targetFile)){
$img_array = getimagesize($targetFile);
$isResize = false;
$ratio = 512;
if ($img_array[0] > $ratio){
$config['width'] = $ratio;
$height = $img_array[1] / ($img_array[0] / $ratio);
$config['height'] = intval($height);
$isResize = true;
} else if ($img_array[1] > $ratio){
$config['height'] = $ratio;
$width = $img_array[0] / ($img_array[1] / $ratio);
$config['width'] = intval($width);
$isResize = true;
}
if ($isResize){
$this->load->library('image_lib');
$config['image_library'] = 'gd2';
$config['source_image'] = $targetFile;
$config['maintain_ratio'] = TRUE;

//because iphone photo maybe save rotation by sensor
$exif = @exif_read_data($targetFile);
if(!empty($exif['Orientation'])) {
switch($exif['Orientation']) {
  case 8:
   $config['rotation_angle'] = '90';
   break;
  case 3:
   $config['rotation_angle'] = '180';
   break;
  case 6:
   $config['rotation_angle'] = '270';
   break;
}
$this->image_lib->initialize($config);
$this->image_lib->rotate();
}
$config['rotation_angle'] = '0';
$this->image_lib->initialize($config);
$this->image_lib->resize();
}

return true;
} else {
return false;
}

}