项目作者: fonimus

项目描述 :
Spring shell in spring boot application over ssh
高级语言: Java
项目地址: git://github.com/fonimus/ssh-shell-spring-boot.git
创建时间: 2018-03-09T18:31:56Z
项目社区:https://github.com/fonimus/ssh-shell-spring-boot

开源协议:Apache License 2.0

下载


Spring Boot Ssh Shell

Build Status
Maintainability Rating
Coverage
Maven Central

Spring shell in spring boot application over ssh

For more information please
visit spring shell website.

Getting started

Dependency

  1. <dependency>
  2. <groupId>com.github.fonimus</groupId>
  3. <artifactId>ssh-shell-spring-boot-starter</artifactId>
  4. </dependency>

Warning : since version 2.0.0 (spring shell 2.1.0) interactive shell is
enabled by default.
You can set property spring.shell.interactive.enabled=false to disable it.

Note: auto configuration SshShellAutoConfiguration (active by default)
can be deactivated by property
ssh.shell.enable=false.

It means that the ssh server won’t start and the commands won’t be scanned.
Unfortunately the application will still
load the spring-shell auto configuration classes and display a shell at
startup (shell:>). You can disable them with
following property:

  1. spring:
  2. autoconfigure:
  3. exclude:
  4. - org.springframework.shell.boot.ExitCodeAutoConfiguration
  5. - org.springframework.shell.boot.ShellContextAutoConfiguration
  6. - org.springframework.shell.boot.SpringShellAutoConfiguration
  7. - org.springframework.shell.boot.ShellRunnerAutoConfiguration
  8. - org.springframework.shell.boot.ApplicationRunnerAutoConfiguration
  9. - org.springframework.shell.boot.CommandCatalogAutoConfiguration
  10. - org.springframework.shell.boot.LineReaderAutoConfiguration
  11. - org.springframework.shell.boot.CompleterAutoConfiguration
  12. - org.springframework.shell.boot.UserConfigAutoConfiguration
  13. - org.springframework.shell.boot.JLineAutoConfiguration
  14. - org.springframework.shell.boot.JLineShellAutoConfiguration
  15. - org.springframework.shell.boot.ParameterResolverAutoConfiguration
  16. - org.springframework.shell.boot.StandardAPIAutoConfiguration
  17. - org.springframework.shell.boot.ThemingAutoConfiguration
  18. - org.springframework.shell.boot.StandardCommandsAutoConfiguration
  19. - org.springframework.shell.boot.ComponentFlowAutoConfiguration

Configuration

Please check
class: SshShellProperties.java
for more
information

  1. ssh:
  2. shell:
  3. enable: true
  4. # 'simple' or 'security'
  5. authentication: simple
  6. # if authentication set to 'security' the AuthenticationProvider bean name
  7. # if not specified and only one AuthenticationProvider bean is present in the context, it will be used
  8. auth-provider-bean-name:
  9. # since 1.2.2, optional file containing authorized public keys (standard authorized_keys format, one key per line
  10. # starting with 'ssh-rsa'), takes precedence over authentication (simple or not)
  11. authorized-public-keys-file:
  12. # since 1.5.5, optional spring resource containing authorized public keys (file:, classpath: , etc)
  13. # note: in case of a non file resource, a temporary file is created with given content and deleted on process exit
  14. # this is due to ssh external library which only accepts file in api
  15. authorized-public-keys:
  16. # for ssh helper 'confirm' method
  17. confirmation-words:
  18. - y
  19. - yes
  20. # since 1.4.0, set enable to false to disable following default commands
  21. commands:
  22. actuator:
  23. create: true
  24. enable: true
  25. restricted: true
  26. # empty by default
  27. excludes:
  28. - ...
  29. authorized-roles:
  30. - ACTUATOR
  31. # since 1.4.0
  32. jmx:
  33. create: true
  34. enable: true
  35. restricted: true
  36. authorized-roles:
  37. - ADMIN
  38. system:
  39. create: true
  40. enable: true
  41. restricted: true
  42. authorized-roles:
  43. - ADMIN
  44. # since 1.4.0
  45. datasource:
  46. create: true
  47. enable: true
  48. restricted: true
  49. authorized-roles:
  50. - ADMIN
  51. excludes:
  52. - datasource-update
  53. postprocessors:
  54. create: true
  55. enable: true
  56. restricted: false
  57. # history and script added in 1.8.0
  58. history:
  59. create: true
  60. enable: true
  61. restricted: false
  62. script:
  63. create: true
  64. enable: true
  65. restricted: false
  66. # since 1.3.0, command which allows you to list ssh sessions, and stop them
  67. manage-sessions:
  68. create: true
  69. enable: false
  70. restricted: true
  71. authorized-roles:
  72. - ADMIN
  73. # since 1.5.0
  74. tasks:
  75. create: true
  76. enable: false
  77. restricted: true
  78. authorized-roles:
  79. - ADMIN
  80. display-banner: true
  81. history-file: <java.io.tmpdir>/sshShellHistory.log
  82. # since 1.3.0, set to false to have one file per user (<history-directory>/sshShellHistory-<user>.log)
  83. shared-history: true
  84. # since 1.3.0, only if shared-history is set to false
  85. history-directory: <java.io.tmpdir>
  86. host: 127.0.0.1
  87. host-key-file: <java.io.tmpdir>/hostKey.ser
  88. # displayed in log if generated
  89. password:
  90. port: 2222
  91. user: user
  92. prompt:
  93. # in enum: com.github.fonimus.ssh.shell.PromptColor (black, red, green, yellow, blue, magenta, cyan, white, bright)
  94. color: white
  95. text: 'shell>'
  • Add spring-boot-starter-actuator dependency to get actuator commands

  • Add spring-boot-starter-security dependency to
    configure ssh.shell.authentication=security with
    AuthenticationProvider

Default behavior

Some commands are disabled by default, it can be the whole group (
like manage-sessions), or just
one sub command (like datasource-update in group datasource).

To enable a group, set the enable property to true :

  1. ssh:
  2. shell:
  3. commands:
  4. manage-sessions:
  5. enable: true
  6. datasource:
  7. excludes:

To un-exclude a sub command inside a group, set the excludes property to the
new wanted
array. To include all sub commands, set new empty array :

  1. ssh:
  2. shell:
  3. commands:
  4. datasource:
  5. excludes:

Writing commands

You can write your command exactly the way you would do with spring shell (For
more information please
visit spring shell website.

Instead of using org.springframework.shell.standard.ShellComponent annotation,
you should
use com.github.fonimus.ssh.shell.commands.SshShellComponent:
it is just a conditional @ShellComponent with @ConditionalOnProperty on
property ssh.shell.enable

Example:

  1. import org.springframework.shell.standard.ShellCommandGroup;
  2. import org.springframework.shell.standard.ShellMethod;
  3. import com.github.fonimus.ssh.shell.commands.SshShellComponent;
  4. @SshShellComponent
  5. @ShellCommandGroup("Test Commands")
  6. public class TestCommands {
  7. @ShellMethod("test command")
  8. public String test() {
  9. return "ok";
  10. }
  11. }

Commands

All commands group can be deactivated by enable property :

  1. ssh:
  2. shell:
  3. commands:
  4. <command>:
  5. enable: true

Sub commands in group can be also filtered by includes and excludes properties :

  1. ssh:
  2. shell:
  3. commands:
  4. <command>:
  5. includes:
  6. - xxx
  7. excludes:
  8. - xxx

Actuator

If org.springframework.boot:spring-boot-starter-actuator dependency is
present, actuator commands
will be available.

Command availability is also bind to endpoint activation.

  1. management:
  2. endpoint:
  3. audit:
  4. enabled: false

Tasks

Activated by default if you have @EnableScheduling,
these commands allow you to interact with spring boot scheduled tasks :

  • tasks-list : List scheduled tasks
  • tasks-stop : Stop one or all scheduled tasks or execution
  • tasks-restart : Restart one or all scheduled tasks
  • tasks-single : Launch one execution of all or specified task(s)

Note: refresh parameter in tasks-list will remove single executions.

Task scheduler

Based on spring
documentation org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.setScheduler
the task scheduler used for scheduled tasks will be :

If not specified, it will look for unique bean of type TaskScheduler, or
with name
taskScheduler. Otherwise, a local single-threaded will be created.

The TasksCommand keep the same mechanism in order to be able to restart
stopped scheduled tasks.
It also provides a setTaskScheduler() in case you want to specify custom
one.

Examples
Context Task scheduler used in TaskCommand
No TaskScheduler bean in context Local single-threaded
One TaskScheduler bean named ts in context ts bean
Multiple TaskScheduler beans named ts1, ts2 in context Local single-threaded (could not find name taskScheduler)
Multiple TaskScheduler beans named taskScheduler, ts2, ts3 in context taskScheduler bean
Task scheduler specified in method SchedulingConfigurer#configureTasks Local single-threaded (not set in task)
Task scheduler specified in method SchedulingConfigurer#configureTasks AND com.github.fonimus.ssh.shell.commands.TasksCommand.setTaskScheduler Scheduler manually set

Jmx

  • jmx-info: Displays information about jmx mbean. Use -a option to query
    attribute values.
  • jmx-invoke: Invoke operation on object name.
  • jmx-list: List jmx mbeans.

System

  • system-env: List system environment.
  • system-properties: List system properties.
  • system-threads: List jvm threads.

Datasource

  • datasource-list: List available datasources
  • datasource-properties: Datasource properties command. Executes ‘show
    variables’
  • datasource-query: Datasource query command.
  • datasource-update: Datasource update command.

Postprocessors

  • postprocessors: Display the available post processors

Post processors

Note: since 1.0.6

Post processors can be used with ‘|’ (pipe character) followed by the name of
the post processor and the parameters.
Also, custom ones can be added.

Provided post processors

Save

This specific post processor takes the key character ‘>’.

Example: echo test > /path/to/file.txt

Pretty

This post processor, named pretty takes an object and apply jackson pretty
writer.

Example: info | pretty

Json

This post processor, named json allows you to find a specific path within a
json object.

Caution: you need to have a json string. You can apply pretty post processor
before to do so.

Example: info | pretty | json /build/version

Grep

This post processor, named grep allows you to find specific patterns within a
string.

Examples: info | grep boot,info | pretty | grep boot spring

Highlight

This post processor, named highlight allows you to highlight specific patterns
within a string.

Examples: info | highlight boot,info | pretty | highlight boot spring

Custom

To register a new json result post processor, you need to implement
interface PostProcessor

Then register it within a spring configuration.

Example:

  1. @Configuration
  2. class PostProcessorConfiguration {
  3. @Bean
  4. public PostProcessor quotePostProcessor() {
  5. return new PostProcessor<String>() {
  6. @Override
  7. public String getName() {
  8. return "quote";
  9. }
  10. @Override
  11. public String process(String result, List parameters) {
  12. return "'" + result + "'";
  13. }
  14. };
  15. }
  16. }

Parameter providers

Enum

Enumeration option parameters have auto completion by default.

File

Thanks to ExtendedFileValueProvider.java
(or FileValueProvider is deactivated), auto completion is available
for java.io.File option parameters.

Custom values

To enable auto completion for a parameter, declare a valueProvider class.

Note: the value provider has to be in the spring context.

  1. class Commands {
  2. public command(@ShellOption(valueProvider = CustomValuesProvider.class) String message) {
  3. // deal with message
  4. }
  5. }
  6. @Component
  7. class CustomValuesProvider implements ValueProvider {
  8. private final static String[] VALUES = new String[]{
  9. "message1", "message2", "message3"
  10. };
  11. @Override
  12. public List<CompletionProposal> complete(CompletionContext completionContext) {
  13. return Arrays.stream(VALUES).map(CompletionProposal::new).collect(Collectors.toList());
  14. }
  15. }

Custom authentication

Instead of setting user and password (or using generated one), you can implement
your
own SshShellAuthenticationProvider.

Auto configuration will create default implementation only if there is not an
existing one in the spring context.

Example:

  1. import com.github.fonimus.ssh.shell.SshShellAuthenticationProvider;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. @Configuration
  5. public class CustomPasswordConfiguration {
  6. @Bean
  7. public SshShellAuthenticationProvider sshShellAuthenticationProvider() {
  8. return (user, pass, serverSession) -> user.equals(pass);
  9. }
  10. }

Command helper

A com.github.fonimus.ssh.shell.SshShellHelper bean is provided in context to
help for additional functionalities.

You can either autowire it or inject it in a constructor:

  1. import com.github.fonimus.ssh.shell.SshShellHelper;
  2. @SshShellComponent
  3. public class DemoCommand {
  4. @Autowired
  5. private SshShellHelper helper;
  6. // or
  7. public DemoCommand(SshShellHelper helper) {
  8. this.helper = helper;
  9. }
  10. }

User interaction

Print output

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Print command")
  6. public String print() {
  7. boolean success = true;
  8. helper.print("Some message");
  9. helper.print("Some black message", PromptColor.BLACK);
  10. helper.printSuccess("Some success message");
  11. return success ? helper.getSuccess("Some returned success message") : helper.getColored("Some returned blue message", PromptColor.BLUE);
  12. }
  13. }

Read input

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Welcome command")
  6. public String welcome() {
  7. String name = helper.read("What's your name ?");
  8. return "Hello, '" + name + "' !";
  9. }
  10. }

Confirmation

Util confirm method displays confirmation message and returns true
if response equals ignore case confirmation words.

Default confirmation words are [y, yes]:

You can specify if it is case sensitive and provide your own confirmation words.

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Confirmation command")
  6. public String conf() {
  7. return helper.confirm("Are you sure ?") ? "Great ! Let's do it !" : "Such a shame ...";
  8. }
  9. }

Table

A builder com.github.fonimus.ssh.shell.SimpleTableBuilder is available to
quickly set up print table.

Quick example:

  1. class Commands {
  2. public table() {
  3. helper.renderTable(SimpleTable.builder()
  4. .column("col1")
  5. .column("col2")
  6. .column("col3")
  7. .column("col4")
  8. .line(Arrays.asList("line1 col1", "line1 col2", "line1 col3", "line1 col4"))
  9. .line(Arrays.asList("line2 col1", "line2 col2", "line2 col3", "line2 col4"))
  10. .line(Arrays.asList("line3 col1", "line3 col2", "line3 col3", "line3 col4"))
  11. .line(Arrays.asList("line4 col1", "line4 col2", "line4 col3", "line4 col4"))
  12. .line(Arrays.asList("line5 col1", "line5 col2", "line5 col3", "line5 col4"))
  13. .line(Arrays.asList("line6 col1", "line6 col2", "line6 col3", "line6 col4"))
  14. .build());
  15. }
  16. }

Result :

  1. ┌──────────┬──────────┬──────────┬──────────┐
  2. col1 col2 col3 col4
  3. ├──────────┼──────────┼──────────┼──────────┤
  4. line1 col1line1 col2line1 col3line1 col4
  5. ├──────────┼──────────┼──────────┼──────────┤
  6. line2 col1line2 col2line2 col3line2 col4
  7. ├──────────┼──────────┼──────────┼──────────┤
  8. line3 col1line3 col2line3 col3line3 col4
  9. ├──────────┼──────────┼──────────┼──────────┤
  10. line4 col1line4 col2line4 col3line4 col4
  11. ├──────────┼──────────┼──────────┼──────────┤
  12. line5 col1line5 col2line5 col3line5 col4
  13. ├──────────┼──────────┼──────────┼──────────┤
  14. line6 col1line6 col2line6 col3line6 col4
  15. └──────────┴──────────┴──────────┴──────────┘

Interactive

Note: since 1.1.3

This method takes an interface to display lines at regular interval.

Every refresh delay (here 2
seconds), com.github.fonimus.ssh.shell.interactive.InteractiveInput.getLines
is
called.

This can be used to display progress, monitoring, etc.

The interactive
builder, Interactive.java
allows you to build your interactive command.

This builder can also take key bindings to make specific actions, whose can be
made by the following builder:
KeyBinding.java.

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Interactive command")
  6. public void interactive() {
  7. KeyBinding binding = KeyBinding.builder()
  8. .description("K binding example")
  9. .key("k").input(() -> LOGGER.info("In specific action triggered by key 'k' !")).build();
  10. Interactive interactive = Interactive.builder().input((size, currentDelay) -> {
  11. LOGGER.info("In interactive command for input...");
  12. List<AttributedString> lines = new ArrayList<>();
  13. AttributedStringBuilder sb = new AttributedStringBuilder(size.getColumns());
  14. sb.append("\nCurrent time", AttributedStyle.BOLD).append(" : ");
  15. sb.append(String.format("%8tT", new Date()));
  16. lines.add(sb.toAttributedString());
  17. SecureRandom sr = new SecureRandom();
  18. lines.add(new AttributedStringBuilder().append(helper.progress(sr.nextInt(100)),
  19. AttributedStyle.DEFAULT.foreground(sr.nextInt(6) + 1)).toAttributedString());
  20. lines.add(AttributedString.fromAnsi(SshShellHelper.INTERACTIVE_LONG_MESSAGE + "\n"));
  21. return lines;
  22. }).binding(binding).fullScreen(true | false).refreshDelay(5000).build();
  23. helper.interactive(interactive);
  24. }
  25. }

Note: existing key bindings are:

  • q: to quit interactive command and go back to shell
  • +: to increase refresh delay by 1000 milliseconds
  • -: to decrease refresh delay by 1000 milliseconds

Role check

If you are using AuthenticationProvider thanks to
property ssh.shell.authentication=security, you can check that
connected user has right authorities for command.
The easiest way of doing it is thanks to ShellMethodAvailability
functionality. Example:

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Admin command")
  6. @ShellMethodAvailability("adminAvailability")
  7. public String admin() {
  8. return "Finally an administrator !!";
  9. }
  10. public Availability adminAvailability() {
  11. if (!helper.checkAuthorities(Collections.singletonList("ADMIN"))) {
  12. return Availability.unavailable("admin command is only for an admin users !");
  13. }
  14. return Availability.available();
  15. }
  16. }

Retrieve spring security authentication

  1. @SshShellComponent
  2. public class DemoCommand {
  3. @Autowired
  4. private SshShellHelper helper;
  5. @ShellMethod("Authentication command")
  6. public SshAuthentication authentication() {
  7. return helper.getAuthentication();
  8. }
  9. }

Banner

If a banner is found in spring context and display-banner is set to true,
it will be used as welcome prompt message.

Listeners

An interface is provided in order to receive events on ssh
sessions : com.github.fonimus.ssh.shell.listeners .SshShellListener.

Implement it and define a spring bean in order to receive events.

Example

  1. @Configuration
  2. class ShellListenerConfiguration {
  3. @Bean
  4. public SshShellListener sshShellListener() {
  5. return event -> LOGGER.info("[listener] event '{}' [id={}, ip={}]",
  6. event.getType(),
  7. event.getSession().getServerSession().getIoSession().getId(),
  8. event.getSession().getServerSession().getIoSession().getRemoteAddress());
  9. }
  10. }

Session Manager

Note: since 1.3.0`

A session manager bean is available and allows you to:

  • list active sessions
  • get information about one session
  • stop a session

Note: you need to use @Lazy injection if you are using it in a command

Example

  1. class Commands {
  2. public MyCommand(@Lazy SshShellSessionManager sessionManager) {
  3. this.sessionManager = sessionManager;
  4. }
  5. @ShellMethod("My command")
  6. public String myCommand() {
  7. sessionManager.listSessions();
  8. //...
  9. }
  10. }

Manage sessions commands

If activated ssh.shell.commands.manage-sessions.enable=true, the following
commands are available :

  • manage-sessions-info: Displays information about single session
  • manage-sessions-list: Displays active sessions
  • manage-sessions-stop: Stop single specific session

Tests

It can be annoying to load ssh server during spring boot tests.
SshShellProperties class provides constants to easily deactivate
the all ssh and spring shell auto configurations:

  1. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {"ssh.shell.port=2346",
  2. SshShellProperties.DISABLE_SSH_SHELL,
  3. SshShellProperties.DISABLE_SPRING_SHELL_AUTO_CONFIG
  4. })
  5. @ExtendWith(SpringExtension.class)
  6. public class ApplicationTest {
  7. }

Samples

Release notes

Please check github releases page.