6

在你的 Linux 机器上"安全的"给别人开一个终端 — 251's Blog.

 3 years ago
source link: https://blog.251.sh/debian-restricting-ssh-user-session-to-a-directory-chrooted-jail
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
May 26, 2021

这是我的需求:

  • 用户需要在我的机器上搭建一个网站
  • 让用户可以用密码登录
  • 让用户可以用 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/

在终端里的输出是类似于这样的:

‘/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/

如果喜欢本文,欢迎点击下方的「鼓掌」按钮!

如果上面没有加载出任何东西,可以点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK