Google+ Followers

2018年1月20日 星期六

AWS 筆記 - 使用 Ubuntu server 更換 Windows server 2012 帳號狀態

AWS Windows server

公司在三年前在 AWS Beijing 開了一台 Windows server 2012 運行一些套裝軟體,然而,負責的 IT 在整理系統帳號時,把 guest 關閉了。接著發生很多奇妙事件,最恐怖的是機器無法遠端登入了 Orz

一開始想朝更換 Administrator 密碼試試,結果一看過程步驟也不少:
使用 EC2Config 重置 Windows 管理员密码 -
https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/WindowsGuide/ResettingAdminPassword_EC2Config.html
接著,反而朝惡搞的方式進去研究,例如:如何修改機碼啟用 guest 帳號


最後,使用了前一份(一年前版本)的全系統備份,開了一台機器,把他的 C:/Windows/System32/config/SAM 複製出來,覆蓋掉無法登入的機器上,如此一來就搞定了!

情況簡介:
A server: 壞掉的 Windows server 2012
B server: 從 A server 現狀建置 AMI 而開啟的機器
C server: 前一版次備份的 Windows server
D server: Ubuntu 14.04 server
A 計畫:
將 B server 的 C 槽卸下,並掛到 C server 上進行 B server 的 C槽 資料修改,改完後再卸下,重裝到 B server,把 B server 重新啟動,看看是否正常

然而,A 計劃失敗了,在 AWS console 可以看到系統無法正常通過檢驗,於是啟動了 B 計畫

B 計畫:
再次建置乾淨的 B server,將 B server 的 C 槽卸下,也把 C server - C 槽卸下,並一起掛到 D server 上,拿著 C server - C 槽關鍵資訊,直接覆蓋掉 B server - C 槽 資料,修改完後再卸下,重裝到 B server,再把 B server 重新啟動
果真 B 計劃成功了 :p 猜測 A方案 可能因為修改的是 Windows OS level 資訊,修改完可能也改變了 B server - C 槽狀態,以至於重新啟用時反而資訊對不上而無法開機。

AWS Windows server

其他筆記:

  1. Windows 需要 stop 機器,才能卸下指定硬碟,請開啟 “終止保護“ 避免不小心做錯事
  2. Windows 的系統碟(C:)根设备用 /dev/sda1 資訊,卸下後要重新裝上時,記得要用原本的 /dev/sda1 才行
  3. 在 EC2 - EBS 管理介面要卸下某硬碟時,建議把 Name 帶點原本的機器資訊,方便找尋
  4. 在 Ubuntu 要 mount NTFS 都還算簡單,sudo mount /dev/xsgf1 /mnt/Win2012CDisk 
  5. Windows 的帳號資訊擺在 C:\Windows\System32\config\SAM 檔案,可以單純做 cp /mnt/C_server_c_diskt/Windows/System32/config/SAM /mnt/B_server_c_diskt/Windows/System32/config/SAM 
  6. 驗證完畢後,就可以安心地把 A server 先暫停 -> 把系統槽卸下 -> 掛到 Ubuntu server -> mount 起來 -> 覆蓋掉 SAM -> 卸下 -> 掛回 A server -> A server 重新開機

2017年12月30日 星期六

[Javascript] Google Sheets 與 Google app script - 處理 JSON API data

之前看到別人用 Google docs - sheets 與表單製作點飲料的功能(有點年紀的大概都會想到訂便當系統?),才發現 Google Sheets 其實有提供像 Excel - VBA 這種制定功能的架構,真是相見恨晚!最近幾年都改用Google docs做了很多事,面對數據整理也都在透過 Google sheets API 匯出後再整理回去,若有些很簡單的功能,不見得需要這樣做了!

舉個例:撈別人家的 API 資料,取出來參考。來個更精準的需求:查看某銀行的幣值變化或是某交易平台的虛擬幣價格。

這時就需要"存取網路"跟"JSON"處理方式,很佛心的,有人已提供了:

Import JSON into Google Sheets, this library adds various ImportJSON functions to your spreadsheet - https://github.com/bradjasper/ImportJSON

此目的是做成很通用的工具,但有時候,有些欄位資料想要自行客製化,那就寫一點 code 吧

1. 存取網路:

var jsondata = UrlFetchApp.fetch(url);

2. 處理 json format:

var object   = JSON.parse(jsondata.getContentText());

剩下的,對會寫程式的,就很簡單了,一切就是寫 javascript 啦,像是將 timestamp 轉 date:

var date_obj = new Date(input_timestamp_value * 1000);
var date_formated =
        date_obj.getFullYear() + '/' + (date_obj.getMonth() < 10 ? '0' : '' ) +(date_obj.getMonth()+1) +'/'+ (date_obj.getDate() < 10 ? '0' : '' ) + date_obj.getDate()
          ' ' + (date_obj.getHours() < 10 ? '0' : '') + date_obj.getHours() + ':' + date_obj.getMinutes() + ':' + (date_obj.getSeconds() < 10 ? '0': '') + date_obj.getSeconds();


後續在 Google app script 包裝成一個 function 後,並定義註解描述後,在 Google sheets 就可以用了!

/**
 * get_json_info
 *
 * @param {url}
 *
 * @customfunction
 **/
function get_json_info(url,fieldname) {
  var jsondata = UrlFetchApp.fetch(url);
  var object   = JSON.parse(jsondata.getContentText());
  return object[fieldname];
}

function main() { // 用來在 Google app script 裡 debug,可以指定跑此函數
  get_json_info("https://ipinfo.io/json","ip");
}


在 Google sheets 就簡單寫

=get_json_info("https://ipinfo.io/json","ip")

收工 XD

2017年11月27日 星期一

棋手 vs. 棋子

老闆相對於公司就是棋手,專門擬定策略;員工相對於公司就是棋子,負責執行老闆的策略。

許多時候,棋子待久了也會希望棋手多多用它,增加出場的機會,殊不知,使得出“將軍抽車”的招數不是常態,更多佈局可能是換掉敵方的棋子 XD

有意識的棋子,到底多公司是好是壞?這一切都是結果論 orz 只是,若棋手很威了,那棋子還是乖乖執行即可,完美地執行任務才能驗證棋手策略,若棋手不威,我看棋子在政治鬥爭下拿不到權力時,還是乖乖離開吧 XD

前幾天跟一位老棋手閒聊,老棋手說:你的經驗都只是當個棋子而已,我們的視野差距極大。過去老棋手在當棋子時,錯過了一些邀約,令旁人感到惋惜,但一切都基於對自己人生的選擇權,許多事物都是結果論的。老棋手說著說著,只希望每一次的戰場上的輸贏,都可以讓妻小安心地生活。這一切都只是選擇。並且叮嚀著:你無法選擇機運或聰明才智,但你可以選擇努力、用心地過日子,把握機會。

聽著聽著,原本想跟老棋手分享周邊朋友在外商新創打滾的故事,瞬間就不知該說沒,只能點頭唯喏了 XD

2017年10月27日 星期五

Firebase 開發筆記 - 使用 PHP 之 Verify ID tokens using a third-party JWT library

PHP 漸漸地越來越沒有愛了 XD Ubuntu server 預設還是停留在 php5.5 系列,而許多套件都進入了 PHP 7 世界了。不知是不是這樣子,所以官方沒再推出 PHP SDK ?於是乎,只好自己刻一下下。也順勢認識 JWT,第一次認識是在 APNs Auth Key 的使用,沒想到這麼快又要上戰場了 XD

網路資源:
若覺得很煩,就直接用 https://github.com/kreait/firebase-php 吧!這篇專注在紀錄 JWT - Firebase 的使用和驗證。

用法:

$ mkdir jwt-3.2.2 ; cd jwt-3.2.2 ; composer require lcobucci/jwt
$ vim test.php
<?php
require 'vendor/autoload.php';

$token_string = 'XXXXXXXXXXX';

$signer = new Lcobucci\JWT\Signer\Rsa\Sha256();
$keychain = new Lcobucci\JWT\Signer\Keychain;
$parser = new Lcobucci\JWT\Parser;

try {
$token = $parser->parse($token_string);
} catch( Exception $e ) {
echo "decode failed\n";
exit;
}

// check key field
foreach (array( 'iat', 'exp', 'aud', 'iss', 'user_id' ) as $field) {
if (!$token->hasClaim($field)) {
echo "no $field\n";
exit;
}
}

if ($token->isExpired()) {
echo "exp expired\n";
exit;
}

if (!$token->hasHeader('kid')) {
echo "no kid\n";
exit;

}
$kid = $token->getHeader('kid');

$keys = json_decode(file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/[email protected]'), true);

if (!isset($keys[$kid])) {
echo "kid not found\n";
exit;
}


try {
if( $token->verify($signer, $keychain->getPublicKey($keys[$kid])) )
echo "PASS\n";
} catch (Exception $e) {
echo "Failed";
}

$user_id = $token->getClaim('user_id');


連續動作(PHP Codeigniter Usage):

$ cat Jwt_library.php
<?php
require_once 'jwt-3.2.2/vendor/autoload.php';
class Jwt_library {
public function __construct() {
$this->ci = &get_instance();
$this->parser = new Lcobucci\JWT\Parser;
$this->keychain = new Lcobucci\JWT\Signer\Keychain;
$this->signer = new Lcobucci\JWT\Signer\Rsa\Sha256;
}

public function verify_firebase_token(&$error, $user_token, $project_id, $service_public_keys = array()) {
$user_id = NULL;
$error = NULL;
$token = false;
try {
$token = $this->parser->parse($user_token);
} catch (Exception $e) {
$error= 'decode failed';
return NULL;
}

// check filed name
foreach (array( 'iat', 'exp', 'aud', 'iss', 'user_id' ) as $field) {
if (!$token->hasClaim($field)) {
$error = "no $field";
return NULL;
}
}

if ($token->isExpired()) {
$error = 'exp expired';
return NULL;
}
//if ($token->getClaim('iat') > time() )
// return 'token has been issued in the future';

if ($token->getClaim('aud') != $project_id ) {
$error = 'aud error';
return NULL;
}

$user_id = $token->getClaim('user_id');

$kid = false;
try{
$kid = $token->getHeader('kid');
} catch (Exception $e) {
$error = 'no kid';
return NULL;
}

if (!isset($service_public_keys[$kid])) {
$error = 'kid not found: '.$kid;
return NULL;
}

try {
if ($token->verify($this->signer, $this->keychain->getPublicKey($service_public_keys[$kid])))
return $user_id;
} catch (Exception $e) {
$error = 'verify failed';
}

return NULL;
}
}


$ cat php_ci_controller.php
<?php
$output = array( 'status' => false );

$authorization_info = $this->input->get_request_header('Authorization', True);
if (empty($authorization_info) || strstr($authorization_info, 'Bearer ') == false) {
        $output['error'] = -1;
        $output['message'] = 'no Authorization';
        $this->_json_output($output);
        return;
}

$firebase_user_token = trim(substr($authorization_info, strlen('Bearer ')));
$this->load->library('jwt_library');
$keys = @json_decode(@file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/[email protected]'), true);
$firebase_user_id = $this->jwt_library->verify_firebase_token($verify_error, $firebase_user_token, 'YourFirebaseProjectId', $keys);
if (empty($firebase_user_id)) {
$output['error'] = 1;
$output['message'] = 'verify: '.$verify_error;
$this->_json_output($output);
return;
}

2017年10月26日 星期四

網域交易 - 使用 Sedo.com 交易與 Namecheap 網域轉移

domain_transfer_00_namecheap

由於追求更佳的 SEO 效果,強者同事跟原網域擁有者進行議價,並且採用數家網路服務推論網域的價格、Google 關鍵字比較:
最後,老闆也接受提案跟給予此次任務的資源範圍,就開始這一連串的網域轉移過程,整個過程為:
  1. 查 whois 得知網域擁有者,與賣家通信議價
  2. 談定價錢後,賣家挑定 Sedo.com 進行交易
  3. 賣家把 Domain 的特定 Email 聯繫設定為 Sedo.com,如[email protected]
  4. Sedo.com 驗證賣家的確擁有網域,如 Email 聯繫、domain name server 設定等
  5. 買家電匯至 Sedo.com 指定帳戶
  6. 賣家解除 Domain 鎖定、Sedo.com 提供 Domain 轉移 Auth Code
  7. 買家進行網域移轉,過程會發信給 whois record 填寫的 Admin Email進行最後認證
  8. 由於 Sedo.com 為此 Domain Admin Email 收信者,可自動完成驗證
  9. 買家完成網域轉移,可以掌握該網域
其中 (7) 步驟部分,我們採用 Namecheap 管理,一開始就直接用 Namecheap 的 Domain transfer,直接在界面上輸入要移轉的 domain,就會顯示該 domain 是否 unlock 、請輸入 Auth Code 等等,此部會有一個金流要支付,為 ICANN fee ($0.18) + domain 續約一年的費用,約 10 美金上下

domain_transfer_01_init_at_namecheap

付款完,可以在 Namecheap Dashboard 上看到 domain 的狀態,依序為:
  1. Domain is with another registrar. / Transfer will begin shortly.
  2. Domain is with another registrar. / Verifying domain contacts.
  3. Domain is with another registrar. / Awaiting release from previous registrar.
移轉完畢時,Namecheap 會發信告知,信件標題:Transfer Domain: Complete: YourDomainName,整個時程不超過 5 個小時。

關鍵原理:

  • 網域移轉時,轉出方須設定 domain unlock 、提供 Auth Code 給對方,接收方需要正確輸入 Auth Code 才能進行,而接收的網域服務管理者,也會發信給原網域的 Admin Email 告知此事,對方可以有七天的時間思考,七天沒回應就默認接受,若有異議當下就可以取消
  • 流程是 Sedo.com 先幫忙確認該網域可以被轉移,透過檢查 domain lock/unlock 狀態、指定對方要更新 Amdin Email 聯絡人等等的機制
  • 整筆交易 Sedo.com 收取 3% 服務費(此次並未在 Sedo.com 平台議價),金額超過一萬鎂時,無法使用 Paypal ,只能用電匯;若單純在 Sedo.com 進行採購標案,Sedo.com 本身會先檢驗買家的財務能力,包含要給予戶頭資訊,像辦理信用卡一樣
  • 使用 Namecheap 接收時,會收一筆 ICANN fee $0.18 費用 + Domain transfer 費用(滿像原網域續約一年的費用),最後接收後,網域有效時間應當會多增加一年