一. 基础环境准备
操作系统:Ubuntu16.04Server
先
sudo apt-get install vim openssh-server
便于后续上传源码以及调试。
看一下现在openssh的版本:
zjd@ubuntu:~$ ssh -V
OpenSSH_7.2p2 Ubuntu-4ubuntu2.6, OpenSSL 1.0.2g 1 Mar 2016
安装编译所需要的库:
zjd@ubuntu:~$ sudo apt-get install build-essential
二. 源码下载
从https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/下载最新的openssh-7.9p1.tar.gz;
从http://www.zlib.net/下载最新的zlib-1.2.11.tar.gz;
从https://www.openssl.org/source/下载最新的openssl-1.1.1a.tar.gz。
/home/zjd/下新建mySSH目录,然后将这三个压缩包上传上去后用tar分别解压:
zjd@ubuntu:~/mySSH$ tar -zxvf zlib-1.2.11.tar.gz
。。。
zjd@ubuntu:~/mySSH$ ll
total 10312
drwxrwxrwx 5 root root 4096 Dec 17 16:41 ./
drwxr-xr-x 4 root root 4096 Dec 17 16:27 ../
drwxr-xr-x 5 zjd zjd 12288 Oct 18 18:06 openssh-7.9p1/
-rw-rw-r-- 1 zjd zjd 1565384 Dec 17 16:39 openssh-7.9p1.tar.gz
drwxr-xr-x 19 zjd zjd 4096 Nov 20 05:35 openssl-1.1.1a/
-rw-rw-r-- 1 zjd zjd 8350547 Dec 17 16:39 openssl-1.1.1a.tar.gz
drwxr-xr-x 14 zjd zjd 4096 Jan 15 2017 zlib-1.2.11/
-rw-rw-r-- 1 zjd zjd 607698 Dec 17 16:39 zlib-1.2.11.tar.gz
三. 编译安装
1. zlib
进文件夹后直接配置编译安装:
zjd@ubuntu:~/mySSH/zlib-1.2.11$ ./configure --prefix=/usr/local
zjd@ubuntu:~/mySSH/zlib-1.2.11$ make
zjd@ubuntu:~/mySSH/zlib-1.2.11$ sudo make install
2. openssl
注意要先卸载旧版本:
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ sudo apt-get purge openssl
删除旧配置文件:
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ rm -rf /etc/ssl
然后配置编译安装新版本:
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ ./config --prefix=/usr/local --openssldir=/usr/local/ssl
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ make
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ sudo make install
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ ./config shared --prefix=/usr/local --openssldir=/usr/local/ssl
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ make clean
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ make
zjd@ubuntu: ~/mySSH/openssl-1.1.1a$ sudo make install
其中:prefix 是安装目录,openssldir 是配置文件目录,另外建议安装两次,shared 作用是生成动态连接库。
最后,因为是非root用户安装,因此需要增加两条软连接:
zjd@ubuntu:~/mySSH/openssl-1.1.1a$ sudo ln -s /usr/local/lib/libssl.so.1.1 /usr/lib/libssl.so.1.1
zjd@ubuntu:~/mySSH/openssl-1.1.1a$ sudo ln -s /usr/local/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1
OK,现在看一下openssl的版本:
zjd@ubuntu:~/mySSH/openssl-1.1.1a$ openssl version
OpenSSL 1.1.1a 20 Nov 2018
3. openssh
zjd@ubuntu:~/mySSH/openssh-7.9p1$ ./configure -prefix=/usr/local -sysconfdir=/etc/ssh -with-ssl-dir=/usr/local/ssl
zjd@ubuntu:~/mySSH/openssh-7.9p1$ make
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo make install
安装完重启服务:
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo systemctl restart sshd.service
现在再看一下ssh的版本:
zjd@ubuntu:~/mySSH/openssh-7.9p1$ ssh -V
OpenSSH_7.9p1, OpenSSL 1.1.1a 20 Nov 2018
四. 修改openssh源码
当修改了openssh源码后,为了不影响系统已启动的ssh服务的运行,应该重新配置编译输出路径,然后停掉系统已启动的ssh服务,启动新编译的sshd。过程如下:
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo vim session.c
zjd@ubuntu:~/mySSH/openssh-7.9p1$ make clean
zjd@ubuntu:~/mySSH/openssh-7.9p1$ ./configure -prefix=/usr/local/myssh -sysconfdir=/etc/ssh -with-ssl-dir=/usr/local/ssl
zjd@ubuntu:~/mySSH/openssh-7.9p1$ make
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo make install
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo /etc/init.d/ssh stop
zjd@ubuntu:~/mySSH/openssh-7.9p1$ sudo /usr/local/myssh/sbin/sshd
再次继续修改时,最后两行停止和启动服务的操作就不用执行了。
如果重启了系统,则最后两步要重新执行一下。当然,也可以写个开机启动脚本,这样系统启动时就自动执行最后两行了,方法如下:
在/etc/init.d/目录下新建一个脚本文件startmyssh.sh,并赋予运行权限:
zjd@ubuntu:/etc/init.d$ sudo chmod 755 startmyssh.sh
vim编辑脚本内容为:
#!/bin/bash
### BEGIN INIT INFO
# Provides: Zhang Jidong
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: sshd service
# Description: mysshd service
### END INIT INFO
sudo /etc/init.d/ssh stop
sudo /usr/local/myssh/sbin/sshd
exit 0
注意前面的注释部分格式一定要一样,否则会有各种警告;
最后添加启动调用:
zjd@ubuntu:/etc/init.d$ sudo update-rc.d startmyssh.sh defaults 90
好了,现在可以重启试试了!
五. openssh日志
openssh的配置文件位于:
zjd@ubuntu:/etc/ssh$ sudo vim sshd_config
# Logging
SyslogFacility AUTH
LogLevel INFO
详细定义位于log.h,如下:
/* Supported syslog facilities and levels. */
typedef enum {
SYSLOG_FACILITY_DAEMON,
SYSLOG_FACILITY_USER,
SYSLOG_FACILITY_AUTH,
#ifdef LOG_AUTHPRIV
SYSLOG_FACILITY_AUTHPRIV,
#endif
SYSLOG_FACILITY_LOCAL0,
SYSLOG_FACILITY_LOCAL1,
SYSLOG_FACILITY_LOCAL2,
SYSLOG_FACILITY_LOCAL3,
SYSLOG_FACILITY_LOCAL4,
SYSLOG_FACILITY_LOCAL5,
SYSLOG_FACILITY_LOCAL6,
SYSLOG_FACILITY_LOCAL7,
SYSLOG_FACILITY_NOT_SET = -1
} SyslogFacility;
typedef enum {
SYSLOG_LEVEL_QUIET,
SYSLOG_LEVEL_FATAL,
SYSLOG_LEVEL_ERROR,
SYSLOG_LEVEL_INFO,
SYSLOG_LEVEL_VERBOSE,
SYSLOG_LEVEL_DEBUG1,
SYSLOG_LEVEL_DEBUG2,
SYSLOG_LEVEL_DEBUG3,
SYSLOG_LEVEL_NOT_SET = -1
} LogLevel;
SyslogFacility配置为AUTH,则日志会输出到/var/log/auth.log
六. sftp日志
sftp是openssh根据功能需要启动的子进程,缺省是关闭了日志的。打开方法为:
sudo vim /etc/ssh/sshd_config
在Subsystem所在行最后增加 -l DEBUG3,如下:
Subsystem sftp /usr/libexec/openssh/sftp-server -l DEBUG3
将sftp的日志单独存放:
sudo vim /etc/rsyslog.conf
最后面增加如下内容:
# Log sftp-server in a separate file
:programname, isequal, "sftp-server" /var/log/sftp.log
现在就可以重启sshd服务和rsyslog服务来生效了:
sudo systemctl restart rsyslog.service
sudo systemctl restart sshd.service
看:
zjd@ubuntu:/var/log$ ll -t
total 1532
-rw-r----- 1 syslog adm 379366 Dec 20 04:58 syslog
-rw-r----- 1 syslog adm 61409 Dec 20 04:53 auth.log
-rw-r----- 1 syslog adm 6460 Dec 20 04:41 sftp.log
drwxrwxr-x 7 root syslog 4096 Dec 20 04:40 ./
drwxr-xr-x 12 root root 4096 Dec 20 04:31 ../
-rw-r--r-- 1 root root 568865 Dec 20 04:25 dpkg.log
-rw-rw-r-- 1 root utmp 292292 Dec 20 04:22 lastlog
-rw-rw-r-- 1 root utmp 5376 Dec 20 04:22 wtmp
-rw-r----- 1 syslog adm 348026 Dec 20 04:22 kern.log
……
建议设置logrotate避免日志过大。方法是sudo vim创建/etc/logrotate.d/sftp文件,输入下面内容并保存:
/var/log/sftp.log
{
postrotate
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
如果此时通过FileZilla客户端访问openssh-server,查看文件列表以及上传下载等操作,可以通过日志发现这些操作的实现都在sftp-server.c中。
七. 限制sftp用户只能访问指定目录
添加用户test1:
zjd@ubuntu:/home$ sudo useradd test1
为test1设置密码:
zjd@ubuntu:/home$ sudo passwd test1
编辑
sudo vim /etc/ssh/sshd_config
将已有的Subsystem sftp /usr/lib/openssh/sftp-server -l DEBUG3改为:
Subsystem sftp internal-sftp -l DEBUG3
并在最后为test1增加如下配置:
Match User test1
ChrootDirectory /home/test1
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp -l DEBUG3
创建好目录:
zjd@ubuntu:/home# sudo mkdir test1
权限设置:
zjd@ubuntu:/home$ sudo chown root:root /home/test1
zjd@ubuntu:/home$ sudo chmod 755 /home/test1
zjd@ubuntu:/home$ sudo usermod test1 -s /sbin/nologin
现在重启sshd服务,可以发现已经是只能登录到/home/test1目录中了。但此时没有写权限,那就继续如下配置,给test1配置一个可以读写的data文件夹:
zjd@ubuntu:/home/test1# sudo mkdir data
zjd@ubuntu:/home/test1# sudo chown test1:test1 /home/test1/data/
zjd@ubuntu:/home/test1# sudo chmod 755 /home/test1/data/
好了,现在用户通过sftp登录后,就能看到一个data文件夹,并且在这个data文件夹里面可以进行读写操作了。
此时前面加的-l DEBUG3参加(打印sftp.log)无效了,需要再进行如下配置:
/home/test1下新建dev文件夹:
zjd@ubuntu:/home/test1# sudo mkdir dev
编辑sudo vim /etc/rsyslog.conf,最后面新增:
# Create an additional socket for some of the sshd chrooted users.
$AddUnixListenSocket /home/test1/dev/log
# Log internal-sftp in a separate file
:programname, isequal, "sftp" /var/log/sftp.log
(如果增加多个用户,只需重复增加AddUnixListenSocket命令即可。)
编辑
root@ubuntu:/home# vim /etc/selinux/config
新增:
SELINUX=disabled
重启一下日志服务:
zjd@ubuntu:/home/test1# sudo systemctl restart rsyslog.service
好了,现在重启系统吧!
此时sftp用户登录后看到的是两个文件夹data和dev,如果自动进入data文件夹,则体验更好。此时就需要修改sftp-server.c,在函数process_realpath中增加如下
if (strlen(path) == 1 && !strcmp(path, "."))部分的内容:
static void
process_realpath(u_int32_t id)
{
char resolvedname[PATH_MAX];
char *path;
int r;
if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (path[0] == '\0') {
free(path);
path = xstrdup(".");
}
debug3("request %u: realpath", id);
verbose("realpath \"%s\"", path);
if (strlen(path) == 1 && !strcmp(path, "."))
{
logit("======== Change path from '.' to '/data/.'");
free(path);
path = xstrdup("/data/.");
}
if (realpath(path, resolvedname) == NULL) {
send_status(id, errno_to_portable(errno));
} else {
Stat s;
attrib_clear(&s.attrib);
s.name = s.long_name = resolvedname;
send_names(id, 1, &s);
}
free(path);
}