Skip to content

Commit

Permalink
release v1.5.0, use spring-boot [close #24]
Browse files Browse the repository at this point in the history
  • Loading branch information
ggrandes committed Aug 20, 2024
1 parent fd4c228 commit 0c7c524
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 69 deletions.
37 changes: 3 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

SFTP Server (SSH File Transfer Protocol) based on `Apache MINA SSHD`. Open Source Java project under Apache License v2.0

### Current Stable Version is [1.4.2](https://maven-release.s3.amazonaws.com/release/org/javastack/sftpserver/1.4.2/sftpserver-1.4.2-bin.zip)
### Current Stable Version is [1.5.0](https://maven-release.s3.amazonaws.com/release/org/javastack/sftpserver/1.5.0/sftpserver-1.5.0-bin.zip)

---

Expand All @@ -15,6 +15,7 @@ SFTP Server (SSH File Transfer Protocol) based on `Apache MINA SSHD`. Open Sourc
| 1.2.x | 1.7+ |
| 1.3.x | 1.8+ |
| 1.4.x | 1.8+ |
| 1.5.x | 1.8+ |

## Config:

Expand Down Expand Up @@ -106,45 +107,13 @@ SFTP Server (SSH File Transfer Protocol) based on `Apache MINA SSHD`. Open Sourc
## MISC
Current hardcoded values:

* Default `${sftp.home}` is `/opt/sftpd`
* Hostkeys are writed to: `hostkey.pem` or `hostkey.ser` in `${sftp.home}/keys/` directory
* SecurityManager/Policy File is in `conf/${ID}/sftpd.policy` (custom) or `lib/sftpd.policy` (generic)
* Htpasswd File is in `conf/${ID}/htpasswd` (custom) or `conf/htpasswd` (generic)
* Default KexAlgorithms: `curve25519-sha256, [email protected], diffie-hellman-group14-sha256, diffie-hellman-group16-sha512, diffie-hellman-group-exchange-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha1`
* Default Ciphers: `[email protected], aes128-ctr, aes192-ctr, aes256-ctr, [email protected], [email protected]`
* Default MACs: `[email protected], [email protected], [email protected], hmac-sha2-256, hmac-sha2-512, hmac-sha1`

---

Maven Dependencies:

[Apache MINA SSHD](http://mina.apache.org/sshd-project/) [CHANGELOG](https://github.com/apache/mina-sshd/tree/master/docs/changes/)

* mina-core-`XXX`.jar
* sshd-core-`XXX`.jar
* sshd-sftp-`XXX`.jar
* sshd-scp-`XXX`.jar

[Apache Commons Codec (password encryption)](http://commons.apache.org/codec/)

* commons-codec-`XXX`.jar

[Log4J/Reload4J (logging)](https://reload4j.qos.ch/manual.html)

* reload4j-`XXX`.jar

[SLF4J (logging)](http://www.slf4j.org/)

* slf4j-api-`XXX`.jar
* slf4j-reload4j-`XXX`.jar

[Bouncy Castle (encryption)](http://www.bouncycastle.org/java.html)

* bcprov-jdk18on-`XXX`.jar
* bcpkix-jdk18on-`XXX`.jar

[JZlib (for compression)](http://www.jcraft.com/jzlib/)

* jzlib-`XXX`.jar

---
Inspired in [mina-sshd](https://github.com/apache/mina-sshd/blob/master/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java) and [openssh](http://www.openssh.org/).
17 changes: 7 additions & 10 deletions assembly/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,18 @@
<lineEnding>dos</lineEnding>
<fileMode>0644</fileMode>
</file>
<!-- Fat Jar -->
<file>
<source>${project.basedir}/target/${project.artifactId}-${project.version}.jar</source>
<outputDirectory>lib/</outputDirectory>
<destName>${project.artifactId}.jar</destName>
<fileMode>0644</fileMode>
</file>
<file>
<source>${project.basedir}/src/main/resources/sftpd.policy</source>
<outputDirectory>lib/</outputDirectory>
<lineEnding>dos</lineEnding>
<fileMode>0644</fileMode>
</file>
</files>
<dependencySets>
<dependencySet>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
<outputDirectory>lib/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>false</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
30 changes: 17 additions & 13 deletions linux/sftpd.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#!/bin/bash
ID=${2:-default}
JAVA_BIN=${JAVA_BIN:-java}
SFTPD_HOME=${SFTPD_HOME:-/opt/sftpd}
SFTPD_MEM_MB=${SFTPD_MEM_MB:-64}
SFTPD_OPTS_DEF="-XX:+IgnoreUnrecognizedVMOptions -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -verbose:gc -XX:+PrintGCDetails -Xlog:gc*::time,uptime,level,tags -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -showversion -XX:+PrintCommandLineFlags -XX:-PrintFlagsFinal"
SFTPD_OPTS="${SFTPD_OPTS:-${SFTPD_OPTS_DEF}}"
SFTPD_CLASSPATH=$(echo $SFTPD_HOME/lib/*.jar | tr ' ' ':')
SFTPD_JAR="$SFTPD_HOME/lib/sftpserver.jar"
SFTPD_LOG4J="${SFTPD_HOME}/conf/${ID}/log4j.properties"
SFTPD_POLICY_1="${SFTPD_HOME}/conf/${ID}/sftpd.policy" # Custom
SFTPD_POLICY_2="${SFTPD_HOME}/lib/sftpd.policy" # Generic
MAIN_CLASS="org.javastack.sftpserver.Server"
LAUNCHER_CLASS="org.springframework.boot.loader.PropertiesLauncher"
PWD_CLASS="org.javastack.sftpserver.PasswordEncrypt"
PIDFILE="${SFTPD_HOME}/pid/sftpd-${ID}.pid"
#
Expand All @@ -16,25 +18,27 @@ PIDFILE="${SFTPD_HOME}/pid/sftpd-${ID}.pid"
#
do_pwd () {
cd ${SFTPD_HOME}
java \
-cp "${SFTPD_CLASSPATH}" \
${PWD_CLASS}
${JAVA_BIN} \
-Dloader.main=${PWD_CLASS} \
-cp "${SFTPD_JAR}" ${LAUNCHER_CLASS}
}
do_run () {
cd ${SFTPD_HOME}
exec java -Dprogram.name=sftpd ${SFTPD_OPTS} -Xmx${SFTPD_MEM_MB}m \
exec ${JAVA_BIN} -Dprogram.name=sftpd-${ID} ${SFTPD_OPTS} -Xmx${SFTPD_MEM_MB}m \
-Dsftp.id=$ID -Dsftp.home=$SFTPD_HOME -Dsftp.log=${SFTPD_LOG:-CONSOLE} \
-cp "${SFTPD_HOME}/conf/${ID}/:${SFTPD_HOME}/conf/:${SFTPD_CLASSPATH}" \
-Djava.security.manager -Djava.security.policy=file:${SFTPD_POLICY} \
${MAIN_CLASS}
-Dsftp.config="${SFTPD_HOME}/conf/${ID}/:${SFTPD_HOME}/conf/" \
-Djava.security.manager -Djava.security.policy=file:"${SFTPD_POLICY}" \
-Dlog4j.configuration=file:"${SFTPD_LOG4J}" \
-jar "${SFTPD_JAR}"
}
do_start () {
cd ${SFTPD_HOME}
nohup java -Dprogram.name=sftpd ${SFTPD_OPTS} -Xmx${SFTPD_MEM_MB}m \
nohup ${JAVA_BIN} -Dprogram.name=sftpd-${ID} ${SFTPD_OPTS} -Xmx${SFTPD_MEM_MB}m \
-Dsftp.id=$ID -Dsftp.home=$SFTPD_HOME -Dsftp.log=${SFTPD_LOG:-FILE} \
-cp "${SFTPD_HOME}/conf/${ID}/:${SFTPD_HOME}/conf/:${SFTPD_CLASSPATH}" \
-Djava.security.manager -Djava.security.policy=file:${SFTPD_POLICY} \
${MAIN_CLASS} 1>${SFTPD_HOME}/log/sftpd-${ID}.bootstrap 2>&1 &
-Dsftp.config="${SFTPD_HOME}/conf/${ID}/:${SFTPD_HOME}/conf/" \
-Djava.security.manager -Djava.security.policy=file:"${SFTPD_POLICY}" \
-Dlog4j.configuration=file:"${SFTPD_LOG4J}" \
-jar "${SFTPD_JAR}" 1>${SFTPD_HOME}/log/sftpd-${ID}.bootstrap 2>&1 &
PID="$!"
echo ${PID} >$PIDFILE
echo "SFTPD: STARTED [${PID}]"
Expand Down
25 changes: 23 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.javastack</groupId>
<artifactId>sftpserver</artifactId>
<version>1.4.2</version>
<version>1.5.0</version>
<description>SFTP Server (SSH File Transfer Protocol)</description>

<name>${project.groupId}:${project.artifactId}</name>
Expand All @@ -32,6 +32,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<start-class>org.javastack.sftpserver.Server</start-class>
<java.version>1.8</java.version>
<slf4j.version>1.7.36</slf4j.version>
<mina.version>2.1.8</mina.version>
Expand Down Expand Up @@ -112,7 +113,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
Expand Down Expand Up @@ -168,6 +169,26 @@
</execution>
</executions>
</plugin>
<!-- Jars in Jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.4</version>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- https://docs.spring.io/spring-boot/maven-plugin/packaging.html -->
<!-- https://docs.spring.io/spring-boot/specification/executable-jar/launching.html -->
<!-- https://docs.spring.io/spring-boot/specification/executable-jar/property-launcher.html -->
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
<!-- Package ZIP to upload to repo -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/org/javastack/sftpserver/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
Expand All @@ -36,6 +37,7 @@
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
Expand Down Expand Up @@ -84,12 +86,14 @@
* @author Guillermo Grandes / guillermo.grandes[at]gmail.com
*/
public class Server implements PasswordAuthenticator, PublickeyAuthenticator {
public static final String SFTP_CONFIG_PROP = "sftp.config";
public static final String CONFIG_FILE = "/sftpd.properties";
public static final String HTPASSWD_FILE = "/htpasswd";
public static final String HOSTKEY_FILE_PEM = "keys/hostkey.pem";
public static final String HOSTKEY_FILE_SER = "keys/hostkey.ser";

private static final Logger LOG = LoggerFactory.getLogger(Server.class);
private URLClassLoader configClassLoader;
private Config db;
private SshServer sshd;
private ServiceLogger logger;
Expand Down Expand Up @@ -168,7 +172,7 @@ protected void loadHtPasswd() throws IOException {
}
final String htHome = db.getHtValue(Config.PROP_HT_HOME);
final boolean htEnableWrite = Boolean.parseBoolean(db.getHtValue(Config.PROP_HT_ENABLE_WRITE));
is = getClass().getResourceAsStream(HTPASSWD_FILE);
is = getConfigResource(HTPASSWD_FILE);
r = new BufferedReader(new InputStreamReader(is));
if (is == null) {
LOG.error("htpasswd file " + HTPASSWD_FILE + " not found in classpath");
Expand Down Expand Up @@ -212,11 +216,35 @@ protected void setupCompress(final boolean enable) {
}
}

protected URLClassLoader initConfigLoader() {
final String cp = System.getProperty(SFTP_CONFIG_PROP, "");
final List<String> classPath = Arrays.asList(cp
.split(Pattern.quote(File.pathSeparator)));
final URL[] urls = classPath.stream().map(i -> {
try {
if (i.startsWith("http://") || i.startsWith("https://")) {
return new URL(i);
} else {
return new File(i).toURI().toURL();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}).toArray(URL[]::new);
LOG.info("Config loader URLs=" + Arrays.asList(urls));

return new URLClassLoader(urls);
}

protected InputStream getConfigResource(final String name) throws IOException {
return configClassLoader.getResourceAsStream(name.startsWith("/") ? name.substring(1) : name);
}

protected Config loadConfig() {
final Properties db = new Properties();
InputStream is = null;
try {
is = getClass().getResourceAsStream(CONFIG_FILE);
is = getConfigResource(CONFIG_FILE);
if (is == null) {
LOG.error("Config file " + CONFIG_FILE + " not found in classpath");
} else {
Expand Down Expand Up @@ -296,6 +324,7 @@ private void hackModuliDHGEX() {

public void start() {
LOG.info("Starting");
configClassLoader = initConfigLoader();
logger = new ServiceLogger();
db = loadConfig();
LOG.info("BouncyCastle enabled=" + SecurityUtils.isBouncyCastleRegistered());
Expand Down
34 changes: 26 additions & 8 deletions src/main/resources/sftpd.policy
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// This file contains a default set of security policies to be enforced by JVM
//
// $Id: sftpd.policy,v 1.4.1 2024/07/25 17:40:00 ggrandes Exp $
// $Id: sftpd.policy,v 1.5.0 2024/08/20 19:50:00 ggrandes Exp $
// ============================================================================
//
// Load with: -Djava.security.manager -Djava.security.policy=sftpd.policy
Expand All @@ -15,8 +15,10 @@
//
grant {
//
// https://docs.oracle.com/javase/8/docs/api/java/util/logging/LoggingPermission.html
permission java.util.logging.LoggingPermission "control";
//
// https://docs.oracle.com/javase/8/docs/api/java/util/PropertyPermission.html
permission java.util.PropertyPermission "org.bouncycastle.*", "read";
permission java.util.PropertyPermission "org.apache.sshd.*", "read";
permission java.util.PropertyPermission "log4j.*", "read";
Expand All @@ -30,6 +32,16 @@ grant {
// org.apache.sshd.common.SyspropsMapWrapper
permission java.util.PropertyPermission "*", "read, write";
//
// https://docs.oracle.com/javase/8/docs/api/java/lang/RuntimePermission.html
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.reflect";
permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "getClassLoader";
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.RuntimePermission "closeClassLoader";
permission java.lang.RuntimePermission "setFactory";
permission java.lang.RuntimePermission "getProtectionDomain";
//
permission java.lang.RuntimePermission "shutdownHooks";
permission java.lang.RuntimePermission "modifyThread";
permission java.lang.RuntimePermission "readFileDescriptor";
Expand All @@ -38,30 +50,36 @@ grant {
permission java.lang.RuntimePermission "fileSystemProvider";
permission java.lang.RuntimePermission "accessDeclaredMembers";
//
// https://docs.oracle.com/javase/8/docs/api/java/security/SecurityPermission.html
permission java.security.SecurityPermission "getProperty.org.bouncycastle.*";
permission java.security.SecurityPermission "removeProviderProperty.BC";
permission java.security.SecurityPermission "putProviderProperty.BC";
permission java.security.SecurityPermission "insertProvider.BC";
permission java.security.SecurityPermission "putProviderProperty.EdDSA";
permission java.security.SecurityPermission "insertProvider.EdDSA";
permission java.security.SecurityPermission "insertProvider";
//
// https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/ReflectPermission.html
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
//
// https://docs.oracle.com/javase/8/docs/api/java/net/NetPermission.html
permission java.net.NetPermission "specifyStreamHandler";
//
// https://docs.oracle.com/javase/8/docs/api/java/net/SocketPermission.html
permission java.net.SocketPermission "localhost", "listen";
permission java.net.SocketPermission "*", "accept, resolve";
//
// https://docs.oracle.com/javase/8/docs/api/java/io/FilePermission.html
permission java.io.FilePermission "${java.home}${/}-", "read";
permission java.io.FilePermission "${java.io.tmpdir}${/}-", "read, write, delete";
//
permission java.io.FilePermission "/etc/resolv.conf", "read";
permission java.io.FilePermission "/etc/ssh/moduli", "read";
permission java.io.FilePermission "${sftp.home}/-", "read";
permission java.io.FilePermission "${sftp.home}/keys/hostkey.ser", "read, write";
permission java.io.FilePermission "${sftp.home}/keys/hostkey.pem", "read, write";
permission java.io.FilePermission "${sftp.home}${/}-", "read";
permission java.io.FilePermission "${sftp.home}${/}keys${/}hostkey.ser", "read, write";
permission java.io.FilePermission "${sftp.home}${/}keys${/}hostkey.pem", "read, write";
// Home for Users
permission java.io.FilePermission "${sftp.home}/home/-", "read, write, delete";
permission java.io.FilePermission "${sftp.home}${/}home${/}-", "read, write, delete";
// Directory for logs (if use log4j)
permission java.io.FilePermission "${sftp.home}/log/-", "read, write";
permission java.io.FilePermission "${sftp.home}${/}log${/}-", "read, write";
//
// This Disable all security
//permission java.security.AllPermission;
Expand Down

0 comments on commit 0c7c524

Please sign in to comment.