Added hover text and click to copy for /mkpasswd

- Added hover text and click to copy so you can now copy your hash
to the clipboard.
- Added default values for the configuration in case none were set for some reason.
- Validate permissions from the config (something I forgot to do)
- Tell the user permission denied if they don't have permission to run the command.
- Added a default pseudo-user for global permissions and rules.
- Updated some verbage in the config comments
This commit is contained in:
Justin Crawford 2020-03-26 21:43:34 -07:00
parent 40f63bdeeb
commit e0080fb1a0
No known key found for this signature in database
GPG Key ID: 0D84DEDBB8EF259C
9 changed files with 103 additions and 32 deletions

16
pom.xml
View File

@ -34,15 +34,15 @@
<!-- Dependencies --> <!-- Dependencies -->
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.spigotmc</groupId>
<artifactId>bukkit</artifactId> <artifactId>spigot-api</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version> <version>1.15.2-R0.1-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.sshd</groupId> <groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId> <artifactId>sshd-core</artifactId>
<version>2.3.0</version> <version>2.4.0</version>
<scope>compile</scope> <scope>compile</scope>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
@ -50,19 +50,19 @@
<dependency> <dependency>
<groupId>org.apache.sshd</groupId> <groupId>org.apache.sshd</groupId>
<artifactId>sshd-mina</artifactId> <artifactId>sshd-mina</artifactId>
<version>2.3.0</version> <version>2.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.sshd</groupId> <groupId>org.apache.sshd</groupId>
<artifactId>sshd-contrib</artifactId> <artifactId>sshd-contrib</artifactId>
<version>2.3.0</version> <version>2.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<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.4.0</version>
<scope>compile</scope> <scope>compile</scope>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
@ -70,7 +70,7 @@
<dependency> <dependency>
<groupId>org.apache.sshd</groupId> <groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId> <artifactId>sshd-sftp</artifactId>
<version>2.3.0</version> <version>2.4.0</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -70,7 +70,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator
} }
SshdPlugin.instance.getLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication."); SshdPlugin.instance.getLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication.");
Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries"); Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries", 3);
try try
{ {

View File

@ -2,13 +2,13 @@ package com.ryanmichela.sshd;
import com.ryanmichela.sshd.ConsoleCommandCompleter; import com.ryanmichela.sshd.ConsoleCommandCompleter;
import com.ryanmichela.sshd.ConsoleLogFormatter; import com.ryanmichela.sshd.ConsoleLogFormatter;
import com.ryanmichela.sshd.PermissionUtil;
import com.ryanmichela.sshd.FlushyOutputStream; import com.ryanmichela.sshd.FlushyOutputStream;
import com.ryanmichela.sshd.FlushyStreamHandler; import com.ryanmichela.sshd.FlushyStreamHandler;
import com.ryanmichela.sshd.SshTerminal; 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;
@ -29,6 +29,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.StreamHandler; import java.util.logging.StreamHandler;
@ -100,6 +101,17 @@ public class ConsoleShellFactory implements ShellFactory
{ {
try try
{ {
String username = env.getEnv().get(Environment.ENV_USER);
Optional<String> optcred = PermissionUtil.GetCredential(username, "console");
// They don't have access.
if (optcred.isPresent() && !optcred.get().contains("R"))
{
cs.close(true);
return;
}
else
SshdPlugin.instance.getLogger().warning("There is no $default pseudo-user under credential, allowing unrestricted access...");
this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal()); this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal());
this.ConsoleReader.setExpandEvents(true); this.ConsoleReader.setExpandEvents(true);
this.ConsoleReader.addCompleter(new ConsoleCommandCompleter()); this.ConsoleReader.addCompleter(new ConsoleCommandCompleter());
@ -110,14 +122,15 @@ public class ConsoleShellFactory implements ShellFactory
((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender); ((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender);
this.environment = env; this.environment = env;
this.Username = env.getEnv().get(Environment.ENV_USER); this.Username = username;
this.SshdCommandSender = new SSHDCommandSender(); this.SshdCommandSender = new SSHDCommandSender();
this.SshdCommandSender.console = this; this.SshdCommandSender.console = this;
thread = new Thread(this, "SSHD ConsoleShell " + this.Username); thread = new Thread(this, "SSHD ConsoleShell " + username);
thread.start(); thread.start();
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace();
throw new IOException("Error starting shell", e); throw new IOException("Error starting shell", e);
} }
} }
@ -129,7 +142,7 @@ public class ConsoleShellFactory implements ShellFactory
{ {
try try
{ {
if (!SshdPlugin.instance.getConfig().getString("Mode").equals("RPC")) if (!SshdPlugin.instance.getConfig().getString("Mode", "DEFAULT").equals("RPC"))
printPreamble(this.ConsoleReader); printPreamble(this.ConsoleReader);
while (true) while (true)
{ {
@ -153,11 +166,15 @@ public class ConsoleShellFactory implements ShellFactory
} }
// Hide the mkpasswd command input from other users. // Hide the mkpasswd command input from other users.
Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd"); Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd");
Optional<String> optcred = PermissionUtil.GetCredential(this.Username, "console");
if (optcred.isPresent() && !optcred.get().contains("W"))
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", "DEFAULT").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());

View File

@ -7,6 +7,11 @@ import org.bukkit.entity.Player;
import java.util.Arrays; import java.util.Arrays;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import com.ryanmichela.sshd.Cryptography; import com.ryanmichela.sshd.Cryptography;
import com.ryanmichela.sshd.SshdPlugin; import com.ryanmichela.sshd.SshdPlugin;
@ -30,10 +35,6 @@ class MkpasswdCommand implements CommandExecutor
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
{ {
// If we're not mkpasswd, just fuck off.
if (!label.equalsIgnoreCase("mkpasswd"))
return false;
String algoritm, password; String algoritm, password;
try try
{ {
@ -77,7 +78,11 @@ class MkpasswdCommand implements CommandExecutor
return true; return true;
} }
sender.sendMessage("\u00A79Your Hash: " + hash + "\u00A7r"); TextComponent msg = new TextComponent("\u00A79Your Hash: " + hash + "\u00A7r");
msg.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hash));
msg.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("Click to copy the hash!").create()));
sender.spigot().sendMessage(msg);
} }
catch (Exception e) catch (Exception e)
{ {
@ -86,6 +91,8 @@ class MkpasswdCommand implements CommandExecutor
sender.sendMessage("\u00A7cAn error occured. Please check console for details.\u00A7r"); sender.sendMessage("\u00A7cAn error occured. Please check console for details.\u00A7r");
} }
} }
else
sender.sendMessage("\u00A7cPermission Denied.\u00A7r");
return true; return true;
} }

View File

@ -0,0 +1,23 @@
package com.ryanmichela.sshd;
import java.util.Optional;
import com.ryanmichela.sshd.SshdPlugin;
public class PermissionUtil
{
public static Optional<String> GetCredential(String username, String credential)
{
String Default = SshdPlugin.instance.getConfig().getString("Credentials.$default." + credential);
String cred = SshdPlugin.instance.getConfig().getString("Credentials." + username + "." + credential, Default);
if (cred == null)
return Optional.empty();
else if (cred.isEmpty())
return Optional.empty();
else
return Optional.of(cred);
}
};

View File

@ -27,7 +27,7 @@ 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"); Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries", 3);
if (keyFile.exists()) if (keyFile.exists())
{ {
@ -68,7 +68,8 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
} }
catch (Exception e) catch (Exception e)
{ {
SshdPlugin.instance.getLogger().severe("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage()); e.printStackTrace();
SshdPlugin.instance.getLogger().severe("Failed to process public key " + keyFile.getAbsolutePath());
} }
} }
else else

View File

@ -20,7 +20,7 @@ import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -92,11 +92,12 @@ public 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", false))
{ {
// Handle access control for SFTP. // Handle access control for SFTP.
SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder(); SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
builder.addSftpEventListener(new SimpleAccessControlSftpEventListener() { builder.addSftpEventListener(new SimpleAccessControlSftpEventListener()
{
protected boolean isAccessAllowed(ServerSession session, String remote, Path localpath) protected boolean isAccessAllowed(ServerSession session, String remote, Path localpath)
{ {
try try
@ -140,10 +141,11 @@ public class SshdPlugin extends JavaPlugin
{ {
try try
{ {
boolean defaultbool = getConfig().getBoolean("Credentials.$default.sftp.enabled", false);
ConfigurationSection UsernameNamespace = getConfig().getConfigurationSection("Credentials." + session.getUsername() + ".sftp"); ConfigurationSection UsernameNamespace = getConfig().getConfigurationSection("Credentials." + session.getUsername() + ".sftp");
// They don't have SFTP enabled so deny them. // They don't have SFTP enabled so deny them.
if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled")) if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled", defaultbool))
return false; return false;
// Check a list of files against a path trying to be accessed. // Check a list of files against a path trying to be accessed.
@ -165,7 +167,7 @@ public class SshdPlugin extends JavaPlugin
} }
} }
return UsernameNamespace.getString("default").equalsIgnoreCase("allow"); return UsernameNamespace.getString("default", "deny").equalsIgnoreCase("allow");
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -171,4 +171,8 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender
return Bukkit.getServer(); return Bukkit.getServer();
} }
public CommandSender.Spigot spigot()
{
return ((CommandSender)this).spigot();
}
} }

View File

@ -1,9 +1,8 @@
# 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. Note that anything above 1024 will require you to run # The port the SSH server will listen on. Note that anything *below* 1024 will require you to run
# the whole minecraft server with elevated privileges, this is not recommended and you should # the whole minecraft server with elevated privileges (NOT RECOMMENDED).
# use iptables to route packets from a lower port.
Port: 1025 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
@ -37,8 +36,26 @@ PasswordType: bcrypt
# Associate each username with a password hash (or the password if the PasswordType is set to PLAIN) # Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)
Credentials: Credentials:
# Username (should match SSH key if using key-based authentication) # The defaults for any user who does not have a specific section.
justasic: # Specific user permissions override the $default pseudo-user.
$default:
# Whether they can read or write to the console
console: RW
# SFTP access for anyone.
sftp:
# Whether sftp is allowed at all.
enabled: true
# Whether to allow or deny by default
default: allow
# specific rules for directories
rules:
# Deny the SSHD config folder by default as an example.
"*SSHD/*":
readable: false
writeable: false
# Username (should match SSH key if using key-based authentication)
justasic:
# Password hash from /mkpasswd command # Password hash from /mkpasswd command
password: $2a$10$Oqk83FrypRrMF35EDeoQDuidJOQEWBE0joEQ7MJFi/Oeg26wQ3fm2 password: $2a$10$Oqk83FrypRrMF35EDeoQDuidJOQEWBE0joEQ7MJFi/Oeg26wQ3fm2
# Whether they can read, write, or have read/write permissions to console. # Whether they can read, write, or have read/write permissions to console.
@ -47,7 +64,7 @@ Credentials:
sftp: sftp:
# Whether SFTP is enabled for this user. # Whether SFTP is enabled for this user.
enabled: true enabled: true
# Whether to deny by default or allow by default # Whether to deny access by default or allow access by default
default: allow default: allow
# Rules regarding their SFTP access. # Rules regarding their SFTP access.
# These rules are relative to the server root. # These rules are relative to the server root.