10/23/2008

ZFS-Solaris 10的新檔案系統 (一)

Sun Solaris 10作業系統 (Solaris OS) 裡新的ZFS檔案系統於2006年6月正式推出。昇陽號稱ZFS是目前地球上最強的檔案系統,其創新的技術推翻你對檔案系統管理的概念。根據昇陽的文件它具有如下的功能。

* 管理簡單 ZFS 自動化並整合複雜的儲存管理概念,降低 80% 管理上的經常性耗用時間。
* 確實的資料完整性 ZFS 用 64 位元的總和檢查保護所有資料,能夠偵測並修正無訊資料損毀的情況。
* 無限延伸性 ZFS 是世界第一個 128 位元檔案系統,其儲存容量為 32 或 64 位元系統的 1600 萬兆倍。
* 極速效能 作業事件模型消除了傳統式發送 I/O 的順序限制,大幅提升效能。

看了這麼多,還是快開始進入我們的重點吧!要使用ZFS你得要先有一個Solaris 10的系統。不管是SPARC版或是x86版的都可以,但是一定要是Solaris 10 06/06(Update 2)這個版本以後的。再來最好有多顆硬碟,而且每顆硬碟要大於128MB(現在去那兒找比128MB還小的呀)。最後是RAM越多越好,官方說法是512MB是起碼,1G是基本。不過實際上我在VMware下只開了256MB測試也是跑的很順暢。

在一切都準備就緒後就可以開始了。首先看一下我們所安裝的Solaris版本是不是正確的。

# cat /etc/release
Solaris 10 6/06 s10x_u2wos_09a X86
Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
Use is subject to license terms.
Assembled 09 June 2006

是2006年6月的版本,不然你也可以執行zfs這個指令試看看。在我測試的環境裡,我用了6個大小都為1GB的SCSI硬碟,分別是c2t0 ~ c2t5

# echo | format
Searching for disks...done


AVAILABLE DISK SELECTIONS:
0. c0d0
/pci@0,0/pci-ide@7,1/ide@0/cmdk@0,0
1. c2t0d0
/pci@0,0/pci1000,30@10/sd@0,0
2. c2t1d0
/pci@0,0/pci1000,30@10/sd@1,0
3. c2t2d0
/pci@0,0/pci1000,30@10/sd@2,0
4. c2t3d0
/pci@0,0/pci1000,30@10/sd@3,0
5. c2t4d0
/pci@0,0/pci1000,30@10/sd@4,0
6. c2t5d0
/pci@0,0/pci1000,30@10/sd@5,0
Specify disk (enter its number): Specify disk (enter its number):

zfs標榜管理簡單所以指令也相對的少,只有二個指令。我們先介紹第一個zpool。zpool會建立一個storage pool,也會自動產生zfs 檔案系統和掛載點,並且會自動的掛載該檔案系統。如下面的例子會產生一個叫mypool的storage pool和在根目錄(/)產生mypool這個目錄

# zpool create mypool c2t0d0
# ls -ld /mypool
drwxr-xr-x 2 root sys 2 Jul 21 16:40 /mypool

這個新建的mypool大小為1GB,也就是c2t0d0的大小。讓我們在上面建個檔案試看看。

# mkfile 100m testfile
# df -k /mypool
Filesystem kbytes used avail capacity Mounted on
mypool 999424 81455 917906 9% /mypool

mypool這是一個storage pool,同時它也是一個可用的檔案系統,你可以在上面建目錄、寫入檔案,做任何在傳統的UFS檔案系統可以做的事。等等,就只是這樣子嗎?當然不是,ZFS允許你在storage pool上面再建不同的檔案系統(File System)。下個的範例會使用第二個也是最後一個ZFS相關指令 - zfs。我們會使用zfs這個指令在mypool下建了一個叫data的檔案系統,同時它也會自動地建目錄和掛載。

-bash-3.00# zfs create mypool/data
-bash-3.00# df -k
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0d0s0 7730925 2908589 4745027 39% /
/devices 0 0 0 0% /devices
ctfs 0 0 0 0% /system/contract
proc 0 0 0 0% /proc
mnttab 0 0 0 0% /etc/mnttab
swap 561064 644 560420 1% /etc/svc/volatile
objfs 0 0 0 0% /system/object
/usr/lib/libc/libc_hwcap1.so.1
7730925 2908589 4745027 39% /lib/libc.so.1
fd 0 0 0 0% /dev/fd
swap 560452 32 560420 1% /tmp
swap 560448 28 560420 1% /var/run
mypool 999424 102449 896878 11% /mypool
mypool/data 999424 24 896878 1% /mypool/data

看到上面的輸出了嗎?如果你仔細看會發現ZFS的檔案系統和UFS不太一樣,首先是Filesystem這個欄位,傳統上我們會看到的是device的路徑,如:/dev/dsk/c0d0s0,而ZFS則為storage pool的名稱。再來看/mypool跟/mypool/data,二個的大小和可用空間都相同,分別為999424及896878。因為目前我們還沒有針對各別的空間使用做限制,所以/mypool跟/mypool/data的空間是共用的,一個用的多另外那一個就用的少。

目前已經大致介紹了storage pool和ZFS檔案系統的建立。接下來會再對storage pool的使用做更進一步的說明。

上面的範例我們建了一個叫mypool的storage pool,並且指定c2t0d0給它。回憶一下我們剛所用的指令。

-bash-3.00# zpool create mypool c2t0d0 

其中c2t0d0是一個未經solaris 格式化過的硬碟(未使用format這個指令去做過任何的處理)。而也不需要事先處理,如果指定的硬碟已經有分區的話,則整個硬碟的內容會被清除。在指定要使用的裝置時,除了用cxtxdx這種短名外,你也可以給全路徑的名稱, 如/dev/dsk/c2t0d0。這是把一整個硬碟空間都給這個storage pool使用,如果你想把已經劃好分區(slice)的硬碟中某個未使用的分區拿來用也可以,只要將裝置名稱改為cxtxdxsx就可以了。如果你有使用SVM或是VxVM也可以將所做出來的卷(volume)拿來使用。這些裝置在zfs下我們統稱為虛擬裝置(virtual device)。 下面所列的都是合法的裝置名。

c2t0d0
/dev/dsk/c2t0d0
c2t0d0s0
/dev/dsk/c2t0d0s0
/dev/md/dsk/d10
/dev/vx/dsk/datadg/vol01

除了最基本的storage pool外,其實zfs還提供了mirror和RAID-Z二種型式的storage pool。RAID-Z其實是RAID 5的一種變形,傳統的RAID 5在寫入資料時,如果遇到電力中斷有可能會造成寫入的資料並不完整。而zfs則透過將檔案系統及實體磁碟結合,並使用可變長度的跨距單位(variable-width RAID stripes),來避免這種情形的發生。
以mirror(RAID 1)的方式建立storage pool時,需要使用關鍵字mirror並且指定二個以上的虛擬裝置。而在建立RAID-Z時,則使用raidz 這個關鍵字並且指定二個以上的虛擬裝置(這裡跟RAID 5最少需使用三個磁碟不同,但可用空間都是N-1顆)。

zpool create datapool mirror c0t0d0 c0t1d0
zpool create datapool mirror c0t0d0 c0t1d0 mirror c1t0d0 c1t1d0
zpool create datapool raidz c0t0d0 c0t1d0
zpool create datapool raidz c0t0d0 c0t1d0 mirror c1t0d0 c1t1d0

當在建立storage pool時可能會出現錯誤訊息,此時可用-f的參數來強制執行。如下列所要使用的c1t0d0已經含有ufs的檔案系統,所以在建立時會出現錯誤訊息,如果你確定你真的要這麼做,那可以使用-f來強制執行。當然原先ufs檔案系統上的資料也不復存在了。

#zpool create data c1t0d0
invalid vdev specification
use '-f' to override the following errors:
/dev/dsk/c1t0d0s0 contains a ufs filesystem
#zpool create -f data c1t0d0

因為建立storage pool對磁碟資料有無法復原的破壞性。所以在做這些動作之前,請先確定你的頭腦夠清醒,並避免任何可能發生的錯誤。如果你不是很確定你的指令的正確性時,可以加上-n來做個檢查。通常最容易發生的是指定到使用中的磁碟裝置或是pool的名稱重複。

-bash-3.00# zpool create -n mypool c2t0d0
invalid vdev specification
use '-f' to override the following errors:
/dev/dsk/c2t0d0s0 is part of active ZFS pool mypool. Please see zpool(1M).

前面提到在建立storage pool時會在根目錄(/)建立一個同名的目錄以供掛載之用。可是這樣子使用很不方便,想像一下,如果我想要在/oradata下建立一個db1時那就不合需求了。其實在建立storage pool時可以手動指定掛載點,而不必一定要掛載在根目錄下面。下面的範例建立一個db1的pool並將其掛載在/oradata下。

-bash-3.00# zpool create -m /oradata/db1 db1 c2t1d0
-bash-3.00# df -k /oradata/db1
Filesystem kbytes used avail capacity Mounted on
db1 999424 24 999342 1% /oradata/db1

學會建立storage pool當然也得要知道如何把不要的storage pool殺掉。當你不再需要某個storage pool的時候便需要將該pool殺掉, 好把空間釋放出來供未來使用。在建storage pool時使用了關鍵字create,要移除時使用的關鍵字則為destroy。同樣的如果在移除時發生問題,如某個磁碟裝置出現問題,則可以加上-f來強制移除。

-bash-3.00# zpool destroy db1
-bash-3.00# zpool destroy -f db1

當pool的空間已經不夠使用的時候該怎麼辦呢?這時候zfs的好處就出來了。使用zfs你可以動態的將磁碟裝置加入某個pool裡面,該pool 的可用空間馬上就會加大,不需要umount該目錄後再mount,而且只需要一個指令。下面的範例使用zpool add的指令新增一個磁碟c2t1d0 到mypool。從二個不同的df -k的輸出可以看出來可用空間加大了1G左右(這裡的每顆硬碟大小都為1G)。

-bash-3.00# df -k /mypool
Filesystem kbytes used avail capacity Mounted on
mypool 999424 102450 794442 12% /mypool
-bash-3.00# zpool add mypool c2t1d0
-bash-3.00# df -k /mypool
Filesystem kbytes used avail capacity Mounted on
mypool 2031616 102450 1826634 6% /mypool

從上面的範例可以看出來zpool add和zpool create的指令格式一樣,而且也都可以使用-f和-n。我們再來看個範例,這個例子我們將新建一個mirror的pool然後再將它加大,你可以看到解決空間不足的問題有多簡單只要你的硬碟夠多的話。

-bash-3.00# zpool create -f testpool mirror c2t2d0 c2t3d0
-bash-3.00# df -k /testpool
Filesystem kbytes used avail capacity Mounted on
testpool 999424 24 999349 1% /testpool
-bash-3.00# zpool add testpool mirror c2t4d0 c2t5d0
-bash-3.00# df -k /testpool
Filesystem kbytes used avail capacity Mounted on
testpool 2031616 24 2031445 1% /testpool

現在有一個情況,假設我有二個Oracle的資料庫要執行,分別是DB1和db2。而我希望它們的資料是放在/oradata/db1和/oradata/db2下。這個時候可以先建一個叫oradata的storage pool,然後再建二個ZFS的檔案系統DB1和DB2分別給二個oracle的資料庫使用。 建立檔案系統使用zfs create的指令,然後接poolname/fsname看起來會像這個樣子。
zfs create poolname/fsname
下面的範例建立了一個oradata的pool並在其下建了二個檔案系統。在檔檔案系統建立時系統會一併建立目錄並掛載起來。

# zpool create oradata c2t0d0
# zfs create oradata/db1
# zfs create oradata/db2
# df -k
Filesystem kbytes used avail capacity Mounted on
oradata 999424 27 999288 1% /oradata
oradata/db1 999424 24 999288 1% /oradata/db1
oradata/db2 999424 24 999288 1% /oradata/db2

這個時候問題又來了,DBA決定把DB1改叫prod;DB2改叫test,然後prod的使用空間應該會在600MB左右;而test所需的空間不定。這個時候該如何是好?首先我們先來解決第一個問題,改名字。
ZFS的設計理念之一是簡化管理,所以當建立pool或filesystem時都不需要自行建立分割區、格式化、建立目錄、掛載、編輯/etc/vfstab, 並且重開機時系統會自動把zfs格式的檔案系統自動掛載起來。所以我們不能用傳統的方式來把/oradata/db1改成/oradata/prod,必須使用zfs rename的指令來修改。
下面的範例解決了問題一。

# zfs rename oradata/db1 oradata/prod
# zfs rename oradata/db2 oradata/test
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
oradata 136K 976M 27.5K /oradata
oradata/prod 24.5K 976M 24.5K /oradata/prod
oradata/test 24.5K 976M 24.5K /oradata/test

第二個問題要怎麼解決呢?這個時候就得要介紹一下ZFS的屬性(properties),ZFS提供了許多有用的控制屬性,如壓縮(compression)、 空間限額(quota)...等,這些屬性是可設定的,也就是說你可以透過指令來改變這些屬性的值。另外有些屬性是唯讀的,主要是ZFS一些相關的統計資料,如類型(type)、己使用空間(used)...等。現在我們要用到的是空間限額(quota)來設定二個目錄可以使用的空間。在學會怎麼設定之前,我們先要知道如何得知各屬性的值。

# zfs get
missing property argument
usage:
get [-rHp] [-o field[,field]...] [-s source[,source]...]
...

The following properties are supported:

PROPERTY EDIT INHERIT VALUES

type NO NO filesystem | volume | snapshot
creation NO NO
used NO NO
available NO NO
referenced NO NO
compressratio NO NO <1.00x>
mounted NO NO yes | no | -
origin NO NO
quota YES NO | none
reservation YES NO | none
volsize YES NO
volblocksize NO NO 512 to 128k, power of 2
recordsize YES YES 512 to 128k, power of 2
mountpoint YES YES | legacy | none
sharenfs YES YES on | off | share(1M) options
checksum YES YES on | off | fletcher2 | fletcher4 | sha256
compression YES YES on | off | lzjb
atime YES YES on | off
devices YES YES on | off
exec YES YES on | off
setuid YES YES on | off
readonly YES YES on | off
zoned YES YES on | off
snapdir YES YES hidden | visible
aclmode YES YES discard | groupmask | passthrough
aclinherit YES YES discard | noallow | secure | passthrough

Sizes are specified in bytes with standard units such as K, M, G, etc.

zfs get不加其他參數時會列出所有可用的屬性,在EDIT欄的看到NO的話就表示該屬性的值是唯讀的,YES才是可以設定的。再下一欄繼承( INHERIT)表該屬性的值會不會限制其下再建立的目錄。VALUE欄表示該屬性的可能的值。我們以
quota YES NO | none
為例,quota這個屬性是可設定的、不能繼承、值可為大小(數字)或none。
再以quota為例,我們要如何得知quota的值呢?zfs get quota oradata其中quota為屬性名,oradata為pool的名字。其中的屬性名也可以用all來取得所有的屬性。

# zfs get quota oradata
NAME PROPERTY VALUE SOURCE
oradata quota none default

# zfs get all oradata
NAME PROPERTY VALUE SOURCE
oradata type filesystem -
oradata creation Fri Aug 4 14:44 2006 -
oradata used 136K -
oradata available 976M -
oradata referenced 27.5K -
oradata compressratio 1.00x -
oradata mounted yes -
oradata quota none default
oradata reservation none default
oradata recordsize 128K default
oradata mountpoint /oradata default
oradata sharenfs off default
oradata checksum on default
oradata compression off default
oradata atime on default
oradata devices on default
oradata exec on default
oradata setuid on default
oradata readonly off default
oradata zoned off default
oradata snapdir hidden default
oradata aclmode groupmask default
oradata aclinherit secure

在瞭解ZFS的屬性後,要解決第二個問題就很簡單了。只要使用zfs set設定quota這個屬性的值就可以了。

# zfs set quota=600M oradata/prod
# zfs get quota oradata/prod
NAME PROPERTY VALUE SOURCE
oradata/prod quota 600M local

另外還有一個屬性reservation(保留空間)。在前面提到過同一個pool的空間是共用的,不管你在pool之下建了多少個zfs的檔案系統。以 oradata為例,oradata有約1G(978M)的空間可用,其下有二個檔案系統prod和test,但其實這三個總共可用的空間是1G。如果你在 /oradata/test下使用了800M,縱使你將prod的quota設定為600M,但你實際能用的就只有176M而已。為了避免這種情形,我們可以設定reservation的大小,要求系統保留一定的空間給我們使用。

# zfs set reservation=600 oradata/prod

# zfs get quota,reservation oradata/prod oradata/test
NAME PROPERTY VALUE SOURCE
oradata/prod quota 600M local
oradata/prod reservation 600M local
oradata/test quota 600M local
oradata/test reservation none default

# zfs list -r
NAME USED AVAIL REFER MOUNTPOINT
oradata 600M 376M 27.5K /oradata
oradata/prod 24.5K 900M 24.5K /oradata/prod
oradata/test 24.5K 376M 24.5K /oradata/test

觀察上面zfs list的輸出,/oradata已經使用了600M,而這600M實際上是我們保留給prod使用的。而prod最小可以使用600M的空間,如果其他空間沒被使用的話,最大可以用到900M。
下面的範例可以看出設定reservation對oradata的可用空間所產生的影響。

# zfs set reservation=100m oradata/test
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
oradata 700M 276M 27.5K /oradata
oradata/prod 24.5K 876M 24.5K /oradata/prod
oradata/test 24.5K 376M 24.5K /oradata/test

# zfs set reservation=none oradata/test
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
oradata 600M 376M 27.5K /oradata
oradata/prod 24.5K 900M 24.5K /oradata/prod
oradata/test 24.5K 376M 24.5K /oradata/test

我們來做個實驗,試著在/oradata/test下產生一個400M的檔案。但執行的結果只有376M,剛好是/oradata/test的可用空間。再看到/oradata 的可用空間從900M變成了600M正是剛剛設定好的保留大小。

# zfs list
NAME USED AVAIL REFER MOUNTPOINT
oradata 600M 376M 27.5K /oradata
oradata/prod 24.5K 900M 24.5K /oradata/prod
oradata/test 24.5K 376M 24.5K /oradata/test

# cd /oradata/test
# mkfile 400m file1
file1: initialized 393748480 of 419430400 bytes: No space left on device

# zfs list
NAME USED AVAIL REFER MOUNTPOINT
oradata 976M 0 27.5K /oradata
oradata/prod 24.5K 600M 24.5K /oradata/prod
oradata/test 376M 0 376M /oradata/test

一般Oracle的資料庫都會需要存放Archive log,所以我們要再建立一個目錄專來門用來放置Archive log。 因為資料特性的關係所以我們新建一個pool來給它使用。再建二個zfs並其目錄放到/oradata/prod/arch和 /oradata/test/arch。看來有點複雜的樣子,我們把資訊整理如下;
pool : archlog
zfs1 : parch
mountpoint1 : /oradata/prod/arch
zfs2 : tarch
mountpoint2 : /oradata/test/arch
有了以上的資訊後,我們就可以開始動手了。

# zpool create archlog c2t1d0
# zfs create archlog/parch
# zfs create archlog/tarch

# zfs set mountpoint=/oradata/prod/arch archlog/parch
# zfs set mountpoint=/oradata/test/arch archlog/tarch
# zfs set mountpoint=none archlog
上面的範例的解說如下;
1. 在c2t1do上建立一個叫archlog的pool
2. 建立二個zfs,parch和tarch分別給prod和test使用
3. 修改二個zfs的掛載點,使其分別掛在/oradata/prod和/oradata/test下
4. 修改archlog這個pool的掛載點,將之改為none。這有什麼影響呢?看看df的輸出你就知道了。
# df -k
oradata 999424 28 999285 1% /oradata
oradata/prod 999424 25 999285 1% /oradata/prod
oradata/test 999424 25 999285 1% /oradata/test
archlog/parch 999424 24 999267 1% /oradata/prod/arch
archlog/tarch 999424 24 999267 1% /oradata/test/arch

沒錯,將mountpoint設為none就表示這個pool(或zfs)不會被掛載起來使用,這樣做的目的可以讓目錄或掛載點的管理較為單純。
傳統上我們使用mount和umount來做檔案系統的掛載和卸載,zfs也提供了相同的參數來做同樣的事情。 對zfs的檔案系統可以使用zfs mount和zfs umount來達到相同的目的。如下面範例,

# zfs umount /oradata/test/arch
# zfs mount archlog/tarch

如果基於某些理由,你想要使用傳統的方式來掛載那也是可以的。假設DBA要把某個資料庫的資料export出來 ,所以我們需要再建一個目錄專門給他們放資料。而這個檔案系統的掛載你希望透過傳統的方式來使用。其實方法很簡單,只要將該zfs檔案系統的mointpoint改成legacy就可以了。當然你也再可以手動編輯/etc/vfstab 並讓它可以在開機時自動掛載起來。

如果你要修改vfstab的話,要注意的是zfs的內容會和傳統的UFS不太一樣。在device to mount欄的值為zfs檔案系統的名稱,如oradata、oradata/prod, /device to fsck和fsck pass這二欄的值需為-,因為fsck這個指令無法使用在zfs這種格式的檔案系統,FS type當然就是zfs囉。
下面的範例是將帶你完成上述的需求。

# zpool create oraexp c2t2d0
# zfs get mountpoint oraexp
NAME PROPERTY VALUE SOURCE
oraexp mountpoint /oraexp default
# zfs set mountpoint=legacy oraexp
# vi /etc/vfstab
#device device mount FS fsck mount mount
#to mount to fsck point type pass at boot options
oraexp - /oraexp zfs - yes -
# mkdir /oraexp
# mount /oraexp
# df -h /oraexp
Filesystem size used avail capacity Mounted on
oraexp 976M 24K 976M 1% /oraexp

過了二個小時,DBA又跑來告訴你說他資料已經export好了,希望可以用nfs分享然後就可以直接import到另一台去 。於是你心下暗罵"這個煩人的傢伙"。當然你可以使用傳統的nfs分享方式,但ZFS已經把分享變簡單了。要分享檔案 你可以直接去編輯/etc/dfs/dfstab,或是使用設定sharenfs這個屬性

# zfs set sharenfs=on oraexp
# share

怎麼沒有東西呢?當zfs的mountpoint被設成legacy時,你就無法使用zfs的指令來分享必須要先將它改回來。

# zfs set sharenfs=off oraexp
# umount /oraexp
# zfs set mountpoint=/oraexp
# zfs set sharenfs=on oraexp
# share
- /oraexp rw ""

這會兒有了吧!我們再來看一個例子,在己分享的/oraexp下再建一個zfs出來,再用share來看看發生了什麼事。

# zfs create oraexp/son
# share
- /oraexp rw ""
- /oraexp/son rw ""

我們新建了一個叫son的zfs,用share一看他也被分享出來了,可是我們並沒有要分享呀!記得前面有提到過有些屬性是可繼承的,sharenfs就是其中一個,所以新建的son繼承了父親oraexp的值,也一併的分享出來了。
當我們設定sharenfs=on時,預設分享的權限是任何人都有rw的權限。看看上面share指令輸出的結果,對系統來講這是非常不安全的設定,當你在設定時請務必注意。一般我們會針對需要存取的主機設定不同的權限,如下面範例;

# zfs set sharenfs=ro=user1,rw=user2 oraexp
# share
- /oraexp ro=user1,rw=user2 ""

要停止分享時,可以使用如下的方式。第二個指令會停掉系統上所有的分享。

# zfs unshare oraexp/son
# zfs unshare -a

在Windows下,NTFS提供了檔案系統的壓縮功能,讓可用的磁碟空間比實際上的要來的多。ZFS也有提供這樣子的功能,但個人認為在現今每單位容量的成本越來越低的情況下,這樣的功能不具有太大的實質意義。大概只會在少數的情況下會使用如此的功能。如大量的檔案需要不定時的被存取,放在磁帶嫌太慢,放在磁碟又很佔空間。這個時候選擇低價的儲存系統加上壓縮的檔案系統會是最省錢的方法,但前提是不太要求存取的速度。
ZFS的屬性有一欄是compression,預設值是off,表示不開啟壓縮功能;若要開啟則將此值設定為on,這樣子就可以了。

# zpool create mypool c2t3d0
# zfs set compression=on mypool
# cd /mypool
# mkfile 200m 200file
# mkfile 500m 500file
# df -h /mypool
Filesystem size used avail capacity Mounted on
mypool 976M 24K 976M 1% /mypool
# ls -al
total 7
drwxr-xr-x 2 root sys 4 Sep 7 09:42 .
drwxr-xr-x 39 root root 1024 Sep 7 08:53 ..
-rw------T 1 root root 209715200 Sep 7 08:55 200file
-rw------T 1 root root 524288000 Sep 7 08:56 500file
# tar -cvf etc.tar /etc/*
# ls -al
total 45650
drwxr-xr-x 2 root sys 5 Sep 7 09:45 .
drwxr-xr-x 39 root root 1024 Sep 7 08:53 ..
-rw------T 1 root root 209715200 Sep 7 08:55 200file
-rw------T 1 root root 524288000 Sep 7 08:56 500file
-rw-r--r-- 1 root root 51942912 Sep 7 09:46 etc.tar
# df -h /mypool
Filesystem size used avail capacity Mounted on
mypool 976M 22M 954M 3% /mypool

上面的例子,新建了一個pool,並且開啟壓縮功能。然後產生了一個200MB和一個500MB的空白檔案,你可以比較ls,df和du的輸出有何不同。最後又產生了一個tar檔,但實際用到的空間仍然小於三個檔案的總和。你可以自行做類似的實驗,在每次產生一個檔案後執行如下的指令,
zfs get compressratio mypool
來觀察壓縮率的變化。最後要提醒的是壓縮功能的開啟一定會損耗系統的資源,而且會隨著檔案和存取的增加一同成長。是否要使用這個功能,除非不在乎系統的反應時間,否則實在沒必要開啟。

到這裡講的也都差不多了,基本的使用應該都不成問題。Solaris 10 的ZFS確實有其特色,但我的建議是,除非經過仔細的評估和測試,否則不要輕易的將之用在重要的正式系統上。另外使用時不同性質的需求最好建立不同的pool來使用。 以oracle為例,一般的datafile、redo、temp、archive log應該要放在不同的pool上,一來因為資料性質不同,二來存取的需求不同,放在不同的pool可便於辨識和管理,並且散在不同的實體硬碟上才會有最佳的效能。
在安裝Solaris時不曉得你有沒有注意到,安裝時並沒有可以選擇/檔案系統是要用UFS或ZFS。這是限制之一,因為現在的Solaris尚不能支援以ZFS為/目錄的檔案系統,至於/usr、/var能不能使用ZFS,我就不清楚了,建議系統的目錄還是使用 UFS,頂多搭配SVM應該就很夠用了。還有一個限制是,ZFS可以像AIX的LVM(5.0之後)或VxVM一樣任意的擴充檔案系統的空間,但是如果要縮小(回收過多的)空間時就不行了。這一點就比前述的二個要遜色不少。如果一定要把空間給縮小,只能把整個pool的資料備份出來然後打掉重建,再把資料倒回去。很麻煩吧!所以在給空間或建立pool時請仔細評估所需的空間大小,以免日後多了不必要的麻煩。

如何在命令列下寄信

在Unix的系統管理上我們常會把各式各樣指令的輸出導到檔案去,但是檔案在系統上想看一下內容難免還得要登入進去。於是懶惰的人就會把這些輸出寄出來給相關的人,這樣就不需要連進去系統了。
在Unix下寄mail是一件很平常的事,有人可能會覺得這個有點老生常談,但事實是還是有人會來問我命令列下要怎麼寄mail,而這個問題在網路上應該還是FAQ中的FAQ。

在Unix下最常被用來寄信的指令應該非mail莫屬,因為它普遍的存在各種不同版本不同平台的Unix系統上。或許你習慣用它的加強版mailx。不過這二個用起來差不多我也不清楚到底有什麼差別,知道的人跟我講一下吧。另外有一個指令叫mutt 它是一個功能相當強大的MUA軟體,如果你有安裝的話也可以使用它來寄mail。
現在我們就來看個簡單的例子。假設你想把/etc/hosts的內容傳到你的電腦上,你不需要使用ftp來把它抓到你的電腦,只要使用指令來將它寄給你自己。

# cat /etc/hosts |mailx -s 'hosts table' jerry@abc.com

許多人應該都知道要怎麼用,而且也很多人應該是這樣子用的。上面的指令並沒有錯,只是可以直接把檔案導入給mailx,為什麼要cat給mailx呢?公司薪水給的又不多,實在沒必要多花時間在處理公事上,所以字能少打就少打吧。

# mailx -s 'hosts table' jerry@abc.com < /etc/hosts

-s(Subject)是主旨,如果主旨有空白字元的話,記得要用單引號(')或是雙引號(")括起來。最後一個參數是收件人,如果收件人有多個的時候要用逗號(,)分隔,如jerry@abc.com,jerry@abc.com.tw
或者你在Linux下而且又裝了mutt。你也可以使用mutt來寄信,mutt的參數和mail差不多。

# mutt -s 'hosts table' jerry@abc.com 

可是這樣子寄,/etc/hosts的內容是在信裡面呀,我想把它變成附件可以嗎?當然可以,只要跟其他的程式配合就可以了。

# uuencode /etc/hosts hosts.txt | mailx -s 'hosts table' jerry@abc.com

利用uuencode把/etc/hosts編碼過後丟給mailx就可以了,uuencode第一個參數是要編碼的檔案,第二個參數是編碼後的檔名,在這裡就是附件的檔名。下面的例子示範了把tar檔變成附件直接寄出。

# tar cf - /etc/* | uuencode etc.tar | mailx -s 'hosts table' jerry@abc.com 

如果你用的是mutt那就更簡單了。只要加個-a(attach file)

# echo '/etc/hosts'|mutt -a /etc/hosts -s 'hosts table' jerry@abc.com

有人會問說「Jerry,我不要只是寄純文字的mail,可以寄html格式的mail嗎?」在我回答之前,可以容許我先打你一拳嗎? 如果你有mutt(1.5.x 以上才行)的話要寄html格式的mail就很容易了(什麼,到這個時候還沒發現mutt有多好用嗎?還不快去裝起來。)。一行指令就可以搞定。


# mutt -e 'set content_type="text/html"' jerry@abc.com" -s 'html format'

mutt允許我們對要寄出的mail修改它的檔頭(header),所以可以使用-e來設定檔頭的變數content_type為"text/html",這個變數名稱content_type和真正檔頭Content-type的字不一樣哦!不要搞錯了。最後再導入一個html格式的檔案給它就可以了。記住,-e後面那一串字不能錯哦!(1.5.x 以上才有這個變數。)
如果沒有mutt呢!裝一個,不然就繼續看下去。絕大多數的Unix系統應該都有內建sendmail,而sendmail有個參數-t可以達成我們的要求。看看下面的範例。

# echo 'Mime-Version: 1.0
> Content-type: text/html; charset="iso-8859-1"
> From: jerry@abc.com
> To: jerry@abc.com.tw
> Subject: test.......
>
> html file
> ' | sendmail -t

使用echo把檔頭和所要html的內容全部輸出給sendmail這樣子就可以了。不過我想沒人會真的在命令列這樣使用,多半會用在script裡面。下面提供一個script範例給你參考,主要的部份在被#包住的那個區塊,把你要的輸出做成html格式就 可以了。

#!/bin/sh
fromuser=jerry@abc.com
touser=jerry@abc.com,abc,boss@abc.com
subject="`uname -n` df output"
sendmail="/usr/sbin/sendmail -t"

header="Mime-Version: 1.0
Content-type: text/html; charset="iso-8859-1"
From: $fromuser
To: $touser
Subject: $subject
"

footer="
"

{
echo "$header"
### replace your code here. ###
df -k |awk '/^\//{$5=sprintf("%2d%",$5) ; print $0}'
###############################
echo "$footer"
} | $sendmail

下面二個script,第一個是shell script;第二個是perl script,你可以把要輸出的內容存成html檔,再用sendhtml直接寄出html格式的mail。寫的不怎麼樣,但最少可以使用啦!也給你一個想法做參考,你可以以此延伸然後寫一個符合你的script。
使用的方法如下,
sendhtml.pl "my subject" jerry@abc.com my_file.html

#!/bin/sh
## usage : sendhtml.sh subject To_User html_file
subject="$1"
touser="$2"
htmlfile="$3"
fromuser="jerry@abc.com"
sendmail="/usr/sbin/sendmail -t"

header="Mime-Version: 1.0
Content-type: text/html; charset=\"iso-8859-1\"
From: $fromuser
To: $touser
Subject: $subject"
{
echo "$header"
cat "$htmlfile"
} | $sendmail

#!/usr/bin/perl -w
# usage : sendhtml.pl subject To_User html_file
my $sendmail = "/usr/sbin/sendmail";
my $fromuser = "jerry\@abc.com";
$subject = $ARGV[0];
$touser = $ARGV[1];
$htmlfile = $ARGV[2];

$header="Mime-Version: 1.0
Content-type: text/html; charset=\"iso-8859-1\"
From: $fromuser
To: $touser
Subject: $subject\n";

open(fd2, "| $sendmail -t");
print fd2 $header;
open(fd1, $htmlfile);
while(my $line=) {
print fd2 "$line\n";
}
close(fd2);
close(fd1);

在寄附件的部份能不用uuencode就不要用,因為uuencode+mailx所寄出來的mail並不會在檔頭的部份加入MIME編碼的訊息,而現在有越來越多的郵件程式不會正確讀取此種格式的mail。如yahoo, MSN....等webmail,在開啟這些mail時都會顯示亂碼,而不是正確的附檔。所以建議儘量使用別的程式來取代。
在命令列下寄mail的方法不只這些,如使用nail(已經改名叫mailx)或是使用perl、php....等也是可以, 但以一般需求來講,我覺得這些應該已經夠了。如果你有什麼好的方法,歡迎你和大家分享。

Unix下為你的檔案加密

如果你有使用過WinRAR,那你應該知道WinRAR在壓縮時提供一項功能,可以為壓縮檔加上密碼,這樣子一來沒有密碼就 無法解開檔案,也就可以防止資訊外流。在Unix下壓縮通常我們會使用tar+gzip來達成,但是想為檔案多加一層保護時,通常就不知該如何下手了。
其實做法很簡單,所要使用的指令就只有openssl,只要你有裝openssl的套件就可以了。在Linux下預設就會安裝,如果你的是其他的Unix like的系統,你就得要自己檢查了。openssl中我們要用到的是enc(Encoding And Cipher)這個子功能,使用的方式如下。
openssl enc -e -cipher_method -in input_file -out output_file
其中-e表示要加密、-d表示解密、-in表示輸入的檔案、-out表示輸出的檔案、-cipher_method要看你選擇什麼加密方法,可以是-des、-des3、-bf......等。我們看個範例先。

# openssl enc -e -des3 -in /etc/hosts -out /tmp/hosts.cpt
enter des-ede3-cbc encryption password:12345
Verifying - enter des-ede3-cbc encryption password:12345

上面的範例使用des3的加密法將/etc/hosts加密,然後輸出成/tmp/hosts.cpt。過程中你所輸入密碼,也是你解開檔案 時的密碼。
那要如何解開檔案呢?剛有講到-e表示要加密(encrypt),-d就表示要解密(decrypt)了。我們看一下解密的範例。

# openssl enc -d -des3 -in /tmp/hosts.cpt
enter des-ede3-cbc decryption password:12345
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1 localhost.localdomain localhost
192.168.0.1 rac1-priv
192.168.0.2 rac2-priv
192.168.0.101 rac1-vip
192.168.0.102 rac2-vip

上面的例子和加密幾乎沒什麼差別。把-e換-d,-in變成了剛加密時所產生的/tmp/hosts.cpt。然後-out不見了,openssl 如果沒有指定-out或是-in的話,它會接受從stdin輸入或是輸出到stdout。所以上面的範例中,解密後你會看到原本/etc/hosts的內容輸出到螢幕上。
因為openssl可以使用stdin和stdout,所以只要透過幾個命令的組合,就可以很容易的做到像WinRAR的加密功能。下面的例子將/etc下所有的檔案變成一個加密過的壓縮檔。因為有的tar並不支援-z這個壓縮的參數,所以必須要拆成tar+gzip。Linux下的tar則都有這個功能,所以你也可以直接用tar zcf - | openssl....來取代。要解壓縮時,怎麼去,就怎麼回來,把原來的動作倒著作一次就可以了。

# cd /etc
# tar cf - * | gzip -c | openssl enc -e -des3 -out /tmp/hosts.tar.gz.cpt
enter des-ede3-cbc encryption password:12345
Verifying - enter des-ede3-cbc encryption password:12345
# file /tmp/hosts.tar.gz.cpt
/tmp/hosts.tar.gz.cpt: data
# openssl enc -d -des3 -in hosts.tar.gz.cpt |gzip -dc | tar -xf -
enter des-ede3-cbc decryption password:12345

如果不喜歡手動敲密碼,那也可以。openssl允許你可以把密碼存成一個檔案,只要在命令列多加-k的參數告訴它密碼檔案在那裡就可以了。有時候想密碼也是一件很惱人的事情,每次我換密碼的時候總是要想很久。如果你跟我一樣有這種困擾的話,有一個方法可以幫上忙。
我們可以利用/dev/random這個系統的亂數裝置來幫我們產生key檔,也就是我們要的密碼。如下面所示,用dd產生一個 crypt.key的檔,你可以自己看一下它的內容是什麼,然後就利用這個檔案來做加解密的動作。如此一來你只要保謢好這個檔案不要遺失,你就可以不用再記密碼了。

# dd if=/dev/random of=/tmp/crypt.key bs=256 count=1
0+1 records in
0+1 records out
# openssl enc -e -bf -k /tmp/crypt.key -in /etc/hosts -out /tmp/host.cpt2
# openssl enc -d -bf -k /tmp/crypt.key -in /tmp/host.cpt2
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1 localhost.localdomain localhost
192.168.0.1 rac1-priv
192.168.0.2 rac2-priv
192.168.0.101 rac1-vip
192.168.0.102 rac2-vip

很多時候會需要把資料備份到磁帶去,但是萬一磁帶遺失的話該如何是好,尤其如果裡面是敏感性資料,如信用卡客戶資料 、病歷資料......等,那可就不得了了。別怕!我們可以把要備到磁帶資料加密。

# tar cf - /report/* | openssl enc -e -bf -k /tmp/crypt.key | dd of=/dev/st0
# dd if=/dev/st0 | openssl enc -d -bf -k /tmp/crypt.key | tar -xf -

在Solaris 10以前,如果有需要通常我也會安裝openssl的套件。但現在如果只是為了要加密檔案的話Solaris就內建了功能差不多的指令,encrypt和decrypt。參數和openssl也差不多。
encrypt -l會列出可用的演算法和key的大小的資訊。如果要用來加密檔案的話要使用-a(algorithm)加上演算法的名稱,如3des。

# encrypt -l
Algorithm Keysize: Min Max (bits)
------------------------------------------
aes 128 128
arcfour 8 128
des 64 64
3des 192 192

看看下面加密和解密的指令和openssl是不是還挺像的呢?

# encrypt -a 3des -i /etc/hosts -o /tmp/hosts.cpt
Enter key:
# decrypt -a 3des -i /tmp/hosts.cpt
Enter key:
#
# Internet host table
#
127.0.0.1 localhost

當然也可以使用key檔,如果你要用dd產生的話,請注意key的長度限制。如3des的長度為192,故dd的count要等於24(192/8) ,如果是arcfour則為8到128。

# dd if=/dev/random of=/tmp/3des.key bs=24 count=1
# tar cf - * | gzip -c | encrypt -a 3des -k /tmp/3des.key -o /tmp/etc.tar.gz.cpt
# decrypt -a 3des -k /tmp/3des.key -i /tmp/etc.tar.gz.cpt|gzip -dc|tar tf -

再來要談的和檔案的加解密沒什麼關係,不過和加密倒是有關。算了,不知道自己在講什麼。在Linux下我們常會使用md5sum 來驗證檔案是否有更動過或是下載的檔案是否完整。如,

# md5sum /etc/hosts
dd7b3e9f10bbbd510a4637e7f29d3533 /etc/hosts

但在Solaris裡並沒有相對應的指令,不過現在Solaris 10提供了一個指令可以做到一樣的事情,那就是digest。digest和剛談到的encrypt一樣都是SCF(Solaris OS Cryptographic Framework)的指令,所以參數也很類似。-l也是列出支援的演算法,-a則是要使用的演算法。要使用像md5sum的查驗功能的話,那當然就是用md5的演算法囉。

# digest -l
sha1
md5
sha256
sha384
sha512
# digest -a md5 /etc/hosts
26cc2a22d93ecf862c8849ecaa29739c

如果習慣使用md5sum的話,你可以建個alias來代替。

# alias md5sum='digest -va md5'
# md5sum /etc/hosts
md5 (/etc/hosts) = 26cc2a22d93ecf862c8849ecaa29739c

如果你懷疑你的系統遭到入侵,程式有可能遭到竄改時,我們可以用digest取得md5的hash值,然後到Sun的filefingerprint 網站(http://sunsolve.sun.com/pub-cgi/fileFingerprints.pl)去做檢查。

# md5sum /bin/passwd
md5 (/bin/passwd) = f8a67ae893f0ced25b2bd7fd1ccc9b4e

我在Solaris 10 x86的系統下,以/usr/passwd為例,取得md5的值後,將f8a67ae893f0ced25b2bd7fd1ccc9b4e貼到上述 的網址,應該會得到如下的訊息。

 f8a67ae893f0ced25b2bd7fd1ccc9b4e -  - 1 match(es)

* canonical-path: /usr/bin/passwd
* package: SUNWcsu
* version: 11.10.0,REV=2005.01.21.16.34
* architecture: i386
* source: Solaris 10/x86

這表示passwd這一支程式的內容並沒有變動過,如果得到的訊息是如下的話,

 f8a67ae893f0ced25b2bd7fd1ccc9b4a -  - 0 match(es)
Not found in this database.

你的程式要不是因某些原因變動過,那就可能是系統已經有出現問題了。快檢查硬體或確認是否有人入侵吧。

10/16/2008

Solaris - 強化密碼的安全性

Solaris使用者帳號密碼的加密方式是很兩光的,解密的程式輕易的就可以解開。為了強化系統的安全性,Solaris 10在加密的功能上提供了不少好用的東西,現在先針對帳號密碼加密及安全上做個介紹。
Solaris 10提供了四種帳號密碼加密的演算法,下面的表是四種的說明。

試別字 加密法 說明 Man manual
1 crypt_bsdmd5 和BSD及Linux相容的MD5演算法,密碼最長可達255個字元。 crypt_bsdmd5(5)
2acrypt_bsdbf 相容於BSD的Blowfish演算法,密碼最長可達255個字元。 crypt_bsdbf(5)
md5crypt_sunmd5 Sun的MD5,比BSD及Linux更為安全,密碼最長可達255個字元。 crypt_sunmd5(5)
__unix__crypt_unix Unix傳統的演算法,並不安全,密碼最長為8個字元。 crypt_unix(5)

Solaris預設是使用__unix__,所以密碼長度限制為8個字元。我們來做個實驗,建立一個使用者abc並將其密碼設定為1234567890 共10個字元,然後使用abc這個帳號連入,輸入密碼的前8個字元也就是12345678。你會發現登入時的密碼和設定的並不相同,但是我們還是進入系統了。

# useradd abc
# passwd abc
New Password: 1234567890
Re-enter new Password: 1234567890
passwd: password successfully changed for abc
# ssh abc@.
Password: 12345678
Last login: Thu Sep 7 16:38:38 2006 from c01093.ncic.cor
Could not chdir to home directory /home/abc: No such file or directory
Sun Microsystems Inc. SunOS 5.10 Generic January 2005
$ id
uid=100(abc) gid=1(other)

果然是安全性很不夠,沒關係Solaris 10可以讓它變得更安全。
Solaris 10的密碼的加密演算法的相關設定是放在/etc/security/policy.conf裡面,讓我們先來看看裡面有些什麼內容

# cat /etc/security/policy.conf
......
# crypt(3c) Algorithms Configuration
#
# CRYPT_ALGORITHMS_ALLOW specifies the algorithms that are allowed to
# be used for new passwords. This is enforced only in crypt_gensalt(3c).
#
CRYPT_ALGORITHMS_ALLOW=1,2a,md5
# The Solaris default is the traditional UNIX algorithm. This is not
# listed in crypt.conf(4) since it is internal to libc. The reserved
# name __unix__ is used to refer to it.
#
CRYPT_DEFAULT=__unix__
......

因為相關的設定只有二行,所以我把不相關的部份都略去了。CRYPT_ALGORITHMS_ALLOW是在告訴系統,那些是可以使用的加密演算法。這裡要填入的是識別字,系統預設已經把三種都填進去了,所以我們不需要改。CRYPT_DEFAULT就是我們預設要使用什麼加密演算法。系統預設是使用傳統的也就是很爛的__unix__。我們要修改的就是這一行,把__unix__換成1就好 了,其他什麼事都不用做。在你修改之前先將此檔案備份,任何時候都要為自己的做為留退路。多留一個備份不會是壞事。

# cd /etc/security
# cp policy.conf policy.conf.orig
# vi policy.conf
......
#CRYPT_DEFAULT=__unix__
CRYPT_DEFAULT=1

修改完畢就立刻生效。我們立刻來做個實驗,一樣是拿abc這傢伙開刀,重設他的密碼為1234567890abcdef,然後再用8個 字元的密碼登入系統看看會發生什麼事。

# passwd abc
New Password: 1234567890abcdef
Re-enter new Password: 1234567890abcdef
passwd: password successfully changed for abc
-bash-3.00# ssh abc@.
Password: 12345678
Password: 1234567890abcdef
Last login: Fri Sep 8 10:00:29 2006 from localhost
Could not chdir to home directory /home/abc: No such file or directory
Sun Microsystems Inc. SunOS 5.10 Generic January 2005
$ id
uid=100(abc) gid=1(other)
$

將abc的密碼改為16個字元後,重新用abc登入系統,第一次先故意打錯密碼只打前8個字元,結果很明顯的是不給你進去。第二次我們把正確的密碼打進去後就可以登入系統了。
在使用新的加密方式後,原本的密碼依然是舊的加密方式,只有新建的使用者或是變更密碼才會使用新的加密方式。那沒變更過密碼的使用著還能登入嗎?因為系統是允許存在多個不同的加密演算法,就是CRYPT_ALGORITHMS_ALLOW中所設定的那三種,除非你手動的移除某一項,否則是都可以使用的。當然__unix__ 這一項是內建的,無法移除所以也就都可以使用。
或許有人會問,為什麼不用crypt_sunmd5呢?不是更安全更不易破解嗎?當然你可以視情況使用,不過使用crypt_bsdmd5的話,可以輕易的把帳號轉移到linux的平台去。不信你可以試著把/etc/passwd和/etc/shadow中abc那一行複製起來,然後貼到 linux系統上的/etc/passwd和/etc/shadow上,再使用abc登入看看。
最後,別忘了將root的密碼改一下,以使用更安全的密碼哦!

本來寫到這裡就要結束了,但是又想到了二個東西也很有趣,也和帳號有關所以順便也寫了下來。
solaris 10新增了一項歷史密碼的功能。開啟這個功能後,系統會記憶使用者所使用過的密碼,並且在更換密碼時不允許重複前幾次的密碼。這個功能可以防止有些人很懶老是使用相同的密碼,進而造成系統的安全缺口。最高記憶組數可達26組,這記憶力果然驚人。要開啟這個功能只要修改/etc/default/passwd,將HISTORY前的#拿掉,並且值設成HISTORY=n即可。

# grep HISTORY /etc/default/passwd
HISTORY=2
# su - abc
$ passwd abc
Enter existing login password:
New Password:
Re-enter new Password:
passwd: password successfully changed for abc
$ passwd abc
Enter existing login password:
New Password:
passwd: Password in history list.

Please try again
New Password:

如上面的範例所示,將HISTORY設定為2,然後以abc的身份修改密碼,第一次使用了1q2w3e4r。第二次再使用相同的密碼時,系統便會出訊息告訴你,這個密碼你之前用過了哦!然後要你重新輸入。很麻煩的新功能吧!
那歷史密碼放在那裡呢?就放在/etc/security/passhistory,裡頭放的依然是加密過的字串,以使用者名稱為開頭,每個欄位以: 分隔。有興趣自己去看一下吧!
另一個是當使用者登入錯誤幾次後就會被鎖住的功能,這個功能好像Solaris 8開始就有了。使用也很簡單,編輯 /etc/security/policy.conf,將LOCK_AFTER_RETRIES的值設定為YES,並將開頭的#字去掉存檔就ok了。這一個設定是全域的,也就是說每個使用者都受到影響。這個設定是開啟錯誤幾次後會鎖住,至於是幾次要鎖住則是要修改/etc/default/login的 RETRIES。另外有一個相關的是SYSLOG_FAILED_LOGINS,這一個是設定幾次登入錯誤後才會送訊息給syslog。如果設成0,那每次的錯誤都會被記錄起來。

#vi /etc/security/policy.conf
......
LOCK_AFTER_RETRIES=YES
#vi /etc/default/
......
RETRIES=3
SYSLOG_FAILED_LOGINS=3
# ssh abc@.
Password:
Password:
Password:
Permission denied (gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive).
# grep abc /etc/shadow
abc:*LK*$1$wzOJK8UX$JP2AcxvJh6pXQYfLj9k1w0:13399::::::9

上面的範例,開啟了帳號鎖住的功能,並將次數設定為三次。接著用ssh故意登入失敗,在三次失敗後我們可以看到 /etc/shadow中abc在密碼那一欄的最前面已經被加了*LK*,即表示這個帳號被鎖住了。
對於被鎖住的帳號可以使用passwd -u username來解開(-u 好像10才有),或者是手動的把/etc/shadow裡的*LK*拿掉就可以了。


假設你有一個同事常常打錯密碼而鎖住,因此來求你可不可以不要老是鎖住她的帳號時,你該怎麼辦?1.放棄討好漂亮妹妹的機會,凜然的告訴她,這是公司政策不可能為她而更改。2. 繼續看下去,學到最終奧義,然後幫她個人的帳號解套,最後還是落得好人卡一張。
我想多數人會選2,因為好機會不是天天有的,而且誰說未來一定是一張好人卡,說不定是抱得美人歸,或是......更多張好人卡呢!好啦!不管是什麼,總之呢,如果有的人不要套用帳號鎖定的功能的話,請編輯/etc/user_attr然後加入如下的資料
username::::lock_after_retries=no
帳號後接4個:,然後接著lock_after_retries=no這樣子就可以了。我們再以abc為例,修改好後的檔案長得像這個樣子。

# vi /etc/user_attr
adm::::profiles=Log Management
lp::::profiles=Printer Management
root::::auths=solaris.*,solaris.grant;profiles=Web Console Management,All;lock_after_retries=no
abc::::lock_after_retries=no

你可以再試看看,abc這個帳號還會不會被鎖住。最後還是要講一句,做任何的改變之前,一定要做備份。還有希望你不要領到好人卡。

Solaris - log的管理,使用logadm

在開始之前有個故事要和大家分享。有一次我朋友的公司裡有一台Sun的機器疑似crash後重開機,他的第一個反應是去看 messages log,結果使用vi來開啟messages檔時,vi跑了二分多鍾後,丟出一個訊息"檔案太大了,它開不了"。他轉頭問問我怎麼會這樣?於是我接手幫忙處理。執行uptime的結果確認是有重開機過,但為何重開機?則要看messages的內容才會知道,問題是vi開不了messages,所以我只好用tail+more來慢慢看。為什麼開不了呢?一看才知messages這個檔案竟然超過2G,最早的一筆記錄是四年多前的。天呀!
接著想瞭解在機器掛掉之前有誰在線上操作,結果last一跑......約五分鍾後螢幕才開始有東西跑出來。為什麼呢?因為和last相關的檔案wtmpx也超過500MB......。最早的記錄的時間我已經不想知道了。(messages不是有newsyslog會去換檔案嗎?是的,但不知在何時crontab裡已經沒有這一項存在了。唉,天兵管理員!)

早期的Solaris(9 以前)並沒有一個真正可用的log輪替程式來做系統log的管理。所以如果管理員沒有自己做適當的處理, 硬碟空間很容易就被吃光了。不然就會發生像上述的事情,檔案大到很難開啟。
以往的Solaris就只有一支newsyslog的簡單script來做messages的輪替的工作。不然就要自己安裝logrotate的套件來做這一類的工作。雖然logrotate很好用,但總是還要抓下來安裝。不過自從Solaris 9開始,Sun終於聽到使用者的聲音了。

Solaris 9開始提供了一支程式logadm專門用來做系統log的管理工作。個人覺得它和logrotate還有點相像,只是它的設定是一個log檔一行,而logrotate是一個log檔一個區塊。下面就來看看logadm要如何使用。

logadm有一個設定檔/etc/logadm.conf,該檔是一個純文字檔,所以你可以直接用vi去編輯它,或是使用logadm來變更設定。雖然可以直接編輯設定檔,不過使用logadm來變更設定時logadm會檢查語法是否正確,所以建議還是使用指令去變更設定。
Solaris在安裝完成後,root的crontab預設就會有一些東西定期的去執行,其中一個就是logadm。你可以用crontab -l來查 看。所以只要把設定檔寫好後,系統就會自動執行,你也不用再操心了。接下來就來看看logadm要如何使用。
logadm的語法大致上看起來就像下面這個樣子,

logadm /var/adm/sshdlog -p now

第一個參數是log檔的名字(不全然是,後面會說明),這裡我們用的是/var/adm/sshdlog。-p指的是週期(period), 這個值是數字接d(day)、w(week)、m(month)、y(year)。如10d,表10天;2w,表二個星期,14天。m雖然是指月,但實際是三十天哦!它可不會自動分辨大小月。另外還有二個特別的時間值,一個就是上面用的now,另外一個是never 。now就是不管如何,現在馬上給我換掉;never則是永遠都不要換,除非其他條件成立。
logadm的參數很多,下表列出幾個常用的參數

-C n保留的數量,如果沒有指定任何的過期條件,預設為-C 10。
-p n[dwmy] 輪替週期
-s n[bkmg] 檔案大小,當指定的log檔的大小大於等於這個值時就更換。
-z n 使用gzip來壓縮被更換掉的舊檔。
-a log檔更換後要執行的指令。
-b log檔更換前要執行的指令。
-g 新檔案的擁有群組,不給就和原檔案一樣。
-o 新檔案的擁有人,不給就和原檔案一樣。
-m 新檔案的權限,不給就和原檔案一樣。
-w 將此設定寫入設定檔。
-r 將此設定從設定檔中移除。
-V 列出設定檔的內容。

假設apache的error_log,希望每10MB換一次,最少保留8份;另外為了避免浪費空間,舊的檔案要壓縮。下面的範例可以 滿足我們的需求。

# logadm -w /var/apache/logs/error_log -C 8 -s 10m -z0 、
-a 'kill -HUP `cat /var/run/apache2/httpd.pid`'
# logadm -V| grep error
/var/apache/logs/error_log -C 8 -s 10m -z 0 -a 'kill -HUP `cat /var/run/apache2/httpd.pid`'

然後是access_log,由於log要留做分析使用,所以最少要保留2個月:也為了避免單一檔案太大,所以每個星期要換一個新的檔案。

# logadm -w /var/apache/logs/access_log  -C 9 -p 1w 、
-a 'kill -HUP `cat /var/run/apache2/httpd.pid`'
# logadm -V|grep acce
/var/apache/logs/access_log -C 9 -p 1w -a 'kill -HUP `cat /var/run/apache2/httpd.pid`'

logadm在檔案名稱部份也支援*,?,{}。所以上面的例子也可以改成下面的方式

# logadm -w apache_log -C 9 -p 1m 、
-a 'kill -HUP `cat /var/run/apache2/httpd.pid`' /var/apache/logs/{access,
error}_log
# logadm -V| grep apache_log
apache_log -C 9 -a 'kill -HUP `cat /var/run/apache2/httpd.pid`' -p 1m /var/apache/logs/access_log /var/apache/logs/error_log

這個例子好像和前面講的不太一樣,-w 後面接的不再是檔案名稱,取而代之的是apache_log。其實apache_log這是一個自取的識別字,logadm會自動判斷你給的識別字是否為檔案名稱(開頭是否為/);如果是,你可以省略後面的檔案名稱參數。如果不是則後面的檔案名稱就一定要給。前面二個例子的識別字我們都用檔案名稱來代替,而這個例子因為有多個目的檔案,所以要特別再取一個識別字。
當全部都設定好之後,只要直接執行logadm不用加任何的參數,logadm會自動去讀取/etc/logadm.conf,並將符合條件的 log檔進行更換。系統安裝好後root的crontab即已排定每天的3:10會執行logadm,所以只要等時間到log自然就會被換掉。


Solaris預設有幾個系統的log檔已經幫你設定好檔案輪替了,但/var/adm/wtmpx這個檔案並沒有被設定做輪替,所以建議可以把這個檔也加進去給logadm管理。

# logadm -w /var/adm/wtmpx -C 6 -p1m

上面的指令每個月換一次檔,共保留6個月。如果你要看歷史檔的話,只要執行如下的指令就可以了。

# last -f /var/adm/wtmpx.0 

Solaris 9也開始使用ssh的連線方式,但是預設卻沒有產生log檔,下面的範例示範如何手動使ssh產生log檔,並且設定相關的輪替機制。

首先確認,sshd使用的Facility和Level。

# egrep 'Faci|Level' /etc/ssh/sshd_config
SyslogFacility auth
LogLevel info

修改/etc/syslog.conf,加入下方auth.info那一行,存檔後再產生/var/adm/sshdlog這個檔。

# cp /etc/syslog.conf /etc/syslog.conf.orig
# vi /etc/syslog.conf
*.err;kern.notice;auth.notice /dev/sysmsg
*.err;kern.debug;daemon.notice;mail.crit /var/adm/messages
auth.info /var/adm/sshdlog
# touch /var/adm/sshdlog

然後重起syslogd,solaris 9和Solaris 10的方式並不相同。

Solaris 9
# /etc/init.d/syslog stop; /etc/init.d/syslog start
Solaris 10
# svcadm disable system-log ; svcadm enable system-log
或是直接refresh
# svcadm refresh system-log

設定sshdlog每個月換一次,保留3個月,並啟用壓縮。

# logadm -w /var/adm/sshdlog -C3 -p 1m -z 0 -a 'kill -HUP `cat /var/run/syslog.pid`'
# logadm -V| grep sshd
/var/adm/sshdlog -C 3 -p 1m -z 0

要看歷史檔的話,使用gzcat或gzip -dc就可以了。

# gzcat sshdlog.0.gz
# gzip -dc sshdlog.1.gz

logadm預設更換檔案的動作是1.把原檔案改名,2.產生一個和原檔名一樣的檔案。這樣子的行為對常見的log檔,如syslog, messages等不成問題。因為我們可以送HUP的信號給syslogd請它重讀設定檔,所以它可以知道舊檔已經被更換了,然後會把log送到新的檔案去。多數的AP都可以使用如上述的方式來進行檔案的更換,但不是所有AP都吃這一套哦!有些AP需要重新啟動才可以。但問題來了,有人會遇到一種狀況是AP是24hr運作,不能隨便重啟。....這可怎麼辦才好?logadm還有一個參數-c,它會把舊檔的內容複製成新的檔案,然後再把舊檔的內容清空,而不是重新產生一個新的檔案來取代舊的檔案。你可以試試下面的方法來輪替sshdlog。並比較和上述方式的不同。

logadm -w /var/adm/sshdlog -C 3 -p 1m -z0 -c 

有些AP本身即提供log檔案的輪替功能,如果你使用的是Solaris 10的話,系統的Fault Management的log就是使用fmadm來進行檔案輪替的。遇到像這一種AP,建議使用AP的指令來更換log檔,而不要使用logadm來做。但是logadm為了統一管理log的輪替,提供了一個-M參數來允許使用者自定指令來更換檔案。
下面的範例節錄自Solaris 10的logadm.conf,如果有需要你可以參考它是怎麼做的。

/var/fm/fmd/errlog -M '/usr/sbin/fmadm -q rotate errlog && mv /var/fm/fmd/errlog.0- $nfile' -N -s 2m

還有一些AP會不定期或定期的產生不同檔名的log檔,如oracle的trace檔。這一種型式的log檔就比較難使用logadm來做管理了。比較好的方法是自己寫個script來處理這些檔案。下面的範例是我想到比較簡單的方法提供給你做參考,若你有更好的方式別忘了要跟我分享哦!

假設有一支程式叫APservd,log檔存放的位置在/var/APservd/log下,它每個小時都會產生像這樣子的log, APservd_yyyyMMDD_HHmm.log。所以它一天的log檔的數量應該有24個,長的像下面這個樣子。

APservd_20061005_0000.log
APservd_20061005_0100.log
APservd_20061005_0200.log
......
APservd_20061005_2200.log
APservd_20061005_2300.log

於是我們寫一個script把每天24個檔都tar起來成為APservd_log.tar,然後再用logadm對APservd_log.tar來做輪替的動作 。這個script的內容如下,

# cat /root/bin/arch_AP_log.sh
#!/bin/sh
cd /var/APservd/log
/usr/sbin/tar -cf APservd_log.tar `/usr/bin/find . -type f -name "APservd*log"`
#/usr/bin/find . -type f -name "APservd*log" -exec rm -f {} \;

script的內容很簡單,最主要的是find的部份,你應該要自行修改成適合你的條件。如加上-mtime或是! -name "*tar*" 來避免檔名有tar這個字眼的檔被加進去。最後一行是把tar起來的檔案都刪掉,這一行find的條件和第三行的一樣。你應 該確定script執行都無誤後,再把#號拿掉。
最後是加一條設定進去logadm.conf

# logadm -w /var/APservd/log/APservd_log.tar -C 10 -a '/root/bin/arch_AP_log.sh' -p now

指令的參數-p後加的是now,所以每天執行時都會更換APservd_log.tar檔案,然後把所有的log檔再做成APservd_log.tar 在這裡應該有24個檔。你也可以把-p的時間改成你要的週期,如1w,那每次被tar起來的檔案應該就有7*24=168個檔,所保留的檔案則有10個星期。你可以使用logadm -v來看一下logadm是怎麼做的。

logadm比較常用的方法大概就這個樣子了,當然還有其他的參數我並沒有介紹,有需要的話自己看一下man page應該有所幫助。

以資訊安全的觀點來看,log檔能保留的時間是越長越好。不過實際上很多人出問題後並不會去看log,或是相關的log跟本就看不懂,更遑論會去關心log到底留多久,以及現行存放log方式的安全性如何了。如果你們部門正打算申請ISO 27001 (前身為BS 7799)的資安認證的話,這些東西值得你花點心思去處理。

Solaris - 新增使用者的問題

早期Solaris新增使用者時預設的家目錄是在/export/home下,不知何時開始忽然改成在/home下面了。而偏偏/home又是 autofs所要使用的目錄,所以每次新增使用者時,總會因為/home被lock住所以無法順利新增。並且出現如下的訊息。

useradd -m abc
UX: useradd: ERROR: Unable to create the home directory: Operation not applicable.

解決的方法有下面幾種,
1. 停掉autofs

### Solairs 9
# /etc/init.d/autofs stop
# mv /etc/rc2.d/S74autofs /etc/rc2.d/_S74autofs
### Solaris 10
# svcadm disable autofs

2. 修改autofs的設定檔,把/home標記起來或是改成別的路徑。

# vi /etc/auto_master
+auto_master
/net -hosts -nosuid,nobrowse
#/home auto_home -nobrowse

3. 更改使用者家目錄的位置。

### 使用-d指定家目錄
# useradd -d /export/home -m abc
### 變更useradd預設的家目錄位置,一勞永逸。
# useradd -D -b /export/home
group=other,1 project=default,3 basedir=/export/home
skel=/etc/skel shell=/bin/sh inactive=0
expire= auths= profiles= roles= limitpriv=
defaultpriv= lock_after_retries=

當變更useradd預設值後會產生一個檔案/usr/sadm/defadduser,記錄著上述的內容。如果有需要也可以手動修改,不過建 議還是用useradd -D來改比較好。

系統管理該注意的事- 五不二要

這一篇是依據自己的經驗、前輩的教誨和網路上眾多高手的經驗分享整理出來的,也是我這幾年來做系統管理工作的心得,不見得絕對的正確,但希望可以給你參考,幫助你更輕鬆的做好系統管理的工作。。

1. 不要在休假前做任何的事

管理一個系統常會面臨一些系統變更的需求,如修改系統參數、改某個程式的組態、新增一個例行性工作......等,凡此種種變動千萬不要在你休假前夕執行。你以為在休假前把事情做好你就能安心的休假了嗎?通常答案是否定的。我曾經在星期五時,因需求而修改一個備份的script。該script主要的工作是備份已經丟進資料庫的資料,並將已備份過的資料刪除。修改時我將刪除檔案的部份註解起來,以免測試時誤刪了資料。結果script順利修改完成,但是我卻忘了將刪除檔案的註解移掉。......星期一時才發現檔案系統在星期六下午就達100%,前端的機器跟本就無法將資 料送進來,更不要說丟進資料庫裡去。結果是有一天多的資料沒進資料庫,二天的日報表和週報表沒跑,前端AP人員花了一整個工作天才將資料全部丟進去資料庫,晚上才能開始跑報表,預計到星期二下午就可以跑完了。事情結束了嗎?沒有,其中有一個報表是副總要看的,我老闆免不了被叫去罵一頓,而我則負責寫檢討報告。還有一個ip設定錯誤的案例可以講呢?想聽嗎?
我的經驗告訴我,重大的變動一定要計劃,任何的改變一定要保留一段觀察期,選在對系統影響最小的時候(通常是離峰時間)進行。

2. 不要隨意在正式運作的系統上亂搞

熱衷於技術性新知的追求是好事,但在你對新發現的東西有完整的瞭解之前,不要輕易的將之使用在已經正式上線且運作正常的系統上。有的管理員在網路上看到好用的軟體、神奇的設定、便利的script,就會迫不及待的在自己管理的機器上試驗起來。經驗法則告訴我們說,這是一件很危險的事。系統管理強調的是維持系統的可用性,而不是裡頭有多高難度的設定或多偉大的軟體。沒有穩定可用的系統,管理員再高的技術也等於零。

3. 不要亂改預設值

除非有必要,如效能提昇、應用程式需求、安全因素.....等,否則不要改系統的預設值。如果真的需要改變,記得詳細記錄在系統文件上。隨意的改變系統預設值,可能在某些情況下造成系統的不正常,或是在除錯時造成困難。 除了系統參數外,程式安裝的預設路徑和設定檔存放的位置也不要隨意更改。我有一個同事把top安裝在/opt/local/bin 下,結果除了他知道外每個人都到/usr/local/bin下找,結果就是常有人打電話來問top放在那裡。

4. 不要隨便重開機

Unix只有極少的情況會需要重開機,問題發生時該做的第一件事是收集相關資訊,以利排除問題並將系統回覆正常。錯誤的設定、損壞的硬體不會因為一次的重開機,就變成正確或是忽然就好了。如果重開機系統就回覆正常的話,通常表示這個問題是不需要重開機就可以修復的,那為何還要重開機呢?不明就理的重開機,除了浪費時間外,還可能使問題的相關資訊流失或是使問題變的更加糟糕。

5. 不要相信廣告及廠商的話

廠商所講的話絕大多數都不是真正全面的事實,你一定遇過類似像這樣的事。某個廠商說"我們家某型號的SAN storage, 每秒可百萬個IO,效能絕對的領先。"這話只說了一半,真正的事實是,這個數據是把storage的cache加到128GB以上所測試出來結果,而該機器預設只有32GB,並且價格也是絕對的領先。我還遇過一家SI的某個工程師,打算用四個小時的時間把300多GB的資料庫透過100Mb的網路,複製到另一個系統。真不曉得他是怎麼估算的。這類的例子還很多,總之廠商說的話,通常不是真正的事實,甚至是謊話連篇。最好的辦法就是要他證明給你看,請他搬機器過來執行測試計畫, 並且注意到每個測試的細節。

6. 要做備份

任何時候都應該保留一份最新且可用的備份,因為系統會在任何的時間以各種可能的方式遺失掉資料。如硬體故障,程式當掉、停電、人為錯誤......等。可用的表示你要確認這個備份是可以將系統回覆成原來的樣子。如何確認?最好的方法是找一台機實際回覆一次看看。
當你編輯sendmail.cf時是直接就編輯,還是會先cp一個備份檔呢?當你改變routing table時,如果改錯了,你有辦法再改回正確的嗎?不要相信你的記憶,先把正確的備一份起來吧!
總之在你做任何的改變時都要有能力將之回到原來的狀況,所以改變前請先備份吧!

7. 要避免任何可能的危害發生

發現可能存在的危險並且想辦法去避免。我有同事因為誤觸主機電源而使整個系統被迫重新開機,原因在於電源開關的按鍵和磁帶機的退帶鍵太接近了,一個不小心慘劇就發生了。如果之前有使用膠帶把電源開關給貼起來的話,這樣的狀況應該就可以避免了。你應該也曾經遇過拔錯電源線或是網路線的經驗吧!如果貼上容易識別的標籤,是否可以避免這樣的情況呢?類似的情況還有電源線、網路線......等沒有良好的收納而裸露在外或是散亂於地。都有可能造成系統的災難。還會有別的嗎?Mirror的磁碟陣列壞掉一顆時,你有及時去處理嗎?我看過一顆硬碟壞了,而管理員偷懶想說過二天再弄時,只過一天第二顆也壞了的情況。
在災難還沒發生時,先想辦法預防吧。