2013年1月25日 星期五

[Linux] 將 eml 轉 mbox 格式 @ Ubuntu 12.04

$ sudo apt-get install procmail
$ formail -b < file.eml > file.mbox
$ mutt -f file.mbox


[Linux] 手動掛載 Windows Share (網芳/Samba) @ Ubuntu 12.04

$ sudo apt-get install smbfs
$ sudo mkdir -p /data/samba
$ sudo smbmount //WindowsSambaIP/Path /data/samba -o username=user,password=pass,rw
$ sudo smbmount //WindowsSambaIP/Path /data/samba -o username=guest
$ sudo umount /data/samba


2013年1月24日 星期四

[Linux] sudo echo '+::::::' >> /etc/passwd : Permission denied @ Ubuntu 12.04

有時偷懶寫 script 想要用 echo "somethng" 寫資料到 root 擁有的檔案,就會出現 Permission denied 的訊息:


$ sudo echo '+::::::' >> /etc/passwd
-bash: /etc/passwd: Permission denied


改用下招即可:


$ sudo sh -c "echo '+::::::' >> /etc/passwd"


其實碰到的使用情境是 NIS client 的一堆檔案更新:


$ sudo sh -c "echo '+::::::' >> /etc/passwd" && sudo sh -c "echo '+:::' >> /etc/group" && sudo sh -c "echo '+::::::::' >> /etc/shadow" && sudo sh -c "echo '+:::' >> /etc/gshadow"


補充一點,如果 NIS 尚未設定好時,這時用 sudo 或 login 等相關動作會卡住非常久,從 /var/log/syslog 會看到 … ypbind … : broadcast: RPC: Timed out. 相關訊息,故建議先用 sudo su 轉成 root 角色在安裝 NIS 相關動作 :P


[Linux] 架設與管理 NIS Server/NIS Client @Ubuntu 12.04

安裝不難,難就難在沒設定好 debug 痛苦 XD


簡易筆記:


Server (此例 hostname 為 NISServer,需確保 /etc/hosts 有定義此 hostname):


$ sudo apt-get install nis
$ sudo vim /etc/default/nis
NISSERVER = master
$ sudo vim /etc/defaultdomain
NISServer
$ sudo service portmap start ; sudo service ypbind start ; sudo service ypserv start ; sudo service yppasswdd start ; sudo service ypxfrd start
$ sudo /usr/lib/yp/ypinit -m
$ sudo make -C /var/yp


每次更新 /etc/passwd, /etc/group, …
需執行 $ sudo make -C /var/yp


Client:


$ sudo su
$ apt-get install nis
記得 NIS domain 要填好,要填 NIS Server 的,不然會找不到 NIS Server 而導致 login 卡住。此例為 NISServer
$ vim /etc/hosts
NISServerIP NISServer
$ echo '+::::::' >> /etc/passwd ; echo '+:::' >> /etc/group ; echo '+::::::::' >> /etc/shadow ; echo '+:::' >> /etc/gshadow
$ vim /etc/nsswitch.conf
passwd: compat nis
group: compat nis
shadow: compat nis
hosts: files dns nis
netgroup: nis
$ vim /etc/yp.conf
ypserver = NISServerIP_or_NISServer


其他資訊:


若 NIS Client 裝完後,不幸 login、sudo 等動作都非常卡,那查看 syslog 大概就是會這樣:


$ sudo tail -f /var/log/syslog
… ypbind … : broadcast: RPC: Timed out.


解法:


更新 /etc/hosts、/etc/defaultdomain、/etc/yp.conf 設定好後,重開機試試


若 NIS Server 帳號與 NIS Client 帳號的 uid 有撞到,預設 NIS Client 會以自身設定檔為主,需避免一樣:


在 NIS 服務中,假設 server 有個 uid=1000 的帳號,而 client 也有一個一模一樣的 uid=1000 帳號,但兩者帳號名稱不同,此時若使用 server 帳號在 client 端登入,會被對應成 client 端 uid=1000 帳號,導致一些家目錄以及相關權限的問題。實例:


@ NIS Client:


$ ypcat passwd
serveruser:x:1000:1000:serveruser,,,:/home/serveruser:/bin/bash
$ cat /etc/passwd
clientuser:x:1000:1000:user,,,:/home/clientuser:/bin/bash


如此一來,在 client 端用 serveruser 帳號登入後,會變成 client user 帳號,此時用 sudo ls 時,會變成要輸入 client user 密碼,而非用 server user 的。這樣會有不少問題產生。解法就是把 server user 的 uid 起始改成 2000 以上,讓 1000~2000 的作為 client 端帳號的處理,同理在 gid 也要稍微處理一下比較好。而在 sudo 的權限使用上,可以規範 server 端用 admin group ,在 client 端用 sudo group ,分開使用即可。


@ NIS Server:


改變現有 gid (或直接編輯/etc/group):


$  sudo groupmod -g 1999 service


更改現有帳號 uid 跟 gid (或直接編輯 /etc/passwd ?), 更改時該 account 不能在線上:


$ sudo usermod -u 2000 -g 1999 account


別忘了更新已存在的目錄、檔案權限:


$ sudo chown -R account:group /path


往後新增帳號時,多加 --firstuid 和 -gid 來避開 NIS Client 本機 uid/gid:


$ sudo adduser --firstuid 2000 --gid 2000 --no-create-home --disabled-login --gecos ''  account 


簡易 NIS Sever/Client 帳號群組規劃:


NIS Server 建立的帳號 uid 以 2000 為基準點,假設 NIS Client 不會建立太多 local account ,故保留 1000~1500 組供 NIS Client 建立 local account;NIS Server 建立一些服務帳號就以 1999 遞減


/etc/group @ NIS Server:


rd:x:2000:
admin:x:1999:account
service:x:1998:gitolite,redmine,dropbox
dropbox:x:1997:dropbox


/etc/passwd @ NIS Server:


account:x:2000:2000:,,,:/home/account:/bin/bash
gitolite:x:1999:1998:,,,:/tmp:/bin/bash
redmine:x:1998:1998:,,,:/tmp:/bin/bash
dropbox:x:1997:1998:,,,:/tmp:/bin/bash


建帳號 @ NIS Server:


一般帳號:


$ sudo adduser --firstuid 2000 --gid 2000 --disabled-password --gecos '' account


加入 sudoers:


$ sudo usermod -a -G admin account


服務帳號:


$ sudo adduser --uid 1996 --gid 1998 --no-create-home --disabled-login --gecos '' service_account


記得要更新 yp:


$ sudo make -C /var/yp


2013年1月23日 星期三

[Linux] 小型研發團隊 - NIS + Redmine + Gitolite + Dropbox @ Ubuntu 12.04 64Bit

最近正準備遷移工作環境,五人單位,大部份的人有還算不錯用的桌機,是 i3 等級的 CPU、4~8GB 記憶體,如此的環境中,大概個人研發可以用 VM 管理機器。因此,只需準備簡易的 Server 機器,管管大家的程式碼(git/gitolite/gitweb)、工作報告(redmine)、帳號登入(nis)。架設 NIS 的好處是可以在 VM 上使用,掛進帳號資訊就可以省下開帳號等瑣碎的事情了,至於 NFS  呢?很抱歉,小機器負擔不起 XDDD


故以下就在一台小主機上,裝好上述環境。


安裝 Ubuntu 12.04 64Bit server 後:


$ sudo vim /etc/apt/sources.list
:%s/\/\/us\./\/\/jp\./g
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade
$ sudo mkdir /data
$ sudo chmod 777 /data


安裝 Redmine:


$ sudo apt-get -y install apache2 libapache2-mod-passenger git subversion cvs mercurial build-essential apache2-prefork-dev libaprutil1-dev libapr1-dev libcurl4-openssl-dev ruby-rvm ruby-dev gem libmagickwand-dev sqlite3 sqlite3-doc libsqlite3-ruby libsqlite3-dev apache2-mpm-itk
$ sudo gem install rails bundler passenger
$ sudo passenger-install-apache2-module
$ sudo vim /etc/apache2/mods-available/passenger.load
#LoadModule passenger_module /usr/lib/apache2/modules/mod_passenger.so
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-3.0.19/ext/apache2/mod_passenger.so
$ sudo vim /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
#PassengerRoot /usr
#PassengerRuby /usr/bin/ruby
PassengerRoot /var/lib/gems/1.8/gems/passenger-3.0.19
PassengerRuby /usr/bin/ruby1.8
</IfModule>


$ sudo adduser --quiet --gecos "" -disabled-login --home /data/redmine redmine
$ sudo chown -R redmine:redmine /data/redmine
$ sudo su - redmine
$ git clone git://github.com/redmine/redmine.git
$ cd redmine
$ git branch local-config
$ git checkout local-config
$ vim config/database.yml
production:
adapter: sqlite3
database: db/production.db

development:
adapter: sqlite3
database: db/development.db


$ bundle install --without development test --path vendor/bundle
$ ruby script/about
$ rake generate_secret_token
$ RAILS_ENV=production rake db:migrate
$ RAILS_ENV=production rake redmine:load_default_data
$ mkdir public/plugin_assets PassengerUploadBufferDir


$ sudo vim /etc/apache2/conf.d/redmine
<Virtualhost *>
  DocumentRoot /home/changyy/webapp
  AssignUserId redmine redmine
  RailsBaseURI /redmine
  <Directory /home/changyy/web/app/redmine>
    AllowOverride all
    Options -MultiViews
  </Directory>
</Virtualhost>


$ sudo vim /etc/apache2/conf.d/redmine
Alias /redmine "/data/redmine/redmine/public"
RailsBaseURI /redmine
<Directory /data/redmine/redmine/public>
    AssignUserId redmine redmine
    PassengerUploadBufferDir /data/redmine/redmine/PassengerUploadBufferDir
    AllowOverride all
    Options -MultiViews
</Directory>


$ sudo service apache2 restart


往後更新 Redmine:


$ sudo su - redmine
$ cd redmine
$ git checkout master
$ git pull
$ git checkout local-config
$ git merge master
$ bundle update
$ bundle install
$ rake db:migrate RAILS_ENV=production 
$ rake redmine:plugins:migrate RAILS_ENV=production
$ rake tmp:cache:clear
$ rake tmp:sessions:clear
$ exit
$ sudo service apache2 restart


強制使用 https:


$ sudo a2enmode rewrite
$ sudo vim /etc/apache2/sites-available/default
DocumentRoot /var/www
<Directory />
  Options FollowSymLinks
  AllowOverride None
  
  RewriteEngine on
  RewriteCond %{SERVER_PORT} !^443$
  RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>
<Directory /var/www/>
  Options Indexes FollowSymLinks MultiViews
  AllowOverride None
  Order allow,deny
  allow from all

  RewriteEngine on
  RewriteCond %{SERVER_PORT} !^443$
  RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>


安裝 Gitolite:


$ sudo mkdir -p /data/gitolite
$ sudo adduser --quiet --gecos "" -disabled-login --home /data/gitolite gitolite
$ sudo chown -R gitolite:gitolite /data/gitolite
$ sudo su - gitolite
$ whoami
gitolite
$ git clone https://github.com/sitaramc/gitolite.git
$ mkdir ~/.ssh ~/bin
$ chmod 700 ~/.ssh
$ ssh-keygen -t rsa -P '' -f ~/.ssh/gitolite
$ ls ~/.ssh
gitolite gitolite.pub
$ mv ~/.ssh/gitolite ~/.ssh/id_rsa
$ gitolite/install -to $HOME/bin
$ ~/bin/gitolite setup -pk ~/.ssh/gitolite.pub
$ ls ~/
bin gitolite projects.list repositories
$ vim ~/.gitolite.rc
...
UMASK => 0027, # = 0750
...
COMMANDS =>
{
'D' => 1,
},

$REPOPATT_PATT = qr(^\@?[[0-9a-zA-Z\(^][-0-9a-zA-Z._\@/+\\^$|()[\]*?!={},]*$);


$ ssh localhost help
hello gitolite, this is gitolite3 v3.3-4-gd8fe757 on git 1.7.9.5


list of remote commands available:


D
desc
help
info
perms
writable


$ git clone ssh://localhost/gitolite-admin.git
$ cd ~/gitolite-admin
$ vim conf/gitolite.conf
@admin = gitolite changyy
@rd = changyy


repo gitolite-admin
RW+ = @admin


repo testing
RW+ = @all


repo priv/CREATOR/[a-zA-Z0-9].*
C = @rd
RW+D = CREATOR
RW = WRITERS
R = READERS


repo CREATOR/[0-9a-zA-Z].*
C = @rd
RW+D = CREATOR
RW = WRITERS
R = @all


repo ^(?!priv/)[0-9a-zA-Z].*
RW+D = CREATOR
RW = WRITERS
R = @all


其他用法:


可以先由個人先在 priv 慢慢開發程式,等到程式發展差不多後,就用 link 到外頭


$ cd ~/repositories
$ ln -s priv/changyy/my.git public-link-from-priv.git


非擁有者:
$ ssh gitolite@localhost
PTY allocation request failed on channel 0
hello user, this is gitolite@localhost running gitolite3 v3.3-4-gd8fe757 on git 1.7.9.5


R gitolite-admin
R public-link-from-priv
R W testing
Connection to localhost closed.


擁有者:
$ ssh gitolite@localhost
PTY allocation request failed on channel 0
hello changyy, this is gitolite@localhost running gitolite3 v3.3-4-gd8fe757 on git 1.7.9.5


R W gitolite-admin
R W priv/changyy/my
R W public-link-from-priv
R W testing
Connection to localhost closed.


安裝 Gitweb:


$ sudo apt-get install gitweb
$ sudo vim /etc/gitweb.conf
$projectroot = "/data/gitolite/repositories";
$feature{'highlight'}{'default'} = [1];
$ sudo vim /etc/apache2/conf.d/gitweb
Alias /gitweb /usr/share/gitweb


<Directory /usr/share/gitweb>
  AssignUserId gitolite gitolite
  Options FollowSymLinks +ExecCGI
  AddHandler cgi-script .cgi


  AuthUserFile /etc/apache2/gitweb.htpasswd
  AuthName "GitWeb"
  AuthType Basic
  require valid-user
  Order allow,deny
  Allow from 127.0.0.0/255.0.0.0 10.0.0.0/8 192.168.0.0/16 ::1/128
  satisfy any
</Directory>


$ sudo htpasswd -cb /etc/apache2/gitweb.htpasswd account password


有興趣的可以再改 code : /usr/share/gitweb/gitweb.cgi ,把網頁上顯示的擁有者改成 gitolite creator:


sub git_get_project_owner {
  my $project = shift;
  my $owner;


  return undef unless $project;
  $git_dir = "$projectroot/$project";


  if (!defined $gitweb_project_owner) {
    git_get_project_list_from_file();
  }


  if (exists $gitweb_project_owner->{$project}) {
    $owner = $gitweb_project_owner->{$project};
  }
  if (!defined $owner){
    $owner = git_get_project_config('owner');
  }
  if (!defined $owner) {
    if( open(GLCreator, "$git_dir/gl-creator" ) ) {
      $owner = '';
      while(<GLCreator>) {
        $owner .= $_;
      }
      close(GLCreator);
    }
  }
  if (!defined $owner) {
    $owner = get_file_owner("$git_dir");
  }


  return $owner;
}


另外還可以調整哪些 repos 不顯示,如 gitolite-admin.git 等


安裝 NIS Server:


$ sudo apt-get install nis
$ sudo vim /etc/default/nis
NISSERVER = master
$ sudo vim /etc/defaultdomain
$ sudo service portmap start ; sudo service ypbind start ; sudo service ypserv start ; sudo service yppasswdd start ; sudo service ypxfrd start
$ sudo /usr/lib/yp/ypinit -m
$ sudo make -C /var/yp


每次更新 /etc/passwd, /etc/group, …
需執行 $ sudo make -C /var/yp


註:Ubuntu sudoers 預設有開放 admin group 使用,所以只須建立 admin group 後,把管理者加進去即可在各台 NIS Client 使用。 另外,嚴謹的 NIS Master 也該限制到底誰可以來用 XD 此處先不管


安裝 Dropbox:


$ sudo mkdir -p /data/dropbox
$ sudo adduser --quiet --gecos "" -disabled-login --home /data/dropbox dropbox
$ sudo chown -R dropbox:dropbox /data/dropbox
$ sudo su - dropbox
$ whoami
dropbox
$ wget -O dropbox.tar.gz "http://www.dropbox.com/download?plat=lnx.x86_64"
$ tar -xvf dropbox.tar.gz
~/.dropbox-dist/dropboxd
This client is not linked to any account...
Please visit https://www.dropbox.com/cli_link?host_id=########################## to link this machine.
...
Client successfully linked, Welcome Developer!
$ exit


$ groups
xxxx admin
$ wget -O /tmp/dropbox-script https://gist.github.com/raw/861875/c9a585ec7da42ca9a857ef0987f1ccf765431d70/dropbox
$ sudo mv /tmp/dropbox-script /etc/init.d/dropbox
$ sudo chmod +x /etc/init.d/dropbox
$ sudo update-rc.d dropbox defaults
$ sudo vim /etc/group
dropbox:x:1006:dropbox
$ sudo /etc/init.d/dropbox start
$ sudo /etc/init.d/dropbox status
dropboxd for USER dropbox: running (pid 32693)
$ sudo su - dropbox
$ mkdir -p ~/Dropbox/service/redmine ~/Dropbox/service/gitolite
$ ln -s /data/gitolite/repositories ~/Dropbox/service/gitolite/repositories
$ ln -s /data/redmine/redmine/db ~/Dropbox/service/redmine/db
$ ln -s /data/redmine/redmine/files ~/Dropbox/service/redmine/files


如此下來的心得嘛...這個 dropbox 備份只是剛好玩玩而已 XD 設定完就永遠都不會碰它了吧 :P


[Linux] 安裝 Redmine @ Ubuntu 12.04 64Bit


圖:Wikipedia


在 Ubuntu 用 apt-get 安裝 Redmine 並沒有很難:


$ sudo apt-get install apache2 libapache2-mod-passenger git subversion cvs mercurial redmine
$ sudo ln -s /usr/share/redmine/public /var/www/redmine
$ sudo vim /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
  PassengerDefaultUser www-data
  PassengerRoot /usr
  PassengerRuby /usr/bin/ruby
</IfModule>
$ sudo vim sudo vim /etc/apache2/sites-available/default
<Directory /var/www/redmine>
  RailsBaseURI /redmine
  PassengerResolveSymlinksInDocumentRoot on
</Directory>
$ sudo service apache2 reload


之後就可以用 http://localhost/redmine 逛了。此時的 rails 、 redmine 版本:


$ dpkg -l | grep redmine
ii redmine 1.3.2+dfsg1-1ubuntu1 flexible project management web application
ii redmine-sqlite 1.3.2+dfsg1-1ubuntu1 metapackage providing sqlite dependencies for Redmine
$ dpkg -l | grep rails
ii  ruby-rails-2.3 2.3.14-2 MVC ruby based framework geared for web application development


接著就要面對 Rails' Remote Code Execution Vulnerability Explained 問題了。所以,接下來就透過 gem 裝 rails 且從 github 取 redmine 安裝的筆記 :P


$ sudo apt-get install apache2 libapache2-mod-passenger git subversion cvs mercurial build-essential apache2-prefork-dev libaprutil1-dev libapr1-dev libcurl4-openssl-dev ruby-rvm ruby-dev gem libmagickwand-dev sqlite3 sqlite3-doc libsqlite3-ruby libsqlite3-dev
$ sudo gem install rails bundler passenger
$ sudo passenger-install-apache2-module
$ sudo vim /etc/apache2/mods-available/passenger.load
#LoadModule passenger_module /usr/lib/apache2/modules/mod_passenger.so
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-3.0.19/ext/apache2/mod_passenger.so
$ sudo vim /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
  #PassengerRoot /usr
  #PassengerRuby /usr/bin/ruby
  PassengerRoot /var/lib/gems/1.8/gems/passenger-3.0.19
  PassengerRuby /usr/bin/ruby1.8
</IfModule>

$ sudo mkdir -p /data/webapp
$ git clone git://github.com/redmine/redmine.git /data/redmine
$ cd /data/redmine
$ git branch local-config
$ git checkout local-config
$ vim config/database.yml
production:
  adapter: sqlite3
  database: db/redmine.db

development:
  adapter: sqlite3
  database: db/redmine.db
$ bundle install --without development test
$ ruby script/about
...
Environment:
  Redmine version                          2.2.2.devel
  Ruby version                             1.8.7 (x86_64-linux)
  Rails version                            3.2.11
  Environment                              production
  Database adapter                         SQLite
...
$ rake generate_secret_token
$ RAILS_ENV=production rake db:migrate
$ RAILS_ENV=production rake redmine:load_default_data
$ mkdir public/plugin_assets


此時,就可以透過 ruby webrick 簡易測試:


$ cd /data/redmine
$ ruby script/rails server webrick -e production


但終極目標還是跟 apache2 綁在一起 :P 這也是為何會安裝 passenger/libapache2-mod-passenger 這種東西,接著參考強者青蛙的 Virtualhost 設定方式:


$ mkdir /data/webapp
$ cd /data/webapp
$ ln -s ../redmine/public redmine
$ sudo vim /etc/apache2/conf.d/redmine 
<Virtualhost *>
  DocumentRoot /data/webapp
  RailsBaseURI /redmine
  <Directory /data/webapp/redmine>
    AllowOverride all
    Options -MultiViews
  </Directory>
</Virtualhost>
$ cd /data/redmine
$ sudo chown -R www-data:www-data files log tmp public/plugin_assets
$ sudo chmod -R 755 files log tmp public/plugin_assets
$ sudo service apache2 restart


補充:


使用 <Virtualhost *> 用法時,當啟用 http/https 後,我導致 https 出錯 :P 解法就是不要用 <Virtualhost *> 用法,而是直接去修改指定的地方,如 default 或 default-ssl 檔:


$ sudo vim /etc/apache2/conf.d/redmine
Alias /redmine "/path/redmine/public"
RailsBaseURI /redmine
<Directory /path/redmine/public>
  AssignUserId redmine service
  PassengerUploadBufferDir /path/redmine/PassengerUploadBufferDir
  AllowOverride all
  Options -MultiViews
</Directory>


接著再到 DocumentRoot 建立 symbolic link:


$ cd /var/www
$ ln -s /path/redmine/public redmine
$ mkdir -p /path/redmine/PassengerUploadBufferDir
$ chown redmine:service /path/redmine/PassengerUploadBufferDir


2013年1月22日 星期二

[Rails] no such file to load -- dispatcher @ Ubuntu 12.04

no such file to load -- dispatcher


Error message:no such file to load -- dispatcher


Ubuntu 系統內建的 libapache2-mod-passenger 版本不夠新,以至於有些 modules 找不到,解法就是改用 gem 來安裝


$ sudo apt-get libapache2-mod-passenger
$ cat /etc/apache2/mods-available/passenger.load
LoadModule passenger_module /usr/lib/apache2/modules/mod_passenger.so
$ cat /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
  PassengerRoot /usr
  
PassengerRuby /usr/bin/ruby
</IfModule>
$ dpkg -l | grep passenger
ii libapache2-mod-passenger 2.2.11debian-2 Rails and Rack support for Apache2


改用 gem 裝法:


$ sudo gem install passenger
$ sudo passenger-install-apache2-module
$ sudo vim /etc/apache2/mods-available/passenger.load
#LoadModule passenger_module /usr/lib/apache2/modules/mod_passenger.so
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-3.0.19/ext/apache2/mod_passenger.so
$ sudo vim /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
  #PassengerRoot /usr
  #PassengerRuby /usr/bin/ruby
  PassengerRoot /var/lib/gems/1.8/gems/passenger-3.0.19
  PassengerRuby /usr/bin/ruby1.8
</IfModule>
$ sudo service apache2 reload
$ sudo gem list | grep passenger
passenger (3.0.19)


[OSX] 製作開機隨身碟 @ Mac 10.8.2

> hdiutil convert -format UDRW -o /tmp/ubuntu ./ubuntu-12.04.1-server-amd64.iso

> diskutil unmountDisk /dev/disk1
> sudo dd if=/tmp/ubuntu.dmg of=/dev/rdisk1 bs=1m
> diskutil eject /dev/disk1

其中 /dev/disk1 和 /dev/rdisk1 都是指隨身碟裝置位置,可用 diskutil list 來查詢

2013年1月20日 星期日

Sony DSC-RX100

RX100-01


去年遊香港時,跟友人借了一台 Sony Nex-3 伴遊,雖然夜景繁榮到用智慧手機也有不錯效果,但高級一點的相機拍出來真的有不一樣的水準,因此埋下買類單眼的欲望。原先的目標和預算擺在 NEX-F3 或 NEX-5R 這類新款且可以換鏡頭的類單眼,只是左思右想後,還是選擇了 Sony RX100 這款,主要理由是旅行中不想因為鏡頭而多背一個包包,再加上買了可以換鏡頭的相機後,隱性的鏡頭預算也會慢慢在未來浮現出來 XD 最後在友人協助下,快速地敗了 Sony RX100 啦!


這次買他的時機選在年初 XXX 百貨公司周年慶,滿 1 萬送 500禮券,刷卡滿一萬送 200 禮券,接著還有 ooo 理由送 100 禮券,總之最後就拿到 1300 禮券 + 信用卡1%現金折扣。當時選在 Sony Center 下手,好處是維修方便。最近也才稍微了解網路上的 "平行輸入" 的意思,就是所謂的水貨,水貨跟公司貨的主要差別是維修問題,賣水貨的店家通常是沒有代理權的,因此維修有問題時會比較麻煩,有的甚至要消費者自行處理。有一派的認為機身可以考慮水貨、鏡頭買公司貨,不曉得理由是不是鏡頭可以用來傳家、機身壽命短?總之這次選擇公司貨,其中最大的理由是個人對相機不熟 XD 其次是水貨跟公司貨價差不到 2000 元,再加上這次除了百貨公司節慶外,Sony 自家也有打出購買送東西的優惠(一顆原廠電池+SF-16UX 記憶卡),在這樣的條件下就完全不用考慮水貨。但小提一下,最佳購入 Sony RX100 的時機是2012夏天預購活動,還會多送原廠皮套(原價2千附近)


RX100-03


故上圖主要有兩樣還是額外購買的,那就是充電器跟皮套。兩樣加起來不用 850 元 XD 就先用看看吧!


RX100-04


至於入手好的心得嘛,除了照片在自動模式隨便拍隨便好看外,另一個則是在手動模式中,透過機身右下方的轉盤,還算方便地調整快門跟光圈數值,是我覺得還滿不錯的地方。優點嘛,就真的達到輕便、又可以隨便拍隨便好看,如果有想要手動調快門、光圈和 ISO 值也能把玩,缺點嘛,除了價格外,就是預設拍出來的照片檔案大小都以 5MB 起跳,這樣拍100張就吃掉500MB的容量了 XD 未來得要勤勞整理照片了。最後補個兩張自動拍攝的夜景:


RX100 RX100


2013年1月16日 星期三

iOS 開發筆記 - 備份還原匯出 Xcode 所需鑰匙(keys)

xcode_profile_export03


昨晚把 Mac mini 重灌了,灌完才想到 Xcode 開發環境的問題,所幸現在非常方便,直接在 Xcode 操作就能很順地匯出與匯入,因此,我只需要從另一台 Mac OS X 裡,透過 Xcode 把開發要用的 public key/private key 匯出即可,早期的作法可是透過 Keychains 各別匯出。


開啟 Xcode -> Windows -> Organizer -> Team -> 下方有 Export 可選:


xcode_profile_export01


設定打包的密碼:


xcode_profile_export02


如此一來就完成匯出了,至於匯入的方式,可以參照上述 Import 或是丟到另一台電腦直接點選也行。


2013年1月14日 星期一

[C] 一行指令建立帳號、更新密碼 (需setuid) @ Ubuntu 12.04

最近有需求要幫系統加入新增帳號、修改帳密的流程,所以想著想著,就蹦出了一支 C 程式,透過 setuid 的方式,終於可以完成這項工作 (經高手指點,bash 無法被 setuid 使用,也就是設定完後無法達成想要的結果,可以在 script 裡打 id 指令)


相關 Unix 指令回顧:



  • 一行指令新增帳號


    • $ sudo adduser --quiet --gecos "" --disabled-login --no-create-home --shell "/usr/sbin/nologin" tester


  • 一行指令更新密碼


    • $ echo "tester:password" | chpasswd


  • 檢查使用者是否存在


    • $ getent passwd tester



程式碼:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include <sys/types.h>
#include <unistd.h>


#define CMD_ADDUSER "sudo adduser --quiet --gecos \"\" --disabled-login --no-create-home --shell \"/usr/sbin/nologin\" "
#define CMD_CHECK_USER_EXISTS "getent passwd"
#define MAX_BUFFER_LINE 1024
int main(int argc, char *argv[])
{
   char buf[MAX_BUFFER_LINE+1], pass[MAX_BUFFER_LINE+1], *cmd;
   FILE *fp;
   setuid(0); // use root
   //system("id");
   if( argc < 3 )
   {
      fprintf( stderr, "Usage> %s username [ - | password]\n\t%s username -\t\t(read password from stdin)\n\t%s username password\n\n\tothers: sudo chown root %s && sudo chmod 4755 %s\n" , argv[0], argv[0], argv[0] , argv[0], argv[0] );
      exit(1);
   }


   memset( buf, 0, MAX_BUFFER_LINE + 1);
   memset( pass, 0, MAX_BUFFER_LINE + 1);
   if( argv[2][0] != '-' )
   {
      strncpy( pass, argv[2], MAX_BUFFER_LINE );
      pass[MAX_BUFFER_LINE] = '\0';
   }
   else if( !feof( stdin ) && fgets( pass, MAX_BUFFER_LINE, stdin ) > 0 )
   {
      //printf("Pass:[%s]\n",pass);
   }


   if( strlen(pass) < 1 )
   {
      fprintf( stderr, "Error @ Init: password is empty\n" );
      exit(1);
   }

   // check account exists
   cmd = buf;
   snprintf( buf, MAX_BUFFER_LINE, "%s %s", CMD_CHECK_USER_EXISTS, argv[1] );
   fp = popen( cmd , "r" );


   memset( buf, 0, MAX_BUFFER_LINE + 1);
   fgets( buf, MAX_BUFFER_LINE, fp );
   pclose(fp);


   if( !strlen(buf) ) // create the account if not exists
   {
      // add account
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      cmd = buf;
      snprintf( buf, MAX_BUFFER_LINE, "%s %s", CMD_ADDUSER, argv[1] );
      pclose( popen( cmd , "r" ) );


      // query the account
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      cmd = buf;
      snprintf( buf, MAX_BUFFER_LINE, "%s %s", CMD_CHECK_USER_EXISTS, argv[1] );
      fp = popen( cmd , "r" );
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      fgets( buf, MAX_BUFFER_LINE, fp );
      pclose(fp);

      if( !strstr( buf, argv[1] ) ) // user is not created
      {
         printf("Error @ create an account: user cannot be created\n");
         exit(1);
      }


      // change password
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      cmd = buf;
      snprintf( buf, MAX_BUFFER_LINE, "echo \"%s:%s\" | chpasswd ", argv[1] , pass );
      fp = popen( cmd , "r" );
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      fgets( buf, MAX_BUFFER_LINE, fp );
      pclose(fp);
      if( !strlen(buf) )
         printf("OK\n");
   }
   else if( strstr( buf, "/bin/false" ) || strstr( buf, "/nologin" ) ) // change password
   {
      // change password
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      cmd = buf;
      snprintf( buf, MAX_BUFFER_LINE, "echo \"%s:%s\" | chpasswd ", argv[1] , pass );
      fp = popen( cmd , "r" );
      memset( buf, 0, MAX_BUFFER_LINE + 1);
      fgets( buf, MAX_BUFFER_LINE, fp );
      pclose(fp);
      if( !strlen(buf) )
         printf("OK\n");
   }
   else
   {
      printf("SKIP @ cannot change the password for '%s'.\n", argv[1]);
   }
   return 0;
}


暫時把這隻程式定位:



  • 可以新增 nologin 帳號

  • 可以修改 nologin 帳號的密碼


用法:


$ gcc main.c
$ sudo chown root ./a.out && sudo chmod 4755 ./a.out
$ ./a.out new_account new_password


若要在安全一下,可以在設定 uid 範圍,以避免改到一些系統帳號。


2013年1月11日 星期五

[PHP] 處理 PHP 5.3 php-cgi (FastCGI) 掛掉的現象 @ ARM Linux

ab_-n_3000_-c_20


最近的小任務是要在 ARM Linux 版上配置 PHP 環境,所幸同事都先編好,讓我無痛移植 XD 只是在設定過程中,雖然沒發現 php-fpm 存在(忘了編?),但至少還有隻 php-cgi 可以把玩。


既然在板子上,勢必要測試一下堪用度,所以就用 index.php ,內文用 "<?php  phpinfo(); " ,接著找另一台 Linux 跑 ab test:


terminal 1:
$ php/bin/php-cgi -b 127.0.0.1:9000


terminal 2:
$ ab -n 300 -c 20 http://10.0.0.168/index.php


這段 ab 跑下去,嗯,感覺良好,接著我再跑一次 ab 後,在 terminal 1 得 php-cgi 就跳出了(crash?),並且看到 ab report 有 Failed requests (此例是 -n 600):


Concurrency Level: 20
Time taken for tests: 1.235 seconds
Complete requests: 600
Failed requests: 100
(Connect: 0, Receive: 0, Length: 100, Exceptions: 0)
Write errors: 0
Non-2xx responses: 600
Total transferred: 160500 bytes
HTML transferred: 66200 bytes
Requests per second: 485.68 [#/sec] (mean)
Time per request: 41.180 [ms] (mean)
Time per request: 2.059 [ms] (mean, across all concurrent requests)
Transfer rate: 126.87 [Kbytes/sec] received


接著再網路打滾一陣子,看到的解法很多是 crontab 定期開後,有點失落 XD 最後當我準備編 php-fpm 前,看到對岸好文 启动 PHP 内置 FastCGI Server 的脚本 後,搞懂原理後,就迎刃而解啦!


最後使用的參數:


terminal 1:
$ export PHP_FCGI_MAX_REQUESTS=300;
$ export PHP_FCGI_CHILDREN=4;
$ /path/php/bin/php-cgi -b 127.0.0.1:9000


terminal 2:
$ ab -n 3000 -c 20 http://10.0.0.168/index.php
...
Document Path: /index.php
Document Length: 25 bytes


Concurrency Level: 20
Time taken for tests: 6.630 seconds
Complete requests: 3000
Failed requests: 0
Write errors: 0
Non-2xx responses: 3000
Total transferred: 537000 bytes
HTML transferred: 75000 bytes
Requests per second: 452.51 [#/sec] (mean)
Time per request: 44.198 [ms] (mean)
Time per request: 2.210 [ms] (mean, across all concurrent requests)
Transfer rate: 79.10 [Kbytes/sec] received


Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.3 0 4
Processing: 18 43 4.5 43 93
Waiting: 18 43 4.5 43 92
Total: 22 44 4.5 44 93
ERROR: The median and mean for the initial connection time are more than twice the standard
deviation apart. These results are NOT reliable.


Percentage of the requests served within a certain time (ms)
50% 44
66% 44
75% 45
80% 45
90% 46
95% 46
98% 59
99% 68
100% 93 (longest request)


嘖嘖,堪用堪用啊。而原先 500 個 requests 就會掛得原因是預設 PHP_FCGI_MAX_REQUESTS=500 啦,總之,如此一來,就可以繼續用 PHP 把玩了。而要重現處理完 PHP_FCGI_MAX_REQUESTS 就關掉的話,就只需設定 PHP_FCGI_CHILDREN=0 即可


2013年1月4日 星期五

[Linux] 把玩 PAM 筆記 (Dovecot、Postfix 與 POP3/IMAP 認證為例) @ Ubuntu 12.04

PAM 全名為 Pluggable Authentication Module,可用在跨系統的帳號認證整合。有幸從同事那邊得到這邊的資訊,所以來筆記一下。更多 PAM 資訊可自行 Google 一下。在此筆記把玩的成果。


使用情境:



  • 一個提供帳號登入的 Web Service (A系統)

  • 一個提供 POP3/IMAP/SMTP 服務的 Mail Server (B系統)


如果想要提供 B 系統用 A 系統的帳號來做認證時,這時候就可以採用 PAM 來處理,例如在帳密登入流程中,加入 A系統認證流程。(註:此例以 Mail Server 舉例不甚理想,因為 PAM 無法解決 Mail Server 收信的問題,也就是無法提供 user list 讓 Mail Server 驗證帳號是否有效,在此僅用在登入帳密範例)


作法:



  • 實作 pam_yychecker.so 模組,用來認證 A 系統帳號

  • 使用 Dovecot POP3/IMAP 服務架構,增加 pam_yychecker.so 作為帳密認證


實作 pam_yychecker.so 模組:


pam_yychecker.c:(此例為任何帳密進來都 pass,而認證方面應修改 pam_sm_authenticate 函數,把取到的帳密到 A 系統認證)


// 參考 http://www.openpam.org/browser/openpam/trunk/modules/pam_unix/pam_unix.c


$ sudo apt-get install gcc libpam-dev make
$ gcc -shared -lpam -fPIC -Wall pam_yychecker.c -o pam_yychecker.so
$ sudo install pam_yychecker.so /lib/x86_64-linux-gnu/security


#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#include <security/pam_modules.h>
#include <security/pam_ext.h>


#include <syslog.h>


#ifndef PAM_EXTERN
#define PAM_EXTERN
#endif


PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
    int i;
   const char *user;
   char *password;


   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);


   /* identify user */
   if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS)
      return PAM_AUTH_ERR;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate] get account:[%s]", user);


   /* get password */
   if (pam_get_authtok(pamh, PAM_AUTHTOK, (const char **)&password, NULL) != PAM_SUCCESS)
      return PAM_AUTH_ERR;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate] get password:[%s]", password);

   //
   //  add some checker
   // ...


   return PAM_SUCCESS;
}


PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_setcred]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}


PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_acct_mgmt]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}


PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_open_session");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}


PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_close_session");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}


PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_chauthtok]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SERVICE_ERR);
}


使用 pam_yychecker.so 模組:


在此挑 Dovecot IMAP/POP3 為例:


@ /etc/pam.d/yychecker


auth required pam_checker.so debug  # "debug" 可讓 pam_yychecker.c 中的 syslog(LOG_DEBUG, "" ) 起作用,在 /var/log/syslog 可看見訊息
account required pam_checker.so debug arg1=val1  # arg1=val1 純粹測試用,可在 pam_yychecker.c 看到參數傳進來


@ /etc/dovecot/conf.d/99-yychecker.conf


auth_mechanisms = plain login
protocols = pop3 imap
mail_location = maildir:/home/vmail/%u/Maildir
auth_debug=yes # 更多豐富的訊息
passdb {
    driver = pam
    args = yychecker   # /etc/pam.d/yychecker 
}
userdb {
    driver = static
    args = uid=vmail gid=vmail home=/home/vmail/%u
}


透過撰寫上述兩個檔案後,將 dovecot 重開後 (/etc/init.d/dovecot restart),可以用 telnet/openssl 訪問 pop3/imap 服務:


IMAP:


$ openssl s_client -connect localhost:993
...
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
- login account password
- OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS] Logged in
- logout
* BYE Logging out
- OK Logout completed.
closed


POP3:


$ openssl s_client -connect localhost:995
...
+OK Dovecot ready.
user account
+OK
pass password
+OK Logged in.
quit
+OK Logging out.
closed


上述例子藍色是輸入的指令,其中 - login 後面依序接帳號跟密碼,由於在 pam_yychecker.c 之 pam_sm_authenticate 函數中,都是回傳 PAM_SUCCESS ,因此無論哪組(不存在的)帳密都會因此 PASS 的


此外還可以翻一下 /var/log/syslog 得知一下訊息:(此次帳號為test, 密碼為test)


dovecot: auth: Debug: Loading modules from directory: /usr/lib/dovecot/modules/auth
ovecot: auth: Debug: auth client connected (pid=14779)
dovecot: auth: Debug: client in: AUTH#0111#011PLAIN#011service=pop3#011secured#011lip=127.0.0.1#011rip=127.0.0.1#011lport=995#011rport=44325#011resp=AHRlc3QAdGVzdA==
dovecot: auth-worker: Debug: Loading modules from directory: /usr/lib/dovecot/modules/auth
dovecot: auth-worker: Debug: pam(test,127.0.0.1): lookup service=dovecot
dovecot: auth-worker: Debug: pam(test,127.0.0.1): #1/1 style=1 msg=Password:
dovecot: auth-worker: pam(test,127.0.0.1): pam_authenticate() failed: Authentication failure (password mismatch?) (given password: test)
dovecot: auth-worker: Debug: pam(test,127.0.0.1): lookup service=yychecker
auth: @ yychecker [pam_sm_authenticate]
auth: argv[0]=[debug]
auth: @ yychecker [pam_sm_authenticate] get account:[test]
dovecot: auth-worker: Debug: pam(test,127.0.0.1): #1/1 style=1 msg=Password:
auth: @ yychecker [pam_sm_authenticate] get password:[test]
auth: @ yychecker [pam_sm_acct_mgmt]
auth: argv[0]=[debug]
auth: argv[1]=[arg1=val1]
dovecot: auth: Debug: client out: OK#0111#011user=test
dovecot: auth: Debug: master in: REQUEST#0112190999553#01114779#0111#011a7a91b0fc5002a5eb77113ceb9304c7c
dovecot: auth: Debug: passwd(test,127.0.0.1): lookup
dovecot: auth: passwd(test,127.0.0.1): unknown user
dovecot: auth: Debug: master out: USER#0112190999553#011test#011uid=1002#011gid=1003#011home=/home/vmail/test
dovecot: pop3-login: Login: user=<test>, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=14783, TLS
dovecot: pop3(test): Disconnected: Logged out top=0/0, retr=0/0, del=0/0, size=0


從 syslog 大概可以看到,認證流程一開始是內建的帳密系統(/etc/pam.d/dovecot敘述),後來接著走 (/etc/pam.d/yychecker),接著依序走過 pam_sm_authenticate 和 pam_sm_acct_mgmt 函數。如果未實作 pam_sm_acct_mgmt 或 pam_sm_acct_mgmt 回傳 PAM_AUTH_ERR 時,也看到認證失敗的訊息。因此,對於 dovecot PAM 的使用中,除了一開始在 pam_sm_authenticate 就處理帳號過濾外,也可在 pam_sm_acct_mgmt 進行帳號的過濾。


原先是打算利用 PAM 來提供 Mail server (B系統) 使用 Web service (A系統) 帳號進行收信、送信,但測試的結果,盡管可以讓 POP3/IMAP/SMTP 認證都搞定(SMTP還須額外設定,上述範例設定檔還不夠),但在 Mail Server 之 deliver 上,採用 Dovecot LDA 卻碰到 userdb static 問題,也就是無法提供帳號認證,導致 Mail server 無把將 A 系統帳號視為有效帳號。也有可能是 PAM 寫錯或調教經驗上淺,玩了兩天還沒有好的成果。目前的結論是...還是乖乖用 dovecot userdb 其他要製造 user list 清單的方式吧 :P 在此就先筆記這幾天亂玩的東西。