问题背景:
其他系统在请求我的接口的时候需要拿服务器上的文件,但是我不想给权限比较多的用户。
所以我想在linux上新建一个用户sftpuser,这个用户只能从我提供的目录及子目录下 下载文件,无法进行其他操作,比如登录、上传、修改文件等等。
用户关系:
我一共需要三个用户。
因为我jar包所在服务器和文件服务器是单独的。
jar包接口生成的文件先生成到本地/sftp/files/下,然后通过jsch,channelsftp上传到文件服务器,然后删除本地临时文件。
两个文件服务器用户,一个jar包所在服务器用户。
jar包所在服务器用户jaruser需要/sftp的权限。
chown -R jaruser:jaruser /sftp
一个文件服务器用户fileuser需要/sftp的权限。用来通过jsch,channelsftp创建目录,上传文件,这个用户可以权限多一点,正常一点。
还有一个文件服务器用户就是我要提供给其他系统的用户sftpuser,这个用户只能从我提供的目录及子目录下 下载文件,无法进行其他操作,比如登录、上传、修改文件等等。
切换到root用户
新建用户
useradd sftpuser
passwd sftpuser
# 禁止sftpuser登录。
# 这个现在执行和之后执行都可以,放到最后执行可以登录来测试 setfacl 命令起没起作用。测一下cd,ls之类的。
usermod -s /sbin/nologin sftpuser
mkdir -p /sftp/files/
# 这个文件建哪都行,不影响,只是为了运行个脚本。
vim batchACL.sh
batchACL.sh
#!/bin/bash
# 遍历根目录下的所有目录,但排除/sftp和/目录
directories=($(find / -maxdepth 1 -type d ! -wholename "/sftp" ! -wholename "/" -exec echo {} \;))
# 设置 ACL 条目
for dir in "${directories[@]}"; do
setfacl -m u:sftpuser:- "$dir"
done
chmod 700 batchACL.sh
./batchACL.sh
我是在自己电脑虚拟机上和服务器上都配置了一遍。有点不同的地方就是同样是用jsch,自己电脑就可以连接,服务器上就无法连接。
Java代码报错,
com.jcraft.jsch.JSchException: java.io.IOException: Pipe closed
Caused by: java.io.IOException:Pipe closed
这时候就需要配置 /etc/ssh/sshd_config
vim /etc/ssh/sshd_config
PasswordAuthentication这个设不设置没影响。
Subsystem sftp internal-sftp
Match User sftpuser
systemctl restart sshd
# 启动生成文件的jar包的用户 (这个我没测,但好像不用chown,不chown也可以在这个目录下生成文件)
chown -R jaruser:jaruser /sftp
# 通过jsch,channelsftp创建目录,上传文件的用户(这个必须chown)
chown -R fileuser:fileuser /sftp
踩坑:
1、网上很多说设置 /etc/ssh/sshd_config 的,我试了无数次,根本无法限制登陆,还能cd到其他目录,总结就是不管用!!!!!!!
还得是 setfacl 这个linux命令简单粗暴。
这个命令的使用可以参考:setfacl linux 命令 在线中文手册 (51yip.com)
setfacl命令 – 设置文件ACL策略规则 – Linux命令大全(手册) (linuxcool.com)
2、究极大大大坑!!!!!!!
如果你在使用jsch,channel相关代码中打了断点,代码报错java.util.InterruptedException,你把断点都取消掉或者不debug就好了。
应该是和超时时间的设置有关,记不太清了,我好像是把session.connect(10000);,channel.connect(10000);这俩都设置的比较大就可以debug不报错了,这样是10秒。
3、这个是大坑,对用户权限不起作用,我不太懂这个文件是干啥的,但是改这个文件对我的需求没有任何帮助。
vim /etc/sudoers
踩坑过程中遇到的一些其他问题。
1、如果想先不设置禁止sftpuser用户登录,看看是否cd不到其他目录之类的,batchACL.sh脚本可以把directories的值改一下。
#!/bin/bash
# 遍历根目录下的所有目录,但排除/D和/目录 /lib64如果不排除,就会报错。
# [root@localhost ~]# su sftpuser
# su: failed to execute /bin/bash: Permission denied
directories=($(find / -maxdepth 1 -type d ! -wholename "/sftp" ! -wholename "/" ! -wholename "/bin" ! -wholename "/lib64" ! -wholename "/home" -exec echo {} \;))
# 设置 ACL 条目
for dir in "${directories[@]}"; do
setfacl -m u:sftpuser:- "$dir"
done
2、测试 setfacl 命令过程中涉及到的一些命令
# 设置sftpuser对/文件acl规则
setfacl -m u:sftpuser:- /
# 这个-R必须在最前面,清除sftpuser用户,对/文件acl规则
setfacl -R -x u:sftpuser /
# 清除所有acl
setfacl -b sftpuser
3、这是用jsch,channelexec进行测试的时候的截图,这个很清晰了,就是说服务只允许sftp连接,为什么呢,因为我配置了ForceCommand internal-sftp
4、测试用的一些代码。
import com.jcraft.jsch.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
/**
* <dependency>
* <groupId>com.jcraft</groupId>
* <artifactId>jsch</artifactId>
* <version>0.1.55</version>
* </dependency>
* session.connect();,channel.connect();最好设置超时时间,不然默认是无限等待。
* 加个数字就可以了 session.connect(1000);,channel.connect(1000);,具体多大自己根据实际情况把握。
*/
public class SftpDemo {
public static void main(String[] args) throws JSchException, SftpException, IOException {
String host = "192.168.1.200";
int port = 22;
String username = "sftpuser";
String password = "sftpuser";
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(password);
// 这个不设置会报错,至于原因不太明白,有兴趣可以研究研究。
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
// 打开一个通道
Channel channel = session.openChannel("exec");
// 根据;分割,依次执行的。
String cmd = "cd /sftp;mkdir 3.json;";
((ChannelExec) channel).setCommand(cmd);
InputStream in = channel.getInputStream();
channel.connect();
// 读取命令输出
byte[] buffer = new byte[1024];
int bytesRead;
StringBuilder result = new StringBuilder();
while ((bytesRead = in.read(buffer)) > 0) {
result.append(new String(buffer, 0, bytesRead));
}
// 关闭通道和会话
channel.disconnect();
session.disconnect();
// 打印命令输出
System.out.println("Command output:\n" + result);
/*
ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
String remoteFilePath = "/sftp/upload/1.txt";
// String remoteFilePath2 = "/opt/software/2.txt";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
channelSftp.get(remoteFilePath, byteArrayOutputStream);
// channelSftp.get(remoteFilePath2, byteArrayOutputStream);
String fileContent = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
System.out.println("File content:\n" + fileContent);
channelSftp.disconnect();
session.disconnect();
*/
}
}
5、各种命令。
# 监控用户创建、删除、连接等情况。
tailf /var/log/secure
[root@localhost ~]# getent passwd sftpuser
sftpuser:x:1004:1004::/home/sftpuser:/bin/bash
[root@localhost ~]# grep sftpuser /etc/passwd
sftpuser:x:1004:1004::/home/sftpuser:/bin/bash
[root@localhost home]# passwd -u sftpuser
Unlocking password for user sftpuser.
passwd: Success
6、在Linux中,当我们使用mkdir -p /A/B/C/
命令创建目录时,如果目录/A/B/C/
已经存在,不会对现有目录和文件产生任何不好的影响。这是因为mkdir
命令默认会忽略已经存在的目录。