Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e45f7ebf4 | ||
|
|
0e05bb61bc | ||
|
|
0635ea7a35 |
4
pom.xml
4
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.ryanmichela</groupId>
|
<groupId>com.ryanmichela</groupId>
|
||||||
<artifactId>sshd</artifactId>
|
<artifactId>sshd</artifactId>
|
||||||
<version>1.3.4.2</version>
|
<version>1.3.5</version>
|
||||||
<url>https://github.com/Justasic/Bukkit-SSHD/</url>
|
<url>https://github.com/Justasic/Bukkit-SSHD/</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -56,6 +56,8 @@
|
|||||||
<groupId>org.apache.sshd</groupId>
|
<groupId>org.apache.sshd</groupId>
|
||||||
<artifactId>sshd-common</artifactId>
|
<artifactId>sshd-common</artifactId>
|
||||||
<version>2.3.0</version>
|
<version>2.3.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<type>jar</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -11,28 +11,36 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
|
public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
|
||||||
|
|
||||||
private Map<String, Integer> failCounts = new HashMap<String, Integer>();
|
private Map<String, Integer> FailCounts = new HashMap<String, Integer>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean authenticate(String username, String password, ServerSession serverSession) {
|
public boolean authenticate(String username, String password, ServerSession ss)
|
||||||
if (SshdPlugin.instance.getConfig().getString("credentials." + username).equals(password)) {
|
{
|
||||||
failCounts.put(username, 0);
|
if (SshdPlugin.instance.getConfig().getString("Credentials." + username).equals(password))
|
||||||
|
{
|
||||||
|
FailCounts.put(username, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SshdPlugin.instance.getLogger().info("Failed login for " + username + " using password authentication.");
|
SshdPlugin.instance.getLogger().info("Failed login for " + username + " using password authentication.");
|
||||||
|
|
||||||
try {
|
Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
if (failCounts.containsKey(username)) {
|
if (this.FailCounts.containsKey(username))
|
||||||
failCounts.put(username, failCounts.get(username) + 1);
|
this.FailCounts.put(username, this.FailCounts.get(username) + 1);
|
||||||
} else {
|
else
|
||||||
failCounts.put(username, 1);
|
this.FailCounts.put(username, 1);
|
||||||
|
|
||||||
|
if (this.FailCounts.get(username) >= tries)
|
||||||
|
{
|
||||||
|
this.FailCounts.put(username, 0);
|
||||||
|
ss.close(true);
|
||||||
}
|
}
|
||||||
if (failCounts.get(username) >= 3) {
|
|
||||||
failCounts.put(username, 0);
|
|
||||||
serverSession.close(true);
|
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -18,11 +18,55 @@ import java.util.logging.LogRecord;
|
|||||||
public class ConsoleLogFormatter extends Formatter {
|
public class ConsoleLogFormatter extends Formatter {
|
||||||
|
|
||||||
private SimpleDateFormat dateFormat;
|
private SimpleDateFormat dateFormat;
|
||||||
|
private static final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
|
||||||
|
|
||||||
public ConsoleLogFormatter() {
|
public ConsoleLogFormatter() {
|
||||||
this.dateFormat = new SimpleDateFormat("HH:mm:ss");
|
this.dateFormat = new SimpleDateFormat("HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String ColorizeString(String str)
|
||||||
|
{
|
||||||
|
// ORIGINAL CODE FROM org.bukkit.craftbukkit.command.ColouredConsoleSender
|
||||||
|
|
||||||
|
replacements.put(ChatColor.BLACK, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_GREEN, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_AQUA, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_RED, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_PURPLE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.GOLD, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.GRAY, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString());
|
||||||
|
replacements.put(ChatColor.DARK_GRAY, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString());
|
||||||
|
replacements.put(ChatColor.BLUE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString());
|
||||||
|
replacements.put(ChatColor.GREEN, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString());
|
||||||
|
replacements.put(ChatColor.AQUA, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString());
|
||||||
|
replacements.put(ChatColor.RED, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.RED).bold().toString());
|
||||||
|
replacements.put(ChatColor.LIGHT_PURPLE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString());
|
||||||
|
replacements.put(ChatColor.YELLOW, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString());
|
||||||
|
replacements.put(ChatColor.WHITE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString());
|
||||||
|
replacements.put(ChatColor.MAGIC, Ansi.ansi().a(Ansi.Attribute.BLINK_SLOW).toString());
|
||||||
|
replacements.put(ChatColor.BOLD, Ansi.ansi().a(Ansi.Attribute.UNDERLINE_DOUBLE).toString());
|
||||||
|
replacements.put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Ansi.Attribute.STRIKETHROUGH_ON).toString());
|
||||||
|
replacements.put(ChatColor.UNDERLINE, Ansi.ansi().a(Ansi.Attribute.UNDERLINE).toString());
|
||||||
|
replacements.put(ChatColor.ITALIC, Ansi.ansi().a(Ansi.Attribute.ITALIC).toString());
|
||||||
|
replacements.put(ChatColor.RESET, Ansi.ansi().a(Ansi.Attribute.RESET).toString());
|
||||||
|
|
||||||
|
String result = str;
|
||||||
|
for (ChatColor color : ChatColor.values())
|
||||||
|
{
|
||||||
|
if (replacements.containsKey(color))
|
||||||
|
{
|
||||||
|
result = result.replaceAll("(?i)" + color.toString(), replacements.get(color));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = result.replaceAll("(?i)" + color.toString(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += Ansi.ansi().reset().toString();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public String format(LogRecord logrecord) {
|
public String format(LogRecord logrecord) {
|
||||||
try {
|
try {
|
||||||
Class.forName("org.bukkit.craftbukkit.command.ColouredConsoleSender");
|
Class.forName("org.bukkit.craftbukkit.command.ColouredConsoleSender");
|
||||||
@@ -50,51 +94,9 @@ public class ConsoleLogFormatter extends Formatter {
|
|||||||
return stringbuilder.toString();
|
return stringbuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void colorize(LogRecord logrecord) {
|
private void colorize(LogRecord logrecord)
|
||||||
// ORIGINAL CODE FROM org.bukkit.craftbukkit.command.ColouredConsoleSender
|
{
|
||||||
final Map<ChatColor, String> replacements = new EnumMap<>(ChatColor.class);
|
String result = ColorizeString(logrecord.getMessage());
|
||||||
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.BLACK, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString());
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString());
|
|
||||||
replacements.put(ChatColor.DARK_GREEN,
|
|
||||||
Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString());
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.DARK_AQUA, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString());
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.DARK_RED, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString());
|
|
||||||
replacements.put(ChatColor.DARK_PURPLE,
|
|
||||||
Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString());
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.GOLD, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString());
|
|
||||||
replacements.put(ChatColor.GRAY, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString());
|
|
||||||
replacements
|
|
||||||
.put(ChatColor.DARK_GRAY, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString());
|
|
||||||
replacements.put(ChatColor.BLUE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString());
|
|
||||||
replacements.put(ChatColor.GREEN, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString());
|
|
||||||
replacements.put(ChatColor.AQUA, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString());
|
|
||||||
replacements.put(ChatColor.RED, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.RED).bold().toString());
|
|
||||||
replacements.put(ChatColor.LIGHT_PURPLE,
|
|
||||||
Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString());
|
|
||||||
replacements.put(ChatColor.YELLOW, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString());
|
|
||||||
replacements.put(ChatColor.WHITE, Ansi.ansi().a(Ansi.Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString());
|
|
||||||
replacements.put(ChatColor.MAGIC, Ansi.ansi().a(Ansi.Attribute.BLINK_SLOW).toString());
|
|
||||||
replacements.put(ChatColor.BOLD, Ansi.ansi().a(Ansi.Attribute.UNDERLINE_DOUBLE).toString());
|
|
||||||
replacements.put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Ansi.Attribute.STRIKETHROUGH_ON).toString());
|
|
||||||
replacements.put(ChatColor.UNDERLINE, Ansi.ansi().a(Ansi.Attribute.UNDERLINE).toString());
|
|
||||||
replacements.put(ChatColor.ITALIC, Ansi.ansi().a(Ansi.Attribute.ITALIC).toString());
|
|
||||||
replacements.put(ChatColor.RESET, Ansi.ansi().a(Ansi.Attribute.RESET).toString());
|
|
||||||
|
|
||||||
String result = logrecord.getMessage();
|
|
||||||
for (ChatColor color : ChatColor.values()) {
|
|
||||||
if (replacements.containsKey(color)) {
|
|
||||||
result = result.replaceAll("(?i)" + color.toString(), replacements.get(color));
|
|
||||||
} else {
|
|
||||||
result = result.replaceAll("(?i)" + color.toString(), "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += Ansi.ansi().reset().toString();
|
|
||||||
logrecord.setMessage(result);
|
logrecord.setMessage(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.ryanmichela.sshd.SshTerminal;
|
|||||||
import com.ryanmichela.sshd.SshdPlugin;
|
import com.ryanmichela.sshd.SshdPlugin;
|
||||||
import com.ryanmichela.sshd.StreamHandlerAppender;
|
import com.ryanmichela.sshd.StreamHandlerAppender;
|
||||||
import com.ryanmichela.sshd.implementations.SSHDCommandSender;
|
import com.ryanmichela.sshd.implementations.SSHDCommandSender;
|
||||||
|
import com.ryanmichela.sshd.ConsoleLogFormatter;
|
||||||
import jline.console.ConsoleReader;
|
import jline.console.ConsoleReader;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.core.Logger;
|
import org.apache.logging.log4j.core.Logger;
|
||||||
@@ -22,18 +23,17 @@ import org.bukkit.Bukkit;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.StreamHandler;
|
import java.util.logging.StreamHandler;
|
||||||
|
|
||||||
public class ConsoleShellFactory implements ShellFactory {
|
public class ConsoleShellFactory implements ShellFactory {
|
||||||
|
|
||||||
static SSHDCommandSender sshdCommandSender = new SSHDCommandSender();
|
|
||||||
|
|
||||||
public Command createShell(ChannelSession cs) {
|
public Command createShell(ChannelSession cs) {
|
||||||
return new ConsoleShell();
|
return new ConsoleShell();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ConsoleShell implements Command, Runnable {
|
public class ConsoleShell implements Command, Runnable {
|
||||||
|
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
@@ -41,9 +41,11 @@ public class ConsoleShellFactory implements ShellFactory {
|
|||||||
private ExitCallback callback;
|
private ExitCallback callback;
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
private String Username;
|
||||||
|
|
||||||
StreamHandlerAppender streamHandlerAppender;
|
StreamHandlerAppender streamHandlerAppender;
|
||||||
public static ConsoleReader consoleReader;
|
public ConsoleReader ConsoleReader;
|
||||||
|
public SSHDCommandSender SshdCommandSender;
|
||||||
|
|
||||||
public InputStream getIn() {
|
public InputStream getIn() {
|
||||||
return in;
|
return in;
|
||||||
@@ -82,17 +84,20 @@ public class ConsoleShellFactory implements ShellFactory {
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
consoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal());
|
this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal());
|
||||||
consoleReader.setExpandEvents(true);
|
this.ConsoleReader.setExpandEvents(true);
|
||||||
consoleReader.addCompleter(new ConsoleCommandCompleter());
|
this.ConsoleReader.addCompleter(new ConsoleCommandCompleter());
|
||||||
|
|
||||||
StreamHandler streamHandler = new FlushyStreamHandler(out, new ConsoleLogFormatter(), consoleReader);
|
StreamHandler streamHandler = new FlushyStreamHandler(out, new ConsoleLogFormatter(), this.ConsoleReader);
|
||||||
streamHandlerAppender = new StreamHandlerAppender(streamHandler);
|
streamHandlerAppender = new StreamHandlerAppender(streamHandler);
|
||||||
|
|
||||||
((Logger)LogManager.getRootLogger()).addAppender(streamHandlerAppender);
|
((Logger)LogManager.getRootLogger()).addAppender(streamHandlerAppender);
|
||||||
|
|
||||||
environment = env;
|
this.environment = env;
|
||||||
thread = new Thread(this, "SSHD ConsoleShell " + env.getEnv().get(Environment.ENV_USER));
|
this.Username = env.getEnv().get(Environment.ENV_USER);
|
||||||
|
this.SshdCommandSender = new SSHDCommandSender();
|
||||||
|
this.SshdCommandSender.console = this;
|
||||||
|
thread = new Thread(this, "SSHD ConsoleShell " + this.Username);
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -108,28 +113,39 @@ public class ConsoleShellFactory implements ShellFactory {
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!SshdPlugin.instance.getConfig().getString("mode").equals("RPC"))
|
if (!SshdPlugin.instance.getConfig().getString("Mode").equals("RPC"))
|
||||||
printPreamble(consoleReader);
|
printPreamble(this.ConsoleReader);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
String command = consoleReader.readLine("\r>", null);
|
String command = this.ConsoleReader.readLine("\r>", null);
|
||||||
|
// The user sent CTRL+D to close the shell, terminate the session.
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
break;
|
||||||
|
// Skip someone spamming enter
|
||||||
|
if (command.trim().isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
// User wants to exit
|
||||||
if (command.equals("exit") || command.equals("quit"))
|
if (command.equals("exit") || command.equals("quit"))
|
||||||
break;
|
break;
|
||||||
|
// Clear the text from the screen (on supported terminals)
|
||||||
|
if (command.equals("cls"))
|
||||||
|
{
|
||||||
|
this.ConsoleReader.clearScreen();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(
|
Bukkit.getScheduler().runTask(
|
||||||
SshdPlugin.instance, () ->
|
SshdPlugin.instance, () ->
|
||||||
{
|
{
|
||||||
if (SshdPlugin.instance.getConfig().getString("mode").equals("RPC") && command.startsWith("rpc"))
|
if (SshdPlugin.instance.getConfig().getString("Mode").equals("RPC") && command.startsWith("rpc"))
|
||||||
{
|
{
|
||||||
// NO ECHO NO PREAMBLE AND SHIT
|
// NO ECHO NO PREAMBLE AND SHIT
|
||||||
String cmd = command.substring("rpc".length() + 1, command.length());
|
String cmd = command.substring("rpc".length() + 1, command.length());
|
||||||
Bukkit.dispatchCommand(sshdCommandSender, cmd);
|
Bukkit.dispatchCommand(this.SshdCommandSender, cmd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SshdPlugin.instance.getLogger().info("<" + environment.getEnv().get(Environment.ENV_USER) + "> "
|
SshdPlugin.instance.getLogger().info("<" + this.Username + "> " + command);
|
||||||
+ command);
|
|
||||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -141,23 +157,26 @@ public class ConsoleShellFactory implements ShellFactory {
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
SshdPlugin.instance.getLogger().log(Level.INFO, this.Username + " disconnected from SSH.");
|
||||||
callback.onExit(0);
|
callback.onExit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printPreamble(ConsoleReader consoleReader) throws IOException
|
private void printPreamble(ConsoleReader cr) throws IOException
|
||||||
{
|
{
|
||||||
consoleReader.println(" _____ _____ _ _ _____" + "\r");
|
cr.println(" _____ _____ _ _ _____" + "\r");
|
||||||
consoleReader.println(" / ____/ ____| | | | __ \\" + "\r");
|
cr.println(" / ____/ ____| | | | __ \\" + "\r");
|
||||||
consoleReader.println("| (___| (___ | |__| | | | |" + "\r");
|
cr.println("| (___| (___ | |__| | | | |" + "\r");
|
||||||
consoleReader.println(" \\___ \\\\___ \\| __ | | | |" + "\r");
|
cr.println(" \\___ \\\\___ \\| __ | | | |" + "\r");
|
||||||
consoleReader.println(" ____) |___) | | | | |__| |" + "\r");
|
cr.println(" ____) |___) | | | | |__| |" + "\r");
|
||||||
consoleReader.println("|_____/_____/|_| |_|_____/" + "\r");
|
cr.println("|_____/_____/|_| |_|_____/" + "\r");
|
||||||
consoleReader.println("Connected to: " + Bukkit.getServer().getName() + "\r");
|
// Doesn't really guarantee our actual system hostname but
|
||||||
consoleReader.println("- " + Bukkit.getServer().getMotd() + "\r");
|
// it's better than not having one at all.
|
||||||
consoleReader.println("\r");
|
cr.println("Connected to: " + InetAddress.getLocalHost().getHostName() + " (" + Bukkit.getServer().getName() + ")\r");
|
||||||
consoleReader.println("Type 'exit' to exit the shell." + "\r");
|
cr.println(ConsoleLogFormatter.ColorizeString(Bukkit.getServer().getMotd()) + "\r");
|
||||||
consoleReader.println("===============================================" + "\r");
|
cr.println("\r");
|
||||||
|
cr.println("Type 'exit' to exit the shell." + "\r");
|
||||||
|
cr.println("===============================================" + "\r");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ import org.apache.sshd.server.session.ServerSession;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
@@ -16,8 +18,8 @@ import java.security.PublicKey;
|
|||||||
*/
|
*/
|
||||||
public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
||||||
{
|
{
|
||||||
|
|
||||||
private File authorizedKeysDir;
|
private File authorizedKeysDir;
|
||||||
|
private Map<String, Integer> FailCounts = new HashMap<String, Integer>();
|
||||||
|
|
||||||
public PublicKeyAuthenticator(File authorizedKeysDir) { this.authorizedKeysDir = authorizedKeysDir; }
|
public PublicKeyAuthenticator(File authorizedKeysDir) { this.authorizedKeysDir = authorizedKeysDir; }
|
||||||
|
|
||||||
@@ -25,48 +27,43 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
|||||||
{
|
{
|
||||||
byte[] keyBytes = key.getEncoded();
|
byte[] keyBytes = key.getEncoded();
|
||||||
File keyFile = new File(authorizedKeysDir, username);
|
File keyFile = new File(authorizedKeysDir, username);
|
||||||
|
Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries");
|
||||||
|
|
||||||
if (keyFile.exists())
|
if (keyFile.exists())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Read all the public key entries
|
||||||
List<AuthorizedKeyEntry> pklist = AuthorizedKeyEntry.readAuthorizedKeys(keyFile.toPath());
|
List<AuthorizedKeyEntry> pklist = AuthorizedKeyEntry.readAuthorizedKeys(keyFile.toPath());
|
||||||
|
// Get an authenticator
|
||||||
PublickeyAuthenticator auth = PublickeyAuthenticator.fromAuthorizedEntries(username, session, pklist,
|
PublickeyAuthenticator auth = PublickeyAuthenticator.fromAuthorizedEntries(username, session, pklist,
|
||||||
PublicKeyEntryResolver.IGNORING);
|
PublicKeyEntryResolver.IGNORING);
|
||||||
|
|
||||||
boolean accepted = auth.authenticate(username, key, session);
|
// Validate that the logging in user has the same valid SSH key
|
||||||
|
if (auth.authenticate(username, key, session))
|
||||||
if (accepted)
|
|
||||||
{
|
{
|
||||||
SshdPlugin.instance.getLogger().info(
|
FailCounts.put(username, 0);
|
||||||
username + " successfully authenticated via SSH session using key file " + keyFile.getAbsolutePath());
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SshdPlugin.instance.getLogger().info(
|
SshdPlugin.instance.getLogger().info(
|
||||||
username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath());
|
username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath());
|
||||||
}
|
}
|
||||||
return accepted;
|
|
||||||
/*
|
|
||||||
|
|
||||||
FileReader fr = new FileReader(keyFile);
|
// If the user fails with several SSH keys, then terminate the connection.
|
||||||
PemDecoder pd = new PemDecoder(fr);
|
if (this.FailCounts.containsKey(username))
|
||||||
PublicKey k = pd.getPemBytes();
|
this.FailCounts.put(username, this.FailCounts.get(username) + 1);
|
||||||
pd.close();
|
|
||||||
|
|
||||||
if (k != null)
|
|
||||||
{
|
|
||||||
if (ArrayUtils.isEquals(key.getEncoded(), k.getEncoded()))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
this.FailCounts.put(username, 1);
|
||||||
|
|
||||||
|
if (this.FailCounts.get(username) >= tries)
|
||||||
{
|
{
|
||||||
SshdPlugin.instance.getLogger().severe("Failed to parse PEM file. " + keyFile.getAbsolutePath());
|
this.FailCounts.put(username, 0);
|
||||||
|
session.close(true);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ class SshdPlugin extends JavaPlugin
|
|||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
sshd = SshServer.setUpDefaultServer();
|
sshd = SshServer.setUpDefaultServer();
|
||||||
sshd.setPort(getConfig().getInt("port", 22));
|
sshd.setPort(getConfig().getInt("Port", 1025));
|
||||||
String host = getConfig().getString("listenAddress", "all");
|
String host = getConfig().getString("ListenAddress", "all");
|
||||||
sshd.setHost(host.equals("all") ? null : host);
|
sshd.setHost(host.equals("all") ? null : host);
|
||||||
|
|
||||||
File hostKey = new File(getDataFolder(), "hostkey");
|
File hostKey = new File(getDataFolder(), "hostkey");
|
||||||
@@ -56,7 +56,7 @@ class SshdPlugin extends JavaPlugin
|
|||||||
sshd.setPasswordAuthenticator(new ConfigPasswordAuthenticator());
|
sshd.setPasswordAuthenticator(new ConfigPasswordAuthenticator());
|
||||||
sshd.setPublickeyAuthenticator(new PublicKeyAuthenticator(authorizedKeys));
|
sshd.setPublickeyAuthenticator(new PublicKeyAuthenticator(authorizedKeys));
|
||||||
|
|
||||||
if (getConfig().getBoolean("enableSFTP"))
|
if (getConfig().getBoolean("EnableSFTP"))
|
||||||
{
|
{
|
||||||
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||||
sshd.setFileSystemFactory(
|
sshd.setFileSystemFactory(
|
||||||
|
|||||||
@@ -26,16 +26,24 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender {
|
|||||||
|
|
||||||
private final PermissibleBase perm = new PermissibleBase(this);
|
private final PermissibleBase perm = new PermissibleBase(this);
|
||||||
private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker();
|
private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker();
|
||||||
|
// Set by the upstream allocating function
|
||||||
|
public ConsoleShellFactory.ConsoleShell console;
|
||||||
|
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(String message) {
|
||||||
this.sendRawMessage(message);
|
this.sendRawMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendRawMessage(String message) {
|
public void sendRawMessage(String message)
|
||||||
if(ConsoleShellFactory.ConsoleShell.consoleReader == null) return;
|
{
|
||||||
try {
|
// What the fuck does this code even do? Are we sending to one client or all of them?
|
||||||
ConsoleShellFactory.ConsoleShell.consoleReader.println(ChatColor.stripColor(message));
|
if (this.console.ConsoleReader == null)
|
||||||
} catch (IOException e) {
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.console.ConsoleReader.println(ChatColor.stripColor(message));
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error sending message to SSHDCommandSender", e);
|
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error sending message to SSHDCommandSender", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +53,7 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Console";
|
return "SSHD Console";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOp() {
|
public boolean isOp() {
|
||||||
|
|||||||
@@ -1,22 +1,35 @@
|
|||||||
# The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses.
|
# The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses.
|
||||||
# Leave as "all" for all addresses.
|
# Leave as "all" for all addresses.
|
||||||
listenAddress: all
|
ListenAddress: all
|
||||||
# The port the SSH server will listen on.
|
# The port the SSH server will listen on. Note that anything above 1024 will require you to run
|
||||||
port: 22
|
# the whole minecraft server with elevated privileges, this is not recommended and you should
|
||||||
|
# use iptables to route packets from a lower port.
|
||||||
|
Port: 1025
|
||||||
|
|
||||||
# Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC
|
# Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC
|
||||||
mode: DEFAULT
|
Mode: DEFAULT
|
||||||
|
|
||||||
# Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.
|
# Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.
|
||||||
# Might be useful for testing purposes as well , i. e. docker containers.
|
# Might be useful for testing purposes as well , i. e. docker containers.
|
||||||
enableSFTP: true
|
EnableSFTP: true
|
||||||
|
|
||||||
|
# Number of times a person can fail to use an SSH key or enter a password
|
||||||
|
# before it terminates the connection.
|
||||||
|
LoginRetries: 3
|
||||||
|
|
||||||
# By default, only public key authentication is enabled. This is the most secure mode.
|
# By default, only public key authentication is enabled. This is the most secure mode.
|
||||||
# To authorize a user to log in with public key authentication, install their public
|
# To authorize a user to login with their public key, install their key using the
|
||||||
# PEM certificate in the authorized_users directory. Name the key file with user's user
|
# OpenSSH authorized_keys file format in the authorized_users directory. Name the key
|
||||||
# name (no file extension).
|
# file with the user's username and no extension. Note: If you want to let a user have
|
||||||
|
# many keys, you can append the keys to their file in authorized_users.
|
||||||
|
|
||||||
# For less secure username and password based authentication, complete the sections below.
|
# For less secure username and password based authentication, complete the sections below.
|
||||||
credentials:
|
|
||||||
|
# Type of hashing to use for the passwords below.
|
||||||
|
# Options are: PLAIN (insecure), bcrypt, pbkdf, sha256
|
||||||
|
PasswordType: bcrypt
|
||||||
|
|
||||||
|
# Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)
|
||||||
|
Credentials:
|
||||||
# user1: password1
|
# user1: password1
|
||||||
# user2: password2
|
# user2: password2
|
||||||
|
|||||||
Reference in New Issue
Block a user