SFTP搭建

警告
本文最后更新于 2022-12-07,文中内容可能已过时。
注意
本文内容仅在CentOS 7上进行测试

前言

  文章介绍如何让sftp也可以实现vsftpd虚拟用户的功能。
  对于运维来说,我们使用文件传输功能的时候都是优先使用vsftpd,而不是sftp,多数原因我想应该都是因为vsftpd具有虚拟用户的功能,这个功能在针对特定的服务来说是非常友好的。比如php服务降权启动时,被读取文件的文件权限问题。
  上述的问题,sftp实际上也是可以解决,借助useradd -o选项实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[cxd@0x5c0f ~][0]$ useradd --help
用法:useradd [选项] 登录名
      useradd -D
      useradd -D [选项]

选项:
  -h, --help                    显示此帮助信息并退出
  -k, --skel SKEL_DIR           使用此目录作为骨架目录
                                The skeleton directory, which contains files and directories to be copied in the user\'s home directory,
                                when the home directory is created by useradd.

                                This option is only valid if the -m (or --create-home) option is specified.

                                If this option is not set, the skeleton directory is defined by the SKEL variable in
                                /etc/default/useradd or, by default, /etc/skel.

  -m, --create-home             创建用户的主目录
  -o, --non-unique              允许使用重复的 UID 创建用户
                                This option is only valid in combination with the -u option.
  -s, --shell SHELL             新账户的登录 shell
  -u, --uid UID                 新账户的用户 ID
  

正文

以下定义WEBServer的基础用户为www,以php为例,php-fpm启动进程所属则为www用户,那么也只能读取www用户所拥有操作权限的文件。

用户创建

创建sftp登陆用户,使用-o选项,让当前用户保持与www同属主UID、同属组GID

1
2
3
$> groupadd -o -g $(id -g www) webapp
$> useradd -o -u $(id -u www) -g webapp -m -k $(mktemp -d) -s /bin/false webapp
# 此帐号只是sftp使用,所有创建时候添加-k选项,不让useradd复制/etc/skel下内容

帐号创建成功后,可在/etc/passwd中看到该帐号,此时应可以看到他的所属主和属组和www帐号一致

创建sftp登陆密钥

1
2
3
4
5
6
7
8
# 由于ssh-keygen在创建默认密钥时无法更新此路径,因此需要主动创建该目录
$> mkdir /home/webapp/.ssh
# 此处授权可以直接授权www:www,为了看起来更清晰,此处授权还是用创建时的用户,但无论使用的是那一个,系统显示都会是www
$> chown webapp:webapp /home/webapp/.ssh

# 密钥创建
$> su - webapp -s /bin/bash -c "ssh-keygen -f ~/.ssh/id_rsa -t rsa -b 4096 -N ''"
$> su - webapp -s /bin/bash -c "cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

修改/etc/ssh/sshd_config

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 注意此项, 网上的sftp搭建教程基本都是说需要将此项切换为, Subsystem       sftp    internal-sftp
# 切换后将绕过"管理员可能依赖登录shell配置来阻止某些用户登录"。但我们上述使用的是重复UID,所以此处不能更改
# 若更改,则会导致共用UID的用户之间可相互登陆。
# 差异参见: https://serverfault.com/questions/660160/openssh-difference-between-internal-sftp-and-sftp-server
# 差异参见: http://129.226.226.195/post/21921.html
Subsystem       sftp    /usr/libexec/openssh/sftp-server

# 指定匹配用户
Match User webapp
    # 用chroot将用户的根目录指向到固定位置
    ChrootDirectory  /sftpdir
    # -l 指定日志收集 -f 收集内容(应该是)  internal-sftp 请参看上述连接自行参悟
    ForceCommand internal-sftp -l INFO -f AUTH
    # 以下其他配置自行参悟
    PermitTTY no
    X11Forwarding no
    AllowTcpForwarding no
    PasswordAuthentication no

开始测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 上述操作完成后,还需要创建一个chroot目录
$> mkdir /sftpdir 
$> echo hello > /sftpdir/readme.md

# 注意目录属主必须为root,属组可以不是,权限不能超过755 
$> chown root.root /sftpdir 
$> chmod 755 /sftpdir


# 重启sshd服务(重载也可以)
$> systemctl reload sshd

## 登陆测试
$> sftp -i /home/webapp/.ssh/id_rsa webapp@127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:hQkISJWcE+gHf1WAT2bIWSwiAJRD81Bv3wZd+1vZOuU.
ECDSA key fingerprint is MD5:0e:e5:1a:c7:6c:97:fb:48:95:d2:c9:86:bb:d0:7d:91.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts.
Connected to 127.0.0.1.
sftp> ls -l
-rw-r--r--    1 0        0               6 Dec  8 06:31 readme.md
sftp> pwd
Remote working directory: /
sftp>  

后续

至此,sftp搭建完成, 当然由于 /目录属主为root,sftp目前只能登陆,无法上传,需要在/sftpdir目录下创建目录,然后授权www用户即可,在该目录下进行增、删、改操作。

其他问题

  • 为什么使用sftp: sftp使用加密传输认证信息和传输的数据,相对ftp而言更为安全一点.

  • 目录映射: 虚拟用户实现了,那该如何实现目录映射呢,软连接还是每个目录单独建一个用户?其实都不是,我们只需要借助mount命令的bind属性即可,具体使用方式请自行参悟(http://blog.0x5c0f.cc/2019/%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E6%94%B6%E9%9B%86/#mount---bind)

  • 帐号管理: 共UID帐号(webapp)直接删除时候基本都会有提示,如果主帐号www正在使用(如phpnginx),那么删除的时候就会提示无法删除,此时我们只需要强制删除即可(userdel-f选项),并不会影响主帐号和其他帐号(注:这个我只在CentOS 7上进行过测试,理论上所有发行版是一致的)

  • 日志查看: tail -f /var/log/secure

0%