在你的 Linux 机器上”安全的”给别人开一个终端
这是我的需求:
- 用户需要在我的机器上搭建一个网站
- 让用户可以用密码登录
- 让用户可以用 WinSCP / FileZilla 这类软件通过 SFTP 协议上传下载文件
- 让用户可以访问终端来打包解压文件
- 不能让用户访问服务器内其它的文件,因为用户是不可信的
- 用户除了能自己的家目录以外,只能访问他的网站目录
折腾一圈下来真的太累了。
0x00 一切是怎么实现的?
我们可以从 sshd_config 的 man page 里发现 ChrootDirectory 这个设置项:
The ChrootDirectory must contain the necessary files and directo ries to support the user’s session. For an interactive session this requires at least a shell, typically sh(1), and basic /dev nodes such as null(4), zero(4), stdin(4), stdout(4), stderr(4), arandom(4) and tty(4) devices. For file transfer sessions using “sftp”, no additional configuration of the environment is necessary if the in-process sftp server is used, though sessions which use logging do require /dev/log inside the chroot directory.
简言之,通过创建一个 SSH chroot “监狱”,你可以给你不信任的用户 ssh 权限,并且可以限制他能运行的命令(例如 ls、date 和 bash 的内置命令)。
这篇教程是在 Debian 10 + OpenSSH 环境下作为例子的。
0x01 切换成 root 用户
尝试以下命令:
$ sudo -s
$ # 或者更干脆
$ su -
0x02 创建 chroot “监狱”
在这里,我创建一个目录叫做 /home/sshjail
作为该用户连接至的根目录:
# mkdir -p /home/sshjail
然后根据 sshd 的 man page 所说,你还需要这些必须的文件:
# ls -l /dev/{null,zero,stdin,stdout,stderr,random,tty}
在终端里的输出是类似这样的:
crw-rw-rw- 1 root root 1, 3 May 25 22:26 /dev/null
crw-rw-rw- 1 root root 1, 8 May 25 22:26 /dev/random
lrwxrwxrwx 1 root root 15 May 25 22:26 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 May 25 22:26 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 May 25 22:26 /dev/stdout -> /proc/self/fd/1
crw-rw-rw- 1 root tty 5, 0 May 26 00:28 /dev/tty
crw-rw-rw- 1 root root 1, 5 May 25 22:26 /dev/zero
为了创建相同的文件,我们需要使用 mknod 命令:
# mkdir -p /home/sshjail/dev/
# mknod -m 666 /home/sshjail/dev/null c 1 3
# mknod -m 666 /home/sshjail/dev/tty c 5 0
# mknod -m 666 /home/sshjail/dev/zero c 1 5
# mknod -m 666 /home/sshjail/dev/random c 1 8
0x03 设置权限
因为 ChrootDirectory 规定,上级目录的拥有者必须是 root,并且不能被任何人写:
# chown root:root /home/sshjail
# chmod 0755 /home/sshjail
你可以使用以下命令来验证是否正确:
# ls -ld /home/sshjail
在终端里的输出是类似这样的:
drwxr-xr-x 9 root root 4096 May 25 23:31 /home/sshjail
0x04 给一个 bash 终端
Nice,接下来我们需要一个终端,让用户可以运行我们想让他运行的命令。
首先,创建 /bin
目录:
# mkdir -p /home/sshjail/bin
然后复制 /bin/bash
到我们创建的 /bin
目录:
# cp -v /bin/bash /home/sshjail/bin
这里,
-v
指的是 verbose,做了什么都输出出来
在终端里的输出是类似于这样的:
‘/bin/bash’ -> ‘/home/sshjail/bin/bash’
之后,我们还需要将 bash 使用的共享库一并复制进去,否则 bash 无法运行:
# ldd /bin/bash
在终端里的输出是类似于这样的:
linux-vdso.so.1 (0x00007ffc9b5ea000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f6571ea9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6571ea4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6571ce3000)
/lib64/ld-linux-x86-64.so.2 (0x00007f657200f000)
如果你的输出和我的略有出入,这是正常的现象。请仿照我使用的命令来完成,不要直接复制我的命令。
哈!现在我们知道要复制什么了,那就开工吧:
# mkdir -p /home/sshjail/lib/
# mkdir -p /home/sshjail/lib64/
# mkdir -p /home/sshjail/lib/x86_64-linux-gnu/
# cp -v /lib/x86_64-linux-gnu/{libtinfo.so.6,libdl.so.2,libc.so.6} /home/sshjail/lib/x86_64-linux-gnu/
在终端里的输出是类似于这样的:
‘/lib/x86_64-linux-gnu/libtinfo.so.6’ -> ‘/home/sshjail/lib/libtinfo.so.6’
‘/lib/x86_64-linux-gnu/libdl.so.2’ -> ‘/home/sshjail/lib/libdl.so.2’
‘/lib/x86_64-linux-gnu/libc.so.6’ -> ‘/home/sshjail/lib/libc.so.6’
我们还有一个 ld-linux-x86-64.so.2
也需要复制过来:
# cp -v /lib64/ld-linux-x86-64.so.2 /home/sshjail/lib64/
在终端里的输出是类似于这样的:
‘/lib64/ld-linux-x86-64.so.2’ -> ‘/home/sshjail/lib64/ld-linux-x86-64.so.2’
最后,复制 /lib/x86_64-linux-gnu/libnss_files*
:
# cp -va /lib/x86_64-linux-gnu/libnss_files* /home/sshjail/lib/x86_64-linux-gnu/
在终端里的输出是类似于这样的:
'/lib/x86_64-linux-gnu/libnss_files-2.28.so' -> '/home/sshjail/lib/x86_64-linux-gnu/libnss_files-2.28.so'
'/lib/x86_64-linux-gnu/libnss_files.so.2' -> '/home/sshjail/lib/x86_64-linux-gnu/libnss_files.so.2'
0x05 添加用户
万事俱备,只欠用户。首先,我们要创建一名用户,在这里,你也可以创建多名用户:
# adduser andy
然后,我们要将 /etc/{passwd,group}
复制到我们的”监狱”里。
# mkdir -p /home/sshjail/etc/
# cp -vf /etc/{passwd,group} /home/sshjail/etc/
在终端里的输出是类似于这样的:
‘/etc/passwd’ -> ‘/home/sshjail/etc/passwd’
‘/etc/group’ -> ‘/home/sshjail/etc/group’
注意!如果你后面对用户或者用户的密码做了任何更改,都请重新复制这两个文件。
0x06 设置 sshd
编辑 /etc/ssh/sshd_config
,我们要设置登录相关的东西了:
# nano /etc/ssh/sshd_config
在文件最后输入这些:
## Apply the chrooted jail to the user called andy ##
Match User andy
ChrootDirectory /home/sshjail
## Allow sftp to chrooted jail ##
ForceCommand internal-sftp
如果是多名用户的话,那么第一行应该改成这样:
Match User andy,antis,goblin
0x07 重启 sshd 服务
这个很简单吧!
# systemctl restart sshd
0x08 测试 & 给用户必要的目录
接下来,登入你之前设置的账号吧!可以发现,我们的确有了一个 bash,但我们连 ls 和 date 这种命令都运行不了,只能运行 pwd 之类的内置命令。不慌,我们还要做进一步的调整。
在此之前,我们可以发现在测试时,终端提醒我们没有家目录。先给用户家目录吧:
# mkdir -p /home/sshjail/home/andy
# chown -R andy:andy /home/sshjail/home/andy/
# chmod -R 0700 /home/sshjail/home/andy/
然后,我们需要给用户他的网站目录,让他可以查看和编辑。我们还要保证在重启之后,这个目录会被自动挂载上(因为第一句话是手动挂载):
# mkdir /home/sshjail/home/andy/web
# mount --bind /home/wwwroot/andy_web /home/sshjail/home/andy/web
# echo "/home/wwwroot/andy_web /home/sshjail/home/andy/web none bind" >> /etc/fstab
至于目录的权限,我是给了 andy:www
,然后把 www 添加进了 andy 用户组以备不时之需,静态网站是够了的。
0x09 安装其它的命令
首先,是不需要任何共享库的:
# cp -v /bin/ls /home/sshjail/bin/
# cp -v /bin/date /home/sshjail/bin/
# cp -v /bin/cp /home/sshjail/bin/
# cp -v /bin/mv /home/sshjail/bin/
直接复制过去就好了。如果是需要共享库的,那么首先使用 ldd
命令查看一下,再复制就好了。以下是我自己的环境所需要的东西的折腾过程:
xterm
可以发现,此时使用左右键(上下键可能有用)或者 Backspace 键的话,是无法正常执行我们的操作的。
如果你现在也安装了 nano 的话,你可以发现 nano 会报 Error opening terminal: Xterm
的错误。解决方法也很简单:
# mkdir -p /home/sshjail/usr/share/terminfo
# cp -r -v /usr/share/terminfo /home/sshjail/usr/share/
# cp -r -v /lib/terminfo /home/sshjail/lib/
i18n 问题
虽然英文能正常显示,但是中文是不能正常显示和输入的。这该怎么办呢?我想,大概是因为缺少了 locales 包吧。万幸的是,运行 locale 命令发现本身已经是 en_US.UTF-8
了。
# cp -v /usr/bin/locale /home/sshjail/usr/bin/
# cp -r -v /usr/share/i18n /home/sshjail/usr/share
# cp -r -v /usr/lib/locale /home/sshjail/usr/lib/
# cp -v /etc/default/locale /home/sshjail/etc/default
提示:如果知道包名,但不知道要复制哪些文件,可以去 pks.org 去看一看。例如 Debian 10 上 locales 这个包,在这个网站上可以显示包内有什么文件,这样你就知道要手动复制什么文件了。
不能用 SCP 协议上传下载文件
现在我们弄好了中文显示和 xterm,但不能用 SFTP。不过在此之前,我们可以先试试 SCP 协议!SCP 协议虽然能看目录,但是不能上传下载文件。解决方法也很简单:
# cp -v /usr/bin/scp /home/sshjail/usr/bin
SFTP 协议不能用
蛤,我 SSH 都起来了。一看,原来是缺少 openssh 相关的库。
# cp -v /usr/lib/sftp-server /home/sshjail/usr/lib/
# cp -r -v /usr/lib/openssh/ /home/sshjail/usr/lib/
tar 和 zip
tar 的安装很简单:
# cp -v /bin/tar /home/sshjail/bin
万一用户要用其它的打包算法呢:
# cp -v /bin/gzip /home/sshjail/bin
# cp -v /bin/bzip2 /home/sshjail/bin
# cp -v /lib/x86_64-linux-gnu/libbz2.so.1.0 /home/sshjail/lib/x86_64-linux-gnu
# cp -v /usr/bin/xz /home/sshjail/usr/bin
# cp -v /lib/x86_64-linux-gnu/liblzma.so.5 /home/sshjail/lib/x86_64-linux-gnu
zip 的安装也很简单:
# cp -v /usr/bin/zip /home/sshjail/usr/bin/
# cp -v /usr/bin/unzip /home/sshjail/usr/bin/
其它
失败的 locale 相关
# mkdir -p /home/sshjail/usr/sbin
# cp -v /usr/sbin/locale-gen /home/sshjail/usr/sbin
## locale-gen 还需要这些东西
# cp -v /bin/sh /home/sshjail/bin/
# cp -v /bin/sed /home/sshjail/bin/
# cp -v /bin/rm /home/sshjail/bin/
# cp -v /usr/bin/localedef /home/sshjail/usr/bin/
结果好像还是没运行起来?我也忘了运行结果了。
# cp -v /usr/sbin/update-locale /home/sshjail/usr/sbin
# # 草,这个东西要 perl
# cp -v /usr/bin/perl /home/sshjail/usr/bin/
# cp -r -v /usr/lib/x86_64-linux-gnu/perl /home/sshjail/usr/lib/x86_64-linux-gnu/perl
# cp -r -v /usr/share/perl /home/sshjail/usr/share
# cp -v /lib/x86_64-linux-gnu/libm.so.6 /home/sshjail/lib/x86_64-linux-gnu/
# cp -v /lib/x86_64-linux-gnu/libcrypt.so.1 /home/sshjail/lib/x86_64-linux-gnu/
# # 但其实还是不行,它需要 sudo,即使我们复制 sudo 过去还是不能直接用的
sudo 相关
# cp -v /usr/bin/sudo /home/sshjail/usr/bin/
# cp -r -v /usr/lib/sudo /home/sshjail/usr/lib/
# cp -v /lib/x86_64-linux-gnu/libaudit.so.1 /home/sshjail/lib/x86_64-linux-gnu/
# cp -v /lib/x86_64-linux-gnu/libutil.so.1 /home/sshjail/lib/x86_64-linux-gnu/
# cp -v /lib/x86_64-linux-gnu/libcap-ng.so.0 /home/sshjail/lib/x86_64-linux-gnu/
参考文献
- https://www.cyberciti.biz/faq/debian-ubuntu-restricting-ssh-user-session-to-a-directory-chrooted-jail/
- https://serverfault.com/questions/770541/can-connect-via-ssh-but-not-via-sftp-exit-status-127
如果喜欢本文,欢迎点击下方的「鼓掌」按钮!
如果上面没有加载出任何东西,可以点击这里。
如果喜欢本文,欢迎点击下方的「鼓掌」按钮!
如果上面没有加载出任何东西,可以点击这里。