2008年3月18日星期二

为什么我的debian时间总是不对

很郁闷不知道自己装的debian时间等设置的没问题,可是总是出错,比北京时间快了8个小时,即使调整过来,下次又变回来了。网上搜了下,果然有高手解答了,原来是读取BIOS出错。

人家写的非常好,兹引用过来:

Re: 为什么我的debian时间总是不对

发信人: cathayan (失群孤雁逆风飞), 信区: LinuxApp
标 题: Re: 为什么我的debian时间总是不对
发信站: 水木社区 (Thu Jul 12 14:28:58 2007), 站内

要有耐心:

以下针对Debian sid。

先说时区的配置。以前Debian有个好胜的时区配置工具,叫tzconfig,现在也没有
了。但配置时区倒也简单,主要是两个文件:

/etc/localtime
/etc/timezone

timezone这个文件是个文本,里面只需要写一行自己的时区就行,我们这里就是上
海,Asia/Shanghai(谁知道为什么不是北京呢?)。 localtime这个文件的类型
不清楚,里面就写了些timezone data,它可以从系统自带时区文件那里拷贝,位
置在:

/usr/share/zoneinfo

从这个目录下找到Shanghai拷贝到/etc下的localtime即可。有人说建个连接也可
,这样还可以保证系统数据有变化时不必再管。

设定了时区,还要确定Linux的时间方案。Linux支持UTC时间,Coordinated
Universal Time,也就是世界协调时,也就是本初子午线上的时间,它和以前的格
林威治标准时(GMT)的区别似乎是它是由多个原子钟平均出来的。在
/etc/default/rcS这个文件中,设定了系统是否使用UTC,UTC=yes就是用。

计算机自己还有自己的时间,也就是硬件时间,hard clock,也就是存在BIOS里那
个时间,关机也不会丢失。计算机启动时,就要读取这个时间。这个时间如果设定
为UTC(GMT),也就是伦敦那地方的时间,就要在rcS文件中设定UTC=yes,反之则
要设为no。

总之就是两种正确的设置:

BIOS=本地时间,UTC=no
BIOS=UTC时间,UTC=yes

一般来讲,BIOS里面都设定为当地时间,这是因为如果装双系统的话,Windows似
乎不懂utc,就会出问题。这时UTC=no。

如果一切顺利,到这时,进入Linux之后显示的时间应该是正确的了。但不少人的
机器,包括这回这台640m,仍旧不正确,而是比正确时间再往前跑了8小时。这里
的问题出在Linux读取硬件时间上了。

Linux读取这个硬件时间要用到hwclock这个命令:

hwclock --show :显示硬件时间
hwclock --systohc :将系统时间写入硬件
hwclock --hctosys :将硬件时间写入系统时间

在出问题的时候,hwclock 这一组命令的运行通常是不能成功的,错误经常是这样
的:

select() to /dev/rtc to wait for clock tick timed out

也就是不能读取/dev/rtc,也就是硬件时间。这又是因为某些机器的BIOS处理方式
和Linux的rtc内核模块之间出现了问题。

Linux又有3个这类模块,rtc/genrtc/rtc_dev,似乎是一个比一个新,而debian通
常自己用的是rtc这个老模块;但 Dell/ibm/acer等等厂商现在都可能使用新的
BIOS,这种BIOS和这个rtc就可能不对付。如果出现了上面那个timeout的问题,有
两种方法可以解决:

1, 给hwclock加参数,--directisa,也就是:

hwclock --directisa --show

如果运行成功,说明这个办法可行。则把此参数添加为hwclock的缺省参数即可。
在debian 4.0之后,可以直接在/etc/default/rcS中添加一行:

HWCLOCKPARS="--directisa“

而在4.0以前,可能只能在hwclock启动脚本中添加,/etc/init.d/hwclock.sh,把
里面的”/sbin/hwclock“ 全部替换为 ”/sbin/hwclock --directisa"。现在在
debian sid中,这个脚本的第一行其实是HWCLOCKPARS=,也可以像rcS一样添加参
数了。

2, 换用其他内核rtc模块,用如下方法测试哪个模块好用:

# modprobe rtc
# hwclock --show
# rmmod rtc
# modprobe genrtc
# hwclock --show
# rmmod genrtc
# modprobe rtc_dev
# hwclock --show
# rmmod rtc_dev

没有显示time out的就是好用的了,然后可以在blacklist中阻止不好用的,在
modules里面加上好用的那个。

用完这两个方法,hwclock应该能直接工作了,也就是可以读取硬件时间了。再配
上utc设置正确,重启之后时间就是对的了。

为了让BIOS时间更准确,除了可以找个精确的时间源,比如CDMA手机或是GPS,靠
自己的手指来精确设定BIOS时间之外,还可以用hwclock把准确的时间写入BIOS。
前一种方法细心点可以做到几秒误差,而后一种怎么也在1秒以下了。

安装ntpdate这个包,它可以从时间服务器上读取到正确的时间,精度还是很高的

aptitude install ntpdate
ntpdate pool.ntp.org

此时系统时间就已经是ntp的时间了,相当精确,把它写入硬件:

hwclock --systohc

这样BIOS时间也就很准了。以后开机没网络,没办法运行ntpdate的时候也都是准
的。

这里的关键有:bios时间,utc设定,rtc模块,hwclock读取,ntpdate对时。

【 在 bo0od (Share the world) 的大作中提到: 】
: 快说说
: 我郁闷很久了
: 我的一直是yes
: ...................

--

天末同云黯四垂,失行孤雁逆风飞
江湖寥落尔安归

九万里风安税驾,云鹏今悔不卑飞
海北天南总是归

※ 来源:·水木社区 newsmth.net·[FROM: 61.51.121.*]

2008年3月10日星期一

自己想做的事情

1,学业有成---正在进行中;
2,事业有成---1完成之后,进公司、高校均可,要求不高,够用即可;
3,爱情有成---不限时候,互相有爱,共同努力,共建家庭;
4,兴趣有成---计算机方面:希望对C精通,对一门脚本语言精通,如perl和shell编程,熟悉linux;其他方面:出去旅游一番。
... ...
有待补充

2008年3月6日星期四

cluster的安装调试[转载]

网上发现这篇文章,服务器以及安装的软件都和我们的非常类似,兹引用过来作为参考,我就不用写了,呵呵。


cluster的安装调试 (一)

cluster的安装调试
历时月余,实验室的机器总算装好了。
实验室的机器是5台双cpu的机器。tyan主板,amd 2200+MP cpu。1G内存。80g硬盘。
系统是rh9,mpich1.2.5 作业管理系统torque


第一部分 系统安装

第一步先把服务器(管理节点)装好。把ip设为192.168.1.4
装其它机器时是利用pxe安装的。参考了下面的帖子
《Kickstart+HTTP+DHCP+TFTP+PXElinux实现RedHat的网络自动安装》
具体实现的过程是这样的:
1、 配制一个HTTP服务器(当然也可以是NFS、FTP)及kickstart文件:
1)配好后,在HTTP的DocumentRoot目录下(一般为/var/www/html)建一个rh9目录,
用于放 RedHat9的安装文件。把RedHat9第一张安装盘的RedHat目录及下面的内容全部考
到这个目录下,把第二、第三张盘的RPMS目录下的rpm 包考到rh9/RedHat/RPMS目录下,
完成安装文件的考贝。service httpd restart开启HTTP服务。可以在浏览器内打入:
http://192.168.1.4/rh9看一下,是不是能看到RedHat目录。
2) 用redhat-config-kickstart命令做一个ks.cfg文件,注意在“安装方法”选项中
选HTTP,在“HTTP服务器:”中打入HTTP服务器的IP地址,在“HTTP目录:”中打入rh9,
其它的我就不多说了。做好后把这个ks.cfg 文件考到HTTP服务器的DocumentRoot/rh9目
录下。
3)在一台机器上做一下测试(注意:因为是自动安装,这台机器的上的数据可能全部
被格掉喔)。用光盘启动机器,在boot:提示符下打入:
linux ks=http://192.168.1.4/rh9/ks.cfg,
看一下是不是能实现自动安装,如果能,OK我们一半的工作已经做好了。
2、 配制DHCP、TFTP、和PXElinux。(当然这些服务可以和HTTP放在一台服器上)
1) 做一台TFTP服务器太容易了,用rpm –qa | grep tftp看一下tftp有没安装,没
有的话安装一下。用chkconfig tftp on 打开xinetd代理的tftp服务。
2)下面是我的dhcpd.conf文件:

ddns-update-style interim;
allow booting; #定义可以PXE 启动
allow bootp; #定义支持boottp
subnet 192.168.1.0 netmask 255.255.255.0 {

option routers 192.168.14; #定义默认网关
option subnet-mask 255.255.255.0;
option domain-name-servers 192.168.1.4; #定义nameServer
option time-offset -18000; # Eastern Standard Time

#range dynamic-bootp 192.168.168.12 192.168.168.254;
#default-lease-time 21600;
#max-lease-time 43200;

# You need an entry like this for every host
# unless you're using dynamic addresses #可PEX启动的主机的IP与MAC地址的邦定
host p1
{ hardware ethernet 00:00:E2:89:9B:DB;
fixed-address 192.168.1.201;
next-server 192.168.1.4; #TFTPServer的IP
filename "/pxelinux.0";#pxelinux loader文件位置 }
}

文件里节点机的mac地址很好找。把节点机的网络启动打开,启动机器。机器会显示寻找dhcp
的信息,屏幕显示的地址就是机器的mac地址。

3)配制PEXlinux。要远程启动机器,就必须要有PXElinux这个由syslinux、isolinux派生
出来的,支持PXE的 bootloader,它可以实现远程通过PEX启动一台机器。把/usr/lib/syslinux/pxelinux.0
考到/tftpboot目录下,把RedHat9第一张安装光盘上/image/pxeboot/initr.img和vmlinux也
考到/tftpboot目录下。在 /tftpboot建一个pxelinux.cfg目录,用于放syslinux的配制文件。
Sysconfig 的配制文件有现成的可用,在rh9第一张安装光盘isolinux/下有一个isolinux.cfg文件,把它考成 /tftpboot/default,把isolinux/*.msg考到 /tftpboot下(RedHat的安装光盘的Loader就是个isolinux,呵呵)。对default(也就是isolinux.cfg)作一 下修改,下面是我的default文件:
default local
prompt 1
timeout 30
display boot.msg
F1 boot.msg
F2 options.msg
F3 general.msg
F4 param.msg
F5 rescue.msg
F7 snake.msg
label local
localboot 0
label linux
kernel vmlinuz
append initrd=initrd.img devfs=nomount ramdisk_size=9216
label text
kernel vmlinuz
append initrd=initrd.img devfs=nomount ramdisk_size=9216
label expert
kernel vmlinuz
append expert initrd=initrd.img devfs=nomount ramdisk_size=9216
label ks
kernel vmlinuz
append ks initrd=initrd.img devfs=nomount ramdisk_size=9216
label lowres
kernel vmlinuz
append initrd=initrd.img lowres devfs=nomount ramdisk_size=9216
label autoinstall
kernel vmlinuz
append ks=http://192.168.1.4/rh9/ks.cfg initrd=initrd.img devfs=nomount ramdisk_size=9216 nofb


cat一下/tftpboot/boot.msg文件:
splash.lss
- To Local boot 02Red Hat07 Linux , type:
0f07.
- To install or upgrade 02Red Hat07 Linux in graphical mode,
press the 0f linux07 key.

- To install or upgrade 02Red Hat07 Linux in text mode, type:
0flinux text 07.

- To Auto Install 02Red Hat07 Linux , type:
0flinux autoinstall 07.
- Use the function keys listed below for more information.

02[F1-Main] [F2-Options] [F3-General] [F4-Kernel] [F5-Rescue]07

理论上应该可以实现自动安装,可是也许是因为主板上集成了2个网卡。总之无法实现自动
安装,但是可以实现网络启动。不过这也够了。下面说说安装
在安装节点机的时候有几个包没有选,结果在后来配置服务时出来些麻烦。集群需要的服务
主要有NIS NFS SSH RSH所以在安装系统时要注意这个几个服务的rpm一定选上了。当然
还有一些库文件。在安装节点机的时候rsh-server,xinet.d,ligbf2c没有装上,运行程序时报
错,弱了半天。

本文由 amao 于2004年12月14日 03:30 PM 发表 | 评论 (1)

cluster的安装 (二)

第二部分

下面是集群需要的几个服务的配置
1 rsh
配置rsh-server, 下面的操作必须以root 身份执行。
编辑文件/etc/hosts.equiv, 在其中加上本机主机名(单独占一行)。如果该文件不存在则
创建一个。将所主机名加到文件/etc/hosts.equiv 中。此文件在每个机子都建立一份。
physics --------> 管理节点主机名
p1 --------> 计算节点主机名
p2
p3
p4
编辑/etc/xinet.d/rlogin
找到 disable = yes
把yes改成no
– 开启rsh 服务:
/sbin/chkconfig rsh on
/etc/init.d/xinetd start

测试rshd 的配置。以普通用户(非root) 登录并运行命令:
rsh 主机名/bin/hostname
如果配置正确该命令应该显示本机主机名。如果出错可查看/var/log/messages 文件中的
错误信息。

下面介绍的方法使用NIS (Network Information Service, 也叫做SUN Yellow Pages) 管
理用户帐号,使用NFS (Network File System) 共享用户目录。
将所有机器的主机名放在/etc/hosts 文件中。可以在所有机器上使用同样的/etc/hosts 文
件,它包含如下形式的内容:
127.0.0.1 localhost.localdomain localhost
192.168.1.201 p1
192.168.1.202 p2
192.168.1.203 p3
192.168.1.204 p4
192.168.1.4 physics
(按真实情况替换其中的主机名和IP 地址)。

首先根据情况选择一台机器作为NIS 和NFS 服务器,我们将该机器称为服务结点或主结点
机,而其它机器称为从结点机。主结点机和从结点机的配置方式不同,下面将分别介绍。

2 设置NFS主结点机
=======================================
" 创建目录:
mkdir -p /home/local
将/usr/local 链接到/home/local:
/bin/rm -rf /usr/local
ln -s /home/local /usr/.
(注: 如果以前的/usr/local 中安装有有用的文件, 则在执行上述命令前应该将其拷贝或移
走)。"
=======================================
上面的内容并没有用到。NFS服务器,我共享了以下3个目录:
/home ----------->用于存放用户的文件
/usr/local ----------->用于存放共享的软件
/redhat ----------->用于存放系统的安装文件

确认在主结点机上安装了nfs-utils 包。开启NFS 服务:
/sbin/chkconfig nfs on
/sbin/chkconfig nfslock on
/etc/init.d/nfslock restart
/etc/init.d/nfs restart

在文件/etc/exports 中加入下面一行:
/home *(rw,async,no_root_squash)
/usr/local *(ro,async,no_root_squash)
/redhat *(ro,async,no_root_squash)
它将/home 目录输出给所有机器。(注: 出于安全考虑可以限制仅将目录输出给指定的结
点机, 及将no_root_squash 改成root_squash。请用命令man 5 exports 查看相关参数
的写法)。

输出指定目录(/home):
exportfs -a
(也可重新启动系统)。
exportfs -v
查看共享的目录
其他的参数man一下


在结点机
===================================================
"创建目录:
mkdir -p /home "
===================================================
运行命令
showmount -e physics
可以看到共享的目录

在文件/etc/fstab 中加入下面一行:
physics:/home /home nfs defaults 0 0
physics:/redhat /redhat nfs defaults 0 0
physics:/usr/lcoal /usr/local nfs defaults 0 0

以上改动在所有的节点机做一遍
运行命令:
/sbin/chkconfig netfs on
它使得系统启动时自动挂接主结点机上的/home 目录。
运行命令:
mount /home
(也可重新启动系统)。

===================================================
将/usr/local 链接到/home/local:
/bin/rm -rf /usr/local
ln -s /home/local /usr/.
(注: 如果以前的/usr/local 中有有用的文件, 则在执行上述命令前应该将其拷贝或移走)。
上述所有操作必须以root 身份执行。本步骤完成后,所有结点机上的/home 和/usr/local
目录的内容应该是一样的。在从结点机上可用命令df 检查挂接情况。
===================================================

3 设置NIS
以下描述中假设以‘stars’ 作为NIS 域名。
A 主结点机
确认安装了下述包:
ypserv, ypbind, yp-tools
在文件/etc/sysconfig/network 中加入下面一行:
NISDOMAIN=stars 开启NIS 服务:
/sbin/chkconfig ypserv on
/etc/init.d/ypserv start
初始化NIS 数据库:
/usr/lib/yp/ypinit -m
程序运行时按Ctrl-D, 然后按“y” 和回车。该命令将生成NIS 数据库。可以忽略
No rule to make target ...
之类的错误信息。
开启NIS 客户程序:
/sbin/chkconfig ypbind on
/etc/init.d/ypbind start
验证NIS 设置
– 命令“ypwhich” 应该显示出主结点机主机名。
– 命令“ypcat passwd” 应该显示出(主结点机上的) 用户帐号。

B 节点机
确认安装了下述包:
ypbind, yp-tools
在文件/etc/sysconfig/network 中加入下面一行:
NISDOMAIN=stars
开启NIS 客户程序:
/sbin/chkconfig ypbind on
/etc/init.d/ypbind start

C 验证NIS 设置
– 命令“ypwhich” 应该显示出主结点机主机名。
– 命令“ypcat passwd” 应该显示出主结点机上的用户帐号。
为了能够使用NIS 用户登录,还需要修改/etc/nsswitch.conf 文件,使其包含下述设置:
passwd: files nis
shadow: files nis
group: files nis
hosts: files nis dns
注意在从结点机上不要重复定义用户信息,即在从结点机上应该将所有NIS 用户(可用命令
“ypcat passwd” 显示出来) 从文件/etc/passwd 和/etc/shadow 中删除,将所有NIS
组(可用命令“ypcat group” 显示出来) 从文件/etc/group 中删除。
完成NIS 配置后,创建新的用户帐号只需在主结点机上进行(注意将用户的主目录放到
/home下), 然后运行命令“cd /var/yp; make” 即可。
如果在主结点机上修改了一个用户帐号信息,也应该运行一次上述命令以刷新NIS 数据库。
NIS 用户在从结点机上不能用“passwd” 命令修改用户口令,而必须用“yppasswd”
命令来修改。
上述所有操作必须以root 身份执行。

后来启动机器的时候,节点机在mount nfs 分区时显示“ no route to host" --这时候
netfs进程无法启动。后来在/etc/rc.d/rc3.d/S99local中加入一行 /etc/init.d/netfs start

本文由 amao 于2004年12月14日 03:52 PM 发表 | 评论 (0)

cluster的安装 (三)

第三部分 配置 mpich 和pbs

A. 安装mpich
编译时,用下面的命令
./configure --prefix=/usr/local/mpich-1.2.5 --disable-weak-symbols
然后 make
make install
编辑用户目录下的.bashrc文件
export MPICH_GNU=/usr/local/mpich1.2.5/
export MPICH=${MPICH_GNU}
export MPICH_LIB="{MPICH}/lib"
export MPICH_PATH="{MPICH}/bin"
export PATH="{MPICH_PATH}:${PATH}"
export LD_LIBRARY_PATH="/usr/local/lib/:{MPICH_LIB}:${LD_LIBRARY_PATH}"

B. 安装mpiexec

./configure --prefix=/usr/local/mpiexec/ --disable-mpich-gm
--with-pbs=/usr/local/torque/ --with-default-comm=mpich-p4 --disable-p4-shmem

make
make install

C. 安装pbs,即torque
./configure --prefix=/usr/torque --disable--gui
make
make install

在/etc/profile.d目录下编辑pbs.sh
export PATH=${PATH}:/usr/local/torque/bin:/usr/local/torque/sbin
export PBS_DEFAULT=physics
chmod 755 pbs.sh

1)设置服务端
[root@physics server_priv]# pwd
/usr/spool/PBS/server_priv

建立nodes文件
[root@physics server_priv]# cat nodes
p4 np=2
p3 np=2
p2 np=2
p1 np=2

创建pbs.conf在/etc 目录下
pbs_home=/usr/spool/PBS/
pbs_exec=/usr/local/torque/
start_server=1
start_mom=0
start_sched=1

2.)设置客户端 intall pbs_mom in node

1.
[root@node01 init.d]# pwd
/home/torque-1.1.0p0/src/tools/init.d
copy pbs ---> /etc/init.d/
copy pbs.conf --> /etc/
vi pbs.conf
pbs_home=/usr/spool/PBS/
pbs_exec=/usr/local/torque/
start_server=0
start_mom=1
start_sched=0

chmod 755 pbs.conf

2.
/home/torque-1.1.0p0/buildutils
sh pbs_mkdir mom
sh pbs_mkdir aux
sh pbs_mkdir default

3.
建立下面3个文件 config epilogue prologue
cd /usr/spool/PBS/mom_priv
vi config
$logevent 0x1ff
$clienthost physics

[root@p1 mom_priv]# pwd
/usr/spool/PBS/mom_priv
[root@p1 mom_priv]# cat epilogue
#!/bin/sh

sleep 5

PBS_HOME=/usr/spool/PBS
PBS_PREFIX=/usr/local/torque

function printline
{
for i in $(seq 1 40)
do
echo -n "-"
done
echo
}

printline
echo "Begin PBS Epilogue $(date)"
echo "Job ID: $1"
echo "Username: $2"
echo "Group: $3"
echo "Job Name: $4"
echo "Session: $5"
echo "Limits: $6"
echo "Resources: $7"
echo "Queue: $8"
echo "Account: $9"
echo -n "Nodes: "
for i in $(sort $PBS_HOME/aux/$1 | uniq)
do
echo -n "$i "
done
echo
echo

#echo "Killing leftovers..."
echo
echo "End PBS Epilogue $(date)"
printline

exit 0
[root@p1 mom_priv]#


[root@p1 mom_priv]# cat prologue
#!/bin/sh

PBS_HOME=/usr/spool/PBS
PBS_PREFIX=/usr/local/torque

function printline
{
for i in $(seq 1 40)
do
echo -n "-"
done
echo
}

printline
echo "Begin PBS Prologue $(date)"
echo "Job ID: $1"
echo "Username: $2"
echo "Group: $3"
echo -n "Nodes: "
for i in $(sort $PBS_HOME/aux/$1 | uniq)
do
echo -n "$i "
done
echo

echo "End PBS Prologue $(date)"
printline

#sleep 5

exit 0

4.
vi /home/torque-1.1.0p0/torque.setup
找到“pbs_server -t create” 注释掉这行
然后运行./torque.setup

5.
/etc/init.d/pbs start
把上面这行命令加入 /etc/rc.d/rc3.d/S99local

6.
启动pbs后,服务端和客户端
[root@node01 torque-1.1.0p0]# qmgr
Max open servers: 4
Qmgr:

Qmgr: l n node02
Node node02
state = free
np = 1
ntype = cluster
status = arch=linux,
uname=Linux node02 2.4.20-8 #1 Thu Mar 13 17:18:24 EST 2003 i686,
sessions=? 15201,nsessions=? 15201,nusers=0,idletime=1582,
totmem=1501464kb,availmem=1209188kb,physmem=449216kb,ncpus=1,
loadave=0.00,rectime=1123147821
可以查看其它节点的信息。

Qmgr: s s query_other_jobs = true 可以查看其它id的作业。

7. 提交作业
编写脚本 sub.pbs
#PBS -l nodes=4:ppn=2,walltime=168:00:00
#PBS -N cpi
#PBS -j oe
cd ~/pi
/usr/local/mpiexec/bin/mpiexec cpi

第一行 nodes 申请的节点数,ppn 每个节点运行的cpu个数 walltime 程序运行的时间
第二行 任务的名字
第三行 输出文件和错误信息都输出到同一个文件
第四行 进入要运行的文件的所在目录。
qsub sub.pbs

原文地址 http://www.nkstars.org/blog/archives/amao/cat_clusterceeec.html

2008年3月3日星期一

Kerberos的身份鉴别过程

Kerberos的身份鉴别过程

  在Kerberos协议的整个实现过程中,涉及几个身份鉴别过程,Kerberos Server对客户端身份的认证,服务器对客户端身份的鉴别,客户端对服务器身份的鉴别,下面的示意图(图5-8)能够更深入的反映Kerberos的这些身份鉴别过程:


图5-8 Kerberos密钥信息交换过程

   在AS交换阶段,Client端向KDC(上图细化为Kerberos Server)申请“许可票据”,KDC通过使用事先存储的共享客户端密钥(上图为红色密钥)来实现对Client身份的鉴别;在TGS交换阶段, Client端向KDC申请用于和服务器进行加密通信的会话密钥,KDC(上图细化为Ticket Granting Server)是通过鉴别Client端是否拥有AS交换阶段中Kerberos Server颁发的随机密钥(上图为粉红色密钥)来实现对Client身份的鉴别;在CS交换阶段,Server端通过验证Client端是否拥有TGS 服务器随机颁发的会话密钥(上图为褐色密钥)来鉴别Client端的身份,Client端通过验证Server端是否拥有服务器共享密钥(上图为青绿色密 钥)来鉴别Server端的身份。
  可见,在Kerberos工作的AS交换、TGS交换和CS交换三个阶段,所实现的身份鉴别过程均符合前面5.2.3小节中所提出的“基于对称加密的 模型”,所有鉴别机制的实现均依赖于被鉴别方是否拥有某一对称密钥来实现。

SSH使用及协议分析

SSH使用及协议分析
本文出自:http://xfocus.org 作者: yeye (2001-09-12 07:00:01)

SSH是一个用来替代TELNET、FTP以及R命令的工具包,主要是想解决口令在网上明文传输的问题。为了系统安全和用户
自身的权益,推广SSH是必要的。SSH有两个版本,我们现在介绍的是版本2。



安装SSH

具体步骤如下:


获得SSH软件包。 (ftp://ftp.pku.edu.cn:/pub/unix/ssh-2.3.0.tar.gz)


成为超级用户(root).


# gzip –cd ssh-2.3.0.tar.gz |tar xvf –


# cd ssh-2.3.0


# ./configure

注意,如果你希望用tcp_wrappers来控制SSH,那么在configure时需要加上选项“--with-libwrap=/path/to/libwrap/”,
用来告诉SSH关于libwrap.a 和tcpd.h的位置。


# make


# make install

和SSH有关的程序都放置在/usr/local/bin下,包括ssh,sftp,sshd2, ssh-keygen等。


二、配置

SSH的配置文件在/etc/ssh2下,其中包括sshd2的主机公钥和私钥:hostkey和hostkey.pub。这两个文件通常是在安装SSH时自动生成的。你可以通过下面的命令重新来生成它们:

# rm /etc/ssh2/hostkey*

# ssh-keygen2 –P /etc/ssh2/hostkey

而ssh2_config 文件一般情形下无需修改。


三、启动sshd2

每个要使用SSH的系统都必须在后台运行sshd2。用手工启动:

# /usr/local/bin/sshd2&

可以在“/etc/rc2.d/S99local”中加入该命令,这样系统每次启动时会自动启动sshd2。


四、用tcp_wrappers控制SSH

安装SSH的站点可以用tcp_wrappers来限制哪些IP地址可以通过ssh来访问自己。比如,在/etc/hosts.allow中加入

sshd,sshd2: 10.0.0.1

那么只有10.0.0.1可以通过ssh来访问该主机。


以上都是系统管理员完成的工作。下面我们说说普通用户如何使用SSH。


五、基本应用

每个用户在使用SSH之前,都要完成以下步骤:


在本地主机(比如,local.pku.edu.cn)上生成自己的ssh公钥和私钥。命令如下:

local# ssh-keygen

Generating 1024-bit dsa key pair

1 oOo.oOo.o

Key generated.

1024-bit dsa, teng@ns, Fri Oct 20 2000 17:27:05

Passphrase :************ /*在此输入你的口令,以后访问这台主机时要用。

Again :************ /*

Private key saved to /home1/teng/.ssh2/id_dsa_1024_a

Public key saved to /home1/teng/.ssh2/id_dsa_1024_a.pub


生成的私钥和公钥(id_dsa_1024_a和id_dsa_1024_a.pub)存放在你家目录的~/.ssh2目录下。和用户相关的SSH配
置文件都在~/.ssh2下。私钥由用户保存在本地主机上,而公钥需传送到远地主机的你自己的帐号的~/.ssh2下,如
果你要用ssh2访问本地主机的话。


在~/.ssh2下创建“identification”文件用来说明进行身份认证的私钥。命令如下:


local:~/.ssh2# echo "IdKey id_dsa_1024_a" > identification


3.同样地,在远地主机(比如,remote.pku.edu.cn)上完成上面步骤。

4.将本地(local.pku.edu.cn)下你自己(这里是“teng”)的公钥(id_dsa_1024_a.pub)拷贝到远地主机
(remote.pku.edu.cn)上你自己家目录下的.ssh2目录下,可命名为“local.pub”,一般用ftp上传即可。


在远地主机上,你自己家目录的.ssh2目录下,创建“authorization”文件,其中指定用来进行身份认证的公钥文件。
命令如下:

remote:~/.ssh2# echo “Key local.pub” > authorization


现在你可以从本地用ssh2登录到远地系统了。命令如下:

local# ssh remote.pku.edu.cn

Passphrase for key "/home1/teng/.ssh2/id_dsa_1024_a" with comment "1024-bit dsa,

teng@ns, Fri Oct 20 2000 17:27:05":***********


这时会要你输入你的ssh口令(Passphrase)。验证通过后,即登录到remote主机上。




第一部分:协议概览

整个通讯过程中,经过下面几个阶段协商实现认证连接。

第一阶段:

由客户端向服务器发出 TCP 连接请求。TCP 连接建立后,客户端进入等待,服务器向客户端发送第一个报文,宣告自己
的版本号,包括协议版本号和软件版本号。协议版本号由主版本号和次版本号两部分组成。它和软件版本号一起构成形如:

"SSH-<主协议版本号>.<次协议版本号>-<软件版本号>\n"

的字符串。其中软件版本号字符串的最大长度为40个字节,仅供调试使用。客户端接到报文后,回送一个报文,内容也
是版本号。客户端响应报文里的协议版本号这样来决定:当与客户端相比服务器的版本号较低时,如果客户端有特定的
代码来模拟,则它发送较低的版本号;如果它不能,则发送自己的版本号。当与客户端相比服务器的版本号较高时,客
户端发送自己的较低的版本号。按约定,如果协议改变后与以前的相兼容,主协议版本号不变;如果不相兼容,则主主
协议版本号升高。

服务器接到客户端送来的协议版本号后,把它与自己的进行比较,决定能否与客户端一起工作。如果不能,则断开TCP
连接;如果能,则按照二进制数据包协议发送第一个二进制数据包,双方以较低的协议版本来一起工作。到此为止,这
两个报文只是简单的字符串,你我等凡人直接可读。

第二阶段:

协商解决版本问题后,双方就开始采用二进制数据包进行通讯。由服务器向客户端发送第一个包,内容为自己的 RSA主
机密钥(host key)的公钥部分、RSA服务密钥(server key)的公钥部分、支持的加密方法、支持的认证方法、次协议版本
标志、以及一个 64 位的随机数(cookie)。这个包没有加密,是明文发送的。客户端接收包后,依据这两把密钥和被称
为cookie的 64 位随机数计算出会话号(session id)和用于加密的会话密钥(session key)。随后客户端回送一个包给服
务器,内容为选用的加密方法、cookie的拷贝、客户端次协议版本标志、以及用服务器的主机密钥的公钥部分和服务密钥
的公钥部分进行加密的用于服务器计算会话密钥的32 字节随机字串。除这个用于服务器计算会话密钥的 32字节随机字串
外,这个包的其他内容都没有加密。之后,双方的通讯就是加密的了,服务器向客户端发第二个包(双方通讯中的第一个
加密的包)证实客户端的包已收到。

第三阶段:

双方随后进入认证阶段。可以选用的认证的方法有:

(1) ~/.rhosts 或 /etc/hosts.equiv 认证(缺省配置时不容许使用它);
(2) 用 RSA 改进的 ~/.rhosts 或 /etc/hosts.equiv 认证;
(3) RSA 认证;
(4) 口令认证。

如果是使用 ~/.rhosts 或 /etc/hosts.equiv 进行认证,客户端使用的端口号必须小于1024。

认证的第一步是客户端向服务器发 SSH_CMSG_USER 包声明用户名,服务器检查该用户是否存在,确定是否需要进行认证。
如果用户存在,并且不需要认证,服务器回送一个SSH_SMSG_SUCCESS 包,认证完成。否则,服务器会送一个
SSH_SMSG_FAILURE 包,表示或是用户不存在,或是需要进行认证。注意,如果用户不存在,服务器仍然保持读取从客户端
发来的任何包。除了对类型为 SSH_MSG_DISCONNECT、SSH_MSG_IGNORE 以及 SSH_MSG_DEBUG 的包外,对任何类型的包都以
SSH_SMSG_FAILURE 包。用这种方式,客户端无法确定用户究竟是否存在。

如果用户存在但需要进行认证,进入认证的第二步。客户端接到服务器发来的 SSH_SMSG_FAILURE 包后,不停地向服务器
发包申请用各种不同的方法进行认证,直到时限已到服务器关闭连接为止。时限一般设定为 5 分钟。对任何一个申请,
如果服务器接受,就以 SSH_SMSG_SUCCESS 包回应;如果不接受,或者是无法识别,则以 SSH_SMSG_FAILURE 包回应。

第四阶段:

认证完成后,客户端向服务器提交会话请求。服务器则进行等待,处理客户端的请求。在这个阶段,无论什么请求只要成
功处理了,服务器都向客户端回应 SSH_SMSG_SUCCESS包;否则回应 SSH_SMSG_FAILURE 包,这表示或者是服务器处理请求
失败,或者是不能识别请求。会话请求分为这样几类:申请对数据传送进行压缩、申请伪终端、启动 X11、TCP/IP 端口
转发、启动认证代理、运行 shell、执行命令。到此为止,前面所有的报文都要求 IP 的服务类型(TOS)使用选项
IPTOS_THROUGHPUT。

第五阶段:

会话申请成功后,连接进入交互会话模式。在这个模式下,数据在两个方向上双向传送。此时,要求 IP 的服务类型(TOS)
使用 IPTOS_LOWDELAY 选项。当服务器告知客户端自己的退出状态时,交互会话模式结束。

(注意:进入交互会话模式后,加密被关闭。在客户端向服务器发送新的会话密钥后,加密重新开始。用什么方法加密由
客户端决定。)

第二部分:密钥的交换和加密的启动

在服务器端有一个主机密钥文件,它的内容构成是这样的:

1. 私钥文件格式版本字符串;
2. 加密类型(1 个字节);
3. 保留字(4 个字节);
4. 4 个字节的无符号整数;
5. mp 型整数;
6. mp 型整数;
7. 注解字符串的长度;
8. 注解字符串;
9. 校验字(4 个字节);
10. mp 型整数;
11. mp 型整数;
12. mp 型整数;
13. mp 型整数;

其中 4、5、6 三个字段构成主机密钥的公钥部分;10、11、12、13 四个字段构成主机密钥的私钥部分。9、10、11、12、13
五个字段用字段 2 的加密类型标记的加密方法进行了加密。4 个字节的校验字交叉相等,即第一个字节与第三个字节相等,
第二个字节与第四个字节相等。在服务器读取这个文件时进行这种交叉相等检查,如果不满足这个条件,则报错退出。

服务器程序运行的第一步,就是按照上面的字段划分读取主机密钥文件。随后生成一个随机数,再调用函数

void rsa_generate_key
(
RSAPrivateKey *prv,
RSAPublicKey *pub,
RandomState *state,
unsigned int bits
);

生成服务密钥,服务密钥也由公钥和私钥两部分组成。上面的这个函数第一个指针参数指向服务密钥的私钥部分,第二个指
向公钥部分。然后把主机密钥的公钥部分和服务密钥的公钥部分发送给客户端。在等到客户端回应的包后,服务器用自己的
主机密钥的私钥部分和服务密钥的私钥部分解密得到客户端发来的 32 字节随机字串。然后计算自己的会话号,并用会话号
的前 16字节 xor 客户端发来的 32 字节随机字串的前 16 字节,把它作为自己的会话密钥。注意,服务器把8个字节的
cookie、主机密钥的公钥部分、和服务密钥的公钥部分作为参数来计算自己的会话号。

再来看客户端。客户端启动后的第一步骤也是读取主机密钥。然后等待服务器主机密钥、服务密钥、和 8个字节的cookie。
注意,服务器发送来的只是主机密钥和服务密钥的公钥部分。接到包后,客户端立即把从服务器端收到cookie、主机密钥、
和服务密钥作为参数计算出会话号。从上面可以看出,服务器和客户端各自计算出的会话号实际是一样的。

随后,客户端检查用户主机列表和系统主机列表,查看从服务器收到的主机密钥是否在列表中。如果不在列表中,则把它加
入列表中。然后就生成 32 字节的随机字串,这个32 字节的随机字串就是客户端的会话密钥。客户端用 16字节的会话密钥
xor 它的前 16 字节,把结果用服务器的主机密钥和服务密钥进行双重加密后发送给服务器。产生 32字节随机字串时,随
机数种子由两部分组成,其中一部分从系统随机数种子文件中得到,这样来避免会话密钥被猜出。从上面服务器和客户端各
自计算会话密钥的过程可以看出,服务器和客户端计算出的会话密钥是一样的。

上面的这几步,总结起来就要交换确定会话密钥,因为无论是 des、idea、3des、arcfour、还是 blowfish 都是对称加密方
法,只有一把密钥,双方都知道了会话密钥才能启动加密。但会话密钥不能在网络上明文传送,否则加密就失去意义了。于
是使用 RSA 公钥体系对会话密钥进行加密。

RSA 公钥体系的办法是用公钥加密私钥解密,它依据这样的数学定理:

若 p、q 是相异的两个质数,整数 r 和 m 满足
rm == 1 (mod (p-1)(q-1))
a 是任意的整数,整数 b、c 满足 b == a^m (mod pq),
c == b^r (mod pq)。则
c == a (mod pq)。

具体实现是这样的:

(1) 找三个正整数 p、q、r,其中 p、q 是相异的质数,
r 是与(p-1)、(q-1)互质的数。这三个数 p、q、r
就是私钥(private key)。
(2) 再找一个正整数 m 满足 rm == 1 (mod(p-1)(q-1))。
计算 n = pq,m、n 就是公钥(public key)。
(3) 被加密对象 a 看成是正整数,设 a <>= n,
将 a 表示成 s (s < s =" 2^t)" b ="="" c ="="" c =" a。">q);
mpz_init(&prv->p);
mpz_init(&prv->e);
mpz_init(&prv->d);
mpz_init(&prv->u);
mpz_init(&prv->n);
mpz_init(&test);
mpz_init(&aux);

/* 计算质数 p、q 的位数 */
pbits = bits / 2;
qbits = bits - pbits;

retry0:

fprintf(stderr, "Generating p: ");

/* 生成随机质数 p */
rsa_random_prime(&prv->p, state, pbits);

retry:

fprintf(stderr, "Generating q: ");

/* 生成随机质数 q */
rsa_random_prime(&prv->q, state, qbits);

/* 判断是否 p == q,如果是返回重新生成 */
ret = mpz_cmp(&prv->p, &prv->q);
if (ret == 0)
{
fprintf(stderr,
"Generated the same prime twice!\n");
goto retry;
}
if (ret > 0)
{
mpz_set(&aux, &prv->p);
mpz_set(&prv->p, &prv->q);
mpz_set(&prv->q, &aux);
}

/* 确定 p、q 是否很接近 */
mpz_sub(&aux, &prv->q, &prv->p);
mpz_div_2exp(&test, &prv->q, 10);
if (mpz_cmp(&aux, &test) <>p, &prv->q);
if (mpz_cmp_ui(&aux, 1) != 0)
{
fprintf(stderr,
"The primes are not relatively prime!\n");
goto retry;
}

/* 从质数 p、q 导出私钥 */
fprintf(stderr, "Computing the keys...\n");
derive_rsa_keys(&prv->n, &prv->e, &prv->d,
&prv->u, &prv->p, &prv->q, 5);
prv->bits = bits;

/* 从质数 p、q 导出公钥 */
pub->bits = bits;
mpz_init_set(&pub->n, &prv->n);
mpz_init_set(&pub->e, &prv->e);

/* 测试公钥和密钥是否有效 */
fprintf(stderr, "Testing the keys...\n");
rsa_random_integer(&test, state, bits);
mpz_mod(&test, &test, &pub->n); /* must be less than n. */
rsa_private(&aux, &test, prv);
rsa_public(&aux, &aux, pub);
if (mpz_cmp(&aux, &test) != 0)
{
fprintf(stderr,
"**** private+public failed to decrypt.\n");
goto retry0;
}

rsa_public(&aux, &test, pub);
rsa_private(&aux, &aux, prv);
if (mpz_cmp(&aux, &test) != 0)
{
fprintf(stderr,
"**** public+private failed to decrypt.\n");
goto retry0;
}

mpz_clear(&aux);
mpz_clear(&test);

fprintf(stderr, "Key generation complete.\n");
}
_______________________________________________________

在上面的函数成一对密钥时,首先调用函数
_______________________________________________________

void rsa_random_prime
(
MP_INT *ret, RandomState *state,
unsigned int bits
)
{
MP_INT start, aux;
unsigned int num_primes;
int *moduli;
long difference;

mpz_init(&start);
mpz_init(&aux);

retry:

/* 挑出一个随机的足够大的整数 */
rsa_random_integer(&start, state, bits);

/* 设置最高的两位 */
mpz_set_ui(&aux, 3);
mpz_mul_2exp(&aux, &aux, bits - 2);
mpz_ior(&start, &start, &aux);
/* 设置最低的两位为奇数 */
mpz_set_ui(&aux, 1);
mpz_ior(&start, &start, &aux);

/* 启动小质数的 moduli 数 */
moduli = malloc(MAX_PRIMES_IN_TABLE * sizeof(moduli[0]));
if (moduli == NULL)
{
printf(stderr, "Cann't get memory for moduli\n");
exit(1);
}
if (bits < num_primes =" 0;" num_primes =" 0;" difference =" 0;"> 0x70000000)
{
fprintf(stderr, "rsa_random_prime: "
"failed to find a prime, retrying.\n");
if (moduli != NULL)
free(moduli);
else
exit(1);
goto retry;
}

/* 检查它是否是小质数的乘积 */
for (i = 0; i <>= small_primes[i])
moduli[i] -= small_primes[i];
if (moduli[i] + difference == 0)
break;
}
if (i < phi =" (p">= 0)
{
fprintf(stderr, "Warning: G=");
mpz_out_str(stdout, 10, &G);
fprintf(stderr,
" is large (many spare key sets); key may be bad!\n");
}

/* F = phi / G; the number of relative prime
numbers per spare key set. */
mpz_div(&F, φ, &G);

/* Find a suitable e (the public exponent). */
mpz_set_ui(e, 1);
mpz_mul_2exp(e, e, ebits);
mpz_sub_ui(e, e, 1); /*make lowest bit 1, and substract 2.*/
/* Keep adding 2 until it is relatively prime
to (p-1)(q-1). */
do
{
mpz_add_ui(e, e, 2);
mpz_gcd(&aux, e, φ);
}
while (mpz_cmp_ui(&aux, 1) != 0);

/* d is the multiplicative inverse of e, mod F.
Could also be mod (p-1)(q-1); however, we try to
choose the smallest possible d. */
mpz_mod_inverse(d, e, &F);

/* u is the multiplicative inverse of p, mod q,
if p < n =" p" bytes =" (bits" str =" xmalloc(bytes" i =" 0;" bytes =" (host_key_bits" buf =" xmalloc(bytes);" i =" len;">= 4; i -= 4)
{
unsigned long limb = mpz_get_ui(&aux);
PUT_32BIT(buf + i - 4, limb);
mpz_div_2exp(&aux, &aux, 32);
}
for (; i > 0; i--)
{
buf[i - 1] = mpz_get_ui(&aux);
mpz_div_2exp(&aux, &aux, 8);
}
mpz_clear(&aux);
}

随后客户端计算会话密钥,计算过程是首先生成32个字节即256位随机字串:

for (i = 0; i < i =" 0;"> /usr/openwin/bin/xauth list
***.***.***/unix:10 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:11 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
***.***.***:10 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:10 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
***.***.***:11 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
***.***.***/unix:11 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
[wangdb@ /home/wangdb]> echo $DISPLAY
***.***.***:10.0
[wangdb@ /home/wangdb]> /usr/openwin/bin/xauth
Using authority file /home/wangdb/.Xauthority
xauth> list ***.***.***:10.0
***.***.***:10 MIT-MAGIC-COOKIE-1 \
92b404e556588ced6c1acd4ebf053f68
xauth> quit
[wangdb@ /home/wangdb]>

.Xauthority 文件的显示记录里各个字段的含义如下,第一个字段的***.***.*** 是主机名,":"号后的"."前面的数字
是 X 服务器标号,"."后面的数字是显示屏幕(显示器)标号。这个字段称为显示名,$DISPLAY 环境变量里填入这个字段。
第二个字段是协议标号,第三个字段是十六进制的认证钥。认证钥是由系统给的,打开 X 显示时如果认证钥给的不对,
X 服务器拒绝处理显示请求。

ssh 实现 X 转发的第一步是,客户端调用 popen 函数执行 "xauth list $DISPLAY" 命令,读取 X 显示的屏幕号、协议
号、和认证钥,然后把协议号和认证钥保存在内存中。客户端并不把自己的认证钥发送给服务器,而是生成一个 8位二进
制随机数序列,以十六进制打印,把这个十六进制数字串发送给服务器作为认证钥。等到服务器发来打开 X 显示请求时,
客户端使用自己真正的认证钥打开 X 显示。采用这种方法,客户保证了自己的认证钥不会泄露给外界,安全性得到保证。

服务器接到客户端的 X 转发请求后,读取客户端发来的屏幕号、协议号、和认证钥,然后打开一个 socket 并绑定它,
设置成侦听模式,并用这个 socket 设置一个通道。随后就从服务器自己的配置文件读出 X 服务器标号,调用
gethostname函数获取本机主机名,把这两者和客户发来的屏幕号结合在一起构成显示列表记录的第一字段。

在服务器处理客户端执行命令或启动 shell 的请求时,它用前面设置的通道接受一个 TCP 连接,返回一个 socket,
再用这个 socket 设置一个新通道。然后发一个包给客户端要求它打开一个 X 显示。客户端接到这个包后打开一个
socket 与本地 X 服务器连接,即打开一个 X 显示:
_____________________________________________________
int display_number, sock;
const char *display;
struct sockaddr_un ssun;

/* Try to open a socket for the local X server. */
display = getenv("DISPLAY");
if (!display)
{
error("DISPLAY not set.");
goto fail;
}

/* Now we decode the value of the DISPLAY variable
* and make a connection to the real X server.
*/

/* Check if it is a unix domain socket. Unix domain
* displays are in one of the following formats:
* unix:d[.s], :d[.s], ::d[.s]
*/
if (strncmp(display, "unix:", 5) == 0 ||
display[0] == ':')
{
/* Connect to the unix domain socket. */
if (sscanf(strrchr(display, ':') + 1,
"%d", &display_number) != 1)
{
error("Could not parse display number "
"from DISPLAY: %.100s", display);
goto fail;
}
/* Create a socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < sun_family =" AF_UNIX;">