Commit c5cc626d authored by Stephane Nicoll's avatar Stephane Nicoll

Remove remote shell support

See gh-7044
parent 66a3df45
...@@ -234,21 +234,6 @@ ...@@ -234,21 +234,6 @@
<artifactId>tomcat-embed-el</artifactId> <artifactId>tomcat-embed-el</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.shell</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.cli</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.embed.spring</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.jolokia</groupId> <groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId> <artifactId>jolokia-core</artifactId>
...@@ -334,11 +319,6 @@ ...@@ -334,11 +319,6 @@
<artifactId>aspectjrt</artifactId> <artifactId>aspectjrt</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.telnet</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>
......
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.crsh.auth.AuthenticationPlugin;
import org.crsh.plugin.CRaSHPlugin;
import org.crsh.plugin.PluginContext;
import org.crsh.plugin.PluginDiscovery;
import org.crsh.plugin.PluginLifeCycle;
import org.crsh.plugin.PropertyDescriptor;
import org.crsh.plugin.ServiceLoaderDiscovery;
import org.crsh.vfs.FS;
import org.crsh.vfs.spi.AbstractFSDriver;
import org.crsh.vfs.spi.FSDriver;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.CrshShellAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.CrshShellProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.JaasAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.KeyAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.SimpleAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.SpringAuthenticationProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.SpringVersion;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for embedding an extensible shell
* into a Spring Boot enabled application. By default a SSH daemon is started on port
* 2000. If the CRaSH Telnet plugin is available on the classpath, Telnet daemon will be
* launched on port 5000.
* <p>
* The default shell authentication method uses a username and password combination. If no
* configuration is provided the default username is 'user' and the password will be
* printed to console during application startup. Those default values can be overridden
* by using {@code management.shell.auth.simple.username} and
* {@code management.shell.auth.simple.password}.
* <p>
* If a Spring Security {@link AuthenticationManager} is detected, this configuration will
* create a {@link CRaSHPlugin} to forward shell authentication requests to Spring
* Security. This authentication method will get enabled if
* {@code management.shell.auth.type} is set to {@code spring} or if no explicit
* {@code management.shell.auth} is provided and a {@link AuthenticationManager} is
* available. In the latter case shell access will be restricted to users having roles
* that match those configured in {@link ManagementServerProperties}. Required roles can
* be overridden by {@code management.shell.auth.spring.roles}.
* <p>
* To add customizations to the shell simply define beans of type {@link CRaSHPlugin} in
* the application context. Those beans will get auto detected during startup and
* registered with the underlying shell infrastructure. To configure plugins and the CRaSH
* infrastructure add beans of type {@link CrshShellProperties} to the application
* context.
* <p>
* Additional shell commands can be implemented using the guide and documentation at
* <a href="http://www.crashub.org">crashub.org</a>. By default Boot will search for
* commands using the following classpath scanning pattern {@code classpath*:/commands/**}
* . To add different locations or override the default use
* {@code management.shell.command-path-patterns} in your application configuration.
*
* @author Christian Dupuis
* @author Matt Benson
* @see ShellProperties
*/
@Configuration
@ConditionalOnClass(PluginLifeCycle.class)
@EnableConfigurationProperties(ShellProperties.class)
@AutoConfigureAfter({ SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class })
@Deprecated
public class CrshAutoConfiguration {
public static final String AUTH_PREFIX = ShellProperties.SHELL_PREFIX + ".auth";
private final ShellProperties properties;
public CrshAutoConfiguration(ShellProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(PluginLifeCycle.class)
public CrshBootstrapBean shellBootstrap() {
CrshBootstrapBean bootstrapBean = new CrshBootstrapBean();
bootstrapBean.setConfig(this.properties.asCrshShellConfig());
return bootstrapBean;
}
@Configuration
static class CrshAdditionalPropertiesConfiguration {
@Bean
@ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "jaas")
@ConditionalOnMissingBean(CrshShellAuthenticationProperties.class)
public JaasAuthenticationProperties jaasAuthenticationProperties() {
return new JaasAuthenticationProperties();
}
@Bean
@ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "key")
@ConditionalOnMissingBean(CrshShellAuthenticationProperties.class)
public KeyAuthenticationProperties keyAuthenticationProperties() {
return new KeyAuthenticationProperties();
}
@Bean
@ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "simple", matchIfMissing = true)
@ConditionalOnMissingBean(CrshShellAuthenticationProperties.class)
public SimpleAuthenticationProperties simpleAuthenticationProperties() {
return new SimpleAuthenticationProperties();
}
}
/**
* Class to configure CRaSH to authenticate against Spring Security.
*/
@Configuration
@ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "spring", matchIfMissing = true)
@ConditionalOnBean(AuthenticationManager.class)
public static class AuthenticationManagerAdapterConfiguration {
private final ManagementServerProperties management;
public AuthenticationManagerAdapterConfiguration(
ObjectProvider<ManagementServerProperties> managementProvider) {
this.management = managementProvider.getIfAvailable();
}
@Bean
public AuthenticationManagerAdapter shellAuthenticationManager() {
return new AuthenticationManagerAdapter();
}
@Bean
@ConditionalOnMissingBean(CrshShellAuthenticationProperties.class)
public SpringAuthenticationProperties springAuthenticationProperties() {
// In case no management.shell.auth.type property is provided fall back to
// Spring Security based authentication and get role to access shell from
// ManagementServerProperties.
// In case management.shell.auth.type is set to spring and roles are
// configured using shell.auth.spring.roles the below default role will be
// overridden by ConfigurationProperties.
SpringAuthenticationProperties authenticationProperties = new SpringAuthenticationProperties();
if (this.management != null) {
List<String> roles = this.management.getSecurity().getRoles();
authenticationProperties
.setRoles(roles.toArray(new String[roles.size()]));
}
return authenticationProperties;
}
}
/**
* Spring Bean used to bootstrap the CRaSH shell.
*/
public static class CrshBootstrapBean extends PluginLifeCycle {
@Autowired
private ListableBeanFactory beanFactory;
@Autowired
private Environment environment;
@Autowired
private ShellProperties properties;
@Autowired
private ResourcePatternResolver resourceLoader;
@PreDestroy
public void destroy() {
stop();
}
@PostConstruct
public void init() {
FS commandFileSystem = createFileSystem(
this.properties.getCommandPathPatterns(),
this.properties.getDisabledCommands());
FS configurationFileSystem = createFileSystem(
this.properties.getConfigPathPatterns(), new String[0]);
PluginDiscovery discovery = new BeanFactoryFilteringPluginDiscovery(
this.resourceLoader.getClassLoader(), this.beanFactory,
this.properties.getDisabledPlugins());
PluginContext context = new PluginContext(discovery,
createPluginContextAttributes(), commandFileSystem,
configurationFileSystem, this.resourceLoader.getClassLoader());
context.refresh();
start(context);
}
protected FS createFileSystem(String[] pathPatterns, String[] filterPatterns) {
Assert.notNull(pathPatterns, "PathPatterns must not be null");
Assert.notNull(filterPatterns, "FilterPatterns must not be null");
FS fileSystem = new FS();
for (String pathPattern : pathPatterns) {
try {
fileSystem.mount(new SimpleFileSystemDriver(new DirectoryHandle(
pathPattern, this.resourceLoader, filterPatterns)));
}
catch (IOException ex) {
throw new IllegalStateException(
"Failed to mount file system for '" + pathPattern + "'", ex);
}
}
return fileSystem;
}
protected Map<String, Object> createPluginContextAttributes() {
Map<String, Object> attributes = new HashMap<String, Object>();
String bootVersion = CrshAutoConfiguration.class.getPackage()
.getImplementationVersion();
if (bootVersion != null) {
attributes.put("spring.boot.version", bootVersion);
}
attributes.put("spring.version", SpringVersion.getVersion());
if (this.beanFactory != null) {
attributes.put("spring.beanfactory", this.beanFactory);
}
if (this.environment != null) {
attributes.put("spring.environment", this.environment);
}
return attributes;
}
}
/**
* Adapts a Spring Security {@link AuthenticationManager} for use with CRaSH.
*/
@SuppressWarnings("rawtypes")
private static class AuthenticationManagerAdapter extends
CRaSHPlugin<AuthenticationPlugin> implements AuthenticationPlugin<String> {
private static final PropertyDescriptor<String> ROLES = PropertyDescriptor.create(
"auth.spring.roles", "ADMIN",
"Comma separated list of roles required to access the shell");
@Autowired
private AuthenticationManager authenticationManager;
@Autowired(required = false)
@Qualifier("shellAccessDecisionManager")
private AccessDecisionManager accessDecisionManager;
private String[] roles = new String[] { "ADMIN" };
@Override
public boolean authenticate(String username, String password) throws Exception {
Authentication token = new UsernamePasswordAuthenticationToken(username,
password);
try {
// Authenticate first to make sure credentials are valid
token = this.authenticationManager.authenticate(token);
}
catch (AuthenticationException ex) {
return false;
}
// Test access rights if a Spring Security AccessDecisionManager is installed
if (this.accessDecisionManager != null && token.isAuthenticated()
&& this.roles != null) {
try {
this.accessDecisionManager.decide(token, this,
SecurityConfig.createList(this.roles));
}
catch (AccessDeniedException ex) {
return false;
}
}
return token.isAuthenticated();
}
@Override
public Class<String> getCredentialType() {
return String.class;
}
@Override
public AuthenticationPlugin<String> getImplementation() {
return this;
}
@Override
public String getName() {
return "spring";
}
@Override
public void init() {
String rolesPropertyValue = getContext().getProperty(ROLES);
if (rolesPropertyValue != null) {
this.roles = StringUtils
.commaDelimitedListToStringArray(rolesPropertyValue);
}
}
@Override
protected Iterable<PropertyDescriptor<?>> createConfigurationCapabilities() {
return Arrays.<PropertyDescriptor<?>>asList(ROLES);
}
}
/**
* {@link ServiceLoaderDiscovery} to expose {@link CRaSHPlugin} Beans from Spring and
* deal with filtering disabled plugins.
*/
private static class BeanFactoryFilteringPluginDiscovery
extends ServiceLoaderDiscovery {
private final ListableBeanFactory beanFactory;
private final String[] disabledPlugins;
BeanFactoryFilteringPluginDiscovery(ClassLoader classLoader,
ListableBeanFactory beanFactory, String[] disabledPlugins)
throws NullPointerException {
super(classLoader);
this.beanFactory = beanFactory;
this.disabledPlugins = disabledPlugins;
}
@Override
@SuppressWarnings("rawtypes")
public Iterable<CRaSHPlugin<?>> getPlugins() {
List<CRaSHPlugin<?>> plugins = new ArrayList<CRaSHPlugin<?>>();
for (CRaSHPlugin<?> p : super.getPlugins()) {
if (isEnabled(p)) {
plugins.add(p);
}
}
Collection<CRaSHPlugin> pluginBeans = this.beanFactory
.getBeansOfType(CRaSHPlugin.class).values();
for (CRaSHPlugin<?> pluginBean : pluginBeans) {
if (isEnabled(pluginBean)) {
plugins.add(pluginBean);
}
}
return plugins;
}
protected boolean isEnabled(CRaSHPlugin<?> plugin) {
Assert.notNull(plugin, "Plugin must not be null");
if (ObjectUtils.isEmpty(this.disabledPlugins)) {
return true;
}
Set<Class<?>> pluginClasses = ClassUtils.getAllInterfacesAsSet(plugin);
pluginClasses.add(plugin.getClass());
for (Class<?> pluginClass : pluginClasses) {
if (!isEnabled(pluginClass)) {
return false;
}
}
return true;
}
private boolean isEnabled(Class<?> pluginClass) {
for (String disabledPlugin : this.disabledPlugins) {
if (ClassUtils.getShortName(pluginClass).equalsIgnoreCase(disabledPlugin)
|| ClassUtils.getQualifiedName(pluginClass)
.equalsIgnoreCase(disabledPlugin)) {
return false;
}
}
return true;
}
}
/**
* {@link FSDriver} to wrap Spring's {@link Resource} abstraction to CRaSH.
*/
private static class SimpleFileSystemDriver extends AbstractFSDriver<ResourceHandle> {
private final ResourceHandle root;
SimpleFileSystemDriver(ResourceHandle handle) {
this.root = handle;
}
@Override
public Iterable<ResourceHandle> children(ResourceHandle handle)
throws IOException {
if (handle instanceof DirectoryHandle) {
return ((DirectoryHandle) handle).members();
}
return Collections.emptySet();
}
@Override
public long getLastModified(ResourceHandle handle) throws IOException {
if (handle instanceof FileHandle) {
return ((FileHandle) handle).getLastModified();
}
return -1;
}
@Override
public boolean isDir(ResourceHandle handle) throws IOException {
return handle instanceof DirectoryHandle;
}
@Override
public String name(ResourceHandle handle) throws IOException {
return handle.getName();
}
@Override
public Iterator<InputStream> open(ResourceHandle handle) throws IOException {
if (handle instanceof FileHandle) {
return Collections.singletonList(((FileHandle) handle).openStream())
.iterator();
}
return Collections.<InputStream>emptyList().iterator();
}
@Override
public ResourceHandle root() throws IOException {
return this.root;
}
}
/**
* Base for handles to Spring {@link Resource}s.
*/
private abstract static class ResourceHandle {
private final String name;
ResourceHandle(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
/**
* {@link ResourceHandle} for a directory.
*/
private static class DirectoryHandle extends ResourceHandle {
private final ResourcePatternResolver resourceLoader;
private final String[] filterPatterns;
private final AntPathMatcher matcher = new AntPathMatcher();
DirectoryHandle(String name, ResourcePatternResolver resourceLoader,
String[] filterPatterns) {
super(name);
this.resourceLoader = resourceLoader;
this.filterPatterns = filterPatterns;
}
public List<ResourceHandle> members() throws IOException {
Resource[] resources = this.resourceLoader.getResources(getName());
List<ResourceHandle> files = new ArrayList<ResourceHandle>();
for (Resource resource : resources) {
if (!resource.getURL().getPath().endsWith("/")
&& !shouldFilter(resource)) {
files.add(new FileHandle(resource.getFilename(), resource));
}
}
return files;
}
private boolean shouldFilter(Resource resource) {
for (String filterPattern : this.filterPatterns) {
if (this.matcher.match(filterPattern, resource.getFilename())) {
return true;
}
}
return false;
}
}
/**
* {@link ResourceHandle} for a file backed by a Spring {@link Resource}.
*/
private static class FileHandle extends ResourceHandle {
private final Resource resource;
FileHandle(String name, Resource resource) {
super(name);
this.resource = resource;
}
public InputStream openStream() throws IOException {
return this.resource.getInputStream();
}
public long getLastModified() {
try {
return this.resource.lastModified();
}
catch (IOException ex) {
return -1;
}
}
}
}
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Configuration properties for the shell subsystem.
*
* @author Christian Dupuis
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = ShellProperties.SHELL_PREFIX, ignoreUnknownFields = true)
@Deprecated
public class ShellProperties {
public static final String SHELL_PREFIX = "management.shell";
private static final Log logger = LogFactory.getLog(ShellProperties.class);
private final Auth auth = new Auth();
@Autowired(required = false)
private CrshShellProperties[] additionalProperties = new CrshShellProperties[] {
new SimpleAuthenticationProperties() };
/**
* Scan for changes and update the command if necessary (in seconds).
*/
private int commandRefreshInterval = -1;
/**
* Patterns to use to look for commands.
*/
private String[] commandPathPatterns = new String[] { "classpath*:/commands/**",
"classpath*:/crash/commands/**" };
/**
* Patterns to use to look for configurations.
*/
private String[] configPathPatterns = new String[] { "classpath*:/crash/*" };
/**
* Comma-separated list of commands to disable.
*/
private String[] disabledCommands = new String[] { "jpa*", "jdbc*", "jndi*" };
/**
* Comma-separated list of plugins to disable. Certain plugins are disabled by default
* based on the environment.
*/
private String[] disabledPlugins = new String[0];
private final Ssh ssh = new Ssh();
private final Telnet telnet = new Telnet();
public Auth getAuth() {
return this.auth;
}
public CrshShellProperties[] getAdditionalProperties() {
return this.additionalProperties;
}
public void setCommandRefreshInterval(int commandRefreshInterval) {
this.commandRefreshInterval = commandRefreshInterval;
}
public int getCommandRefreshInterval() {
return this.commandRefreshInterval;
}
public void setCommandPathPatterns(String[] commandPathPatterns) {
Assert.notEmpty(commandPathPatterns, "CommandPathPatterns must not be empty");
this.commandPathPatterns = commandPathPatterns;
}
public String[] getCommandPathPatterns() {
return this.commandPathPatterns;
}
public void setConfigPathPatterns(String[] configPathPatterns) {
Assert.notEmpty(configPathPatterns, "ConfigPathPatterns must not be empty");
this.configPathPatterns = configPathPatterns;
}
public String[] getConfigPathPatterns() {
return this.configPathPatterns;
}
public void setDisabledCommands(String[] disabledCommands) {
Assert.notEmpty(disabledCommands);
this.disabledCommands = disabledCommands;
}
public String[] getDisabledCommands() {
return this.disabledCommands;
}
public void setDisabledPlugins(String[] disabledPlugins) {
Assert.notEmpty(disabledPlugins);
this.disabledPlugins = disabledPlugins;
}
public String[] getDisabledPlugins() {
return this.disabledPlugins;
}
public Ssh getSsh() {
return this.ssh;
}
public Telnet getTelnet() {
return this.telnet;
}
/**
* Return a properties file configured from these settings that can be applied to a
* CRaSH shell instance.
* @return the CRaSH properties
*/
public Properties asCrshShellConfig() {
Properties properties = new Properties();
this.ssh.applyToCrshShellConfig(properties);
this.telnet.applyToCrshShellConfig(properties);
for (CrshShellProperties shellProperties : this.additionalProperties) {
shellProperties.applyToCrshShellConfig(properties);
}
if (this.commandRefreshInterval > 0) {
properties.put("crash.vfs.refresh_period",
String.valueOf(this.commandRefreshInterval));
}
// special handling for disabling Ssh and Telnet support
List<String> dp = new ArrayList<String>(Arrays.asList(this.disabledPlugins));
if (!this.ssh.isEnabled()) {
dp.add("org.crsh.ssh.SSHPlugin");
}
if (!this.telnet.isEnabled()) {
dp.add("org.crsh.telnet.TelnetPlugin");
}
this.disabledPlugins = dp.toArray(new String[dp.size()]);
validateCrshShellConfig(properties);
return properties;
}
/**
* Basic validation of applied CRaSH shell configuration.
* @param properties the properties to validate
*/
protected void validateCrshShellConfig(Properties properties) {
getAuth().validateCrshShellConfig(properties);
}
/**
* Base class for CRaSH properties.
*/
public static abstract class CrshShellProperties {
/**
* Apply the properties to a CRaSH configuration.
* @param config the CRaSH configuration properties
*/
protected abstract void applyToCrshShellConfig(Properties config);
}
/**
* Base class for Auth specific properties.
*/
public static abstract class CrshShellAuthenticationProperties
extends CrshShellProperties {
}
public static class Auth {
/**
* Authentication type. Auto-detected according to the environment (i.e. if Spring
* Security is available, "spring" is used by default).
*/
private String type = "simple";
private boolean defaultAuth = true;
public String getType() {
return this.type;
}
public void setType(String type) {
Assert.hasLength(type, "Auth type must not be empty");
this.type = type;
this.defaultAuth = false;
}
/**
* Basic validation of applied CRaSH shell configuration.
* @param properties the properties to validate
*/
protected void validateCrshShellConfig(Properties properties) {
String finalAuth = properties.getProperty("crash.auth");
if (!this.defaultAuth && !this.type.equals(finalAuth)) {
logger.warn(String.format(
"Shell authentication fell back to method '%s' opposed to "
+ "configured method '%s'. Please check your classpath.",
finalAuth, this.type));
}
// Make sure we keep track of final authentication method
this.type = finalAuth;
}
}
/**
* SSH properties.
*/
public static class Ssh extends CrshShellProperties {
/**
* Enable CRaSH SSH support.
*/
private boolean enabled = true;
/**
* Path to the SSH server key.
*/
private String keyPath;
/**
* SSH port.
*/
private Integer port = 2000;
/**
* Number of milliseconds after user will be prompted to login again.
*/
private Integer authTimeout = 600000;
/**
* Number of milliseconds after which unused connections are closed.
*/
private Integer idleTimeout = 600000;
@Override
protected void applyToCrshShellConfig(Properties config) {
if (this.enabled) {
config.put("crash.ssh.port", String.valueOf(this.port));
config.put("crash.ssh.auth_timeout", String.valueOf(this.authTimeout));
config.put("crash.ssh.idle_timeout", String.valueOf(this.idleTimeout));
if (this.keyPath != null) {
config.put("crash.ssh.keypath", this.keyPath);
}
}
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return this.enabled;
}
public void setKeyPath(String keyPath) {
Assert.hasText(keyPath, "keyPath must have text");
this.keyPath = keyPath;
}
public String getKeyPath() {
return this.keyPath;
}
public void setPort(Integer port) {
Assert.notNull(port, "port must not be null");
this.port = port;
}
public Integer getPort() {
return this.port;
}
public Integer getIdleTimeout() {
return this.idleTimeout;
}
public void setIdleTimeout(Integer idleTimeout) {
this.idleTimeout = idleTimeout;
}
public Integer getAuthTimeout() {
return this.authTimeout;
}
public void setAuthTimeout(Integer authTimeout) {
this.authTimeout = authTimeout;
}
}
/**
* Telnet properties.
*/
public static class Telnet extends CrshShellProperties {
/**
* Enable CRaSH telnet support. Enabled by default if the TelnetPlugin is
* available.
*/
private boolean enabled = ClassUtils.isPresent("org.crsh.telnet.TelnetPlugin",
ClassUtils.getDefaultClassLoader());
/**
* Telnet port.
*/
private Integer port = 5000;
@Override
protected void applyToCrshShellConfig(Properties config) {
if (this.enabled) {
config.put("crash.telnet.port", String.valueOf(this.port));
}
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return this.enabled;
}
public void setPort(Integer port) {
Assert.notNull(port, "port must not be null");
this.port = port;
}
public Integer getPort() {
return this.port;
}
}
/**
* Auth specific properties for JAAS authentication.
*/
@ConfigurationProperties(prefix = SHELL_PREFIX
+ ".auth.jaas", ignoreUnknownFields = false)
public static class JaasAuthenticationProperties
extends CrshShellAuthenticationProperties {
/**
* JAAS domain.
*/
private String domain = "my-domain";
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth", "jaas");
config.put("crash.auth.jaas.domain", this.domain);
}
public void setDomain(String domain) {
Assert.hasText(domain, "domain must have text");
this.domain = domain;
}
public String getDomain() {
return this.domain;
}
}
/**
* Auth specific properties for key authentication.
*/
@ConfigurationProperties(prefix = SHELL_PREFIX
+ ".auth.key", ignoreUnknownFields = false)
public static class KeyAuthenticationProperties
extends CrshShellAuthenticationProperties {
/**
* Path to the authentication key. This should point to a valid ".pem" file.
*/
private String path;
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth", "key");
if (this.path != null) {
config.put("crash.auth.key.path", this.path);
}
}
public void setPath(String path) {
Assert.hasText(path, "path must have text");
this.path = path;
}
public String getPath() {
return this.path;
}
}
/**
* Auth specific properties for simple authentication.
*/
@ConfigurationProperties(prefix = SHELL_PREFIX
+ ".auth.simple", ignoreUnknownFields = false)
public static class SimpleAuthenticationProperties
extends CrshShellAuthenticationProperties {
private static final Log logger = LogFactory
.getLog(SimpleAuthenticationProperties.class);
private User user = new User();
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth", "simple");
config.put("crash.auth.simple.username", this.user.getName());
config.put("crash.auth.simple.password", this.user.getPassword());
if (this.user.isDefaultPassword()) {
logger.info(String.format(
"%n%nUsing default password for shell access: %s%n%n",
this.user.getPassword()));
}
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
public static class User {
/**
* Login user.
*/
private String name = "user";
/**
* Login password.
*/
private String password = UUID.randomUUID().toString();
private boolean defaultPassword = true;
boolean isDefaultPassword() {
return this.defaultPassword;
}
public String getName() {
return this.name;
}
public String getPassword() {
return this.password;
}
public void setName(String name) {
Assert.hasLength(name, "name must have text");
this.name = name;
}
public void setPassword(String password) {
if (password.startsWith("${") && password.endsWith("}")
|| !StringUtils.hasLength(password)) {
return;
}
this.password = password;
this.defaultPassword = false;
}
}
}
/**
* Auth specific properties for Spring authentication.
*/
@ConfigurationProperties(prefix = SHELL_PREFIX
+ ".auth.spring", ignoreUnknownFields = false)
public static class SpringAuthenticationProperties
extends CrshShellAuthenticationProperties {
/**
* Comma-separated list of required roles to login to the CRaSH console.
*/
private String[] roles = new String[] { "ADMIN" };
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth", "spring");
config.put("crash.auth.spring.roles",
StringUtils.arrayToCommaDelimitedString(this.roles));
}
public void setRoles(String[] roles) {
// 'roles' can be null. This means no special to access right to connect to
// shell is required.
this.roles = roles;
}
public String[] getRoles() {
return this.roles;
}
}
}
...@@ -253,27 +253,6 @@ ...@@ -253,27 +253,6 @@
"name": "any" "name": "any"
} }
] ]
},
{
"name": "management.shell.auth.type",
"values": [
{
"value": "simple",
"description": "Use a simple user/password based authentication."
},
{
"value": "spring",
"description": "Integrate with Spring Security."
},
{
"value": "key",
"description": "Use a Key-based authentication."
},
{
"value": "jaas",
"description": "Use JAAS authentication."
}
]
} }
]} ]}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
......
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.crsh.auth.AuthenticationPlugin;
import org.crsh.auth.JaasAuthenticationPlugin;
import org.crsh.lang.impl.java.JavaLanguage;
import org.crsh.lang.spi.Language;
import org.crsh.plugin.PluginContext;
import org.crsh.plugin.PluginLifeCycle;
import org.crsh.plugin.ResourceKind;
import org.crsh.telnet.term.processor.ProcessorIOHandler;
import org.crsh.telnet.term.spi.TermIOHandler;
import org.crsh.vfs.Resource;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.testutil.Matched;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.isA;
/**
* Tests for {@link CrshAutoConfiguration}.
*
* @author Christian Dupuis
* @author Andreas Ahlenstorf
* @author Eddú Meléndez
* @author Matt Benson
* @author Stephane Nicoll
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Deprecated
public class CrshAutoConfigurationTests {
private AnnotationConfigWebApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testDisabledPlugins() throws Exception {
load("management.shell.disabled_plugins="
+ "termIOHandler, org.crsh.auth.AuthenticationPlugin, javaLanguage");
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle).isNotNull();
assertThat(lifeCycle.getContext().getPlugins(TermIOHandler.class))
.filteredOn(Matched.<TermIOHandler>when(isA(ProcessorIOHandler.class)))
.isEmpty();
assertThat(lifeCycle.getContext().getPlugins(AuthenticationPlugin.class))
.filteredOn(Matched
.<AuthenticationPlugin>when(isA(JaasAuthenticationPlugin.class)))
.isEmpty();
assertThat(lifeCycle.getContext().getPlugins(Language.class))
.filteredOn(Matched.<Language>when(isA(JavaLanguage.class))).isEmpty();
}
@Test
public void testAttributes() throws Exception {
load();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
Map<String, Object> attributes = lifeCycle.getContext().getAttributes();
assertThat(attributes.containsKey("spring.version")).isTrue();
assertThat(attributes.containsKey("spring.beanfactory")).isTrue();
assertThat(attributes.get("spring.beanfactory"))
.isEqualTo(this.context.getBeanFactory());
}
@Test
public void testSshConfiguration() {
load("management.shell.ssh.enabled=true", "management.shell.ssh.port=3333");
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.port")).isEqualTo("3333");
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.auth_timeout"))
.isEqualTo("600000");
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.idle_timeout"))
.isEqualTo("600000");
}
@Test
public void testSshConfigurationWithKeyPath() {
load("management.shell.ssh.enabled=true",
"management.shell.ssh.key_path=~/.ssh/id.pem");
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.keypath"))
.isEqualTo("~/.ssh/id.pem");
}
@Test
public void testSshConfigurationCustomTimeouts() {
load("management.shell.ssh.enabled=true",
"management.shell.ssh.auth-timeout=300000",
"management.shell.ssh.idle-timeout=400000");
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.auth_timeout"))
.isEqualTo("300000");
assertThat(lifeCycle.getConfig().getProperty("crash.ssh.idle_timeout"))
.isEqualTo("400000");
}
@Test
public void testCommandResolution() {
load();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
int count = 0;
Iterator<Resource> resources = lifeCycle.getContext()
.loadResources("login", ResourceKind.LIFECYCLE).iterator();
while (resources.hasNext()) {
count++;
resources.next();
}
assertThat(count).isEqualTo(1);
count = 0;
resources = lifeCycle.getContext()
.loadResources("sleep.groovy", ResourceKind.COMMAND).iterator();
while (resources.hasNext()) {
count++;
resources.next();
}
assertThat(count).isEqualTo(1);
}
@Test
public void testDisabledCommandResolution() {
load();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
int count = 0;
Iterator<Resource> resources = lifeCycle.getContext()
.loadResources("jdbc.groovy", ResourceKind.COMMAND).iterator();
while (resources.hasNext()) {
count++;
resources.next();
}
assertThat(count).isEqualTo(0);
}
@Test
public void testAuthenticationProvidersAreInstalled() {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
PluginContext pluginContext = lifeCycle.getContext();
int count = 0;
Iterator<AuthenticationPlugin> plugins = pluginContext
.getPlugins(AuthenticationPlugin.class).iterator();
while (plugins.hasNext()) {
count++;
plugins.next();
}
assertThat(count).isEqualTo(3);
}
@Test
public void testDefaultAuthenticationProvider() {
MockEnvironment env = new MockEnvironment();
this.context = new AnnotationConfigWebApplicationContext();
this.context.setEnvironment(env);
this.context.setServletContext(new MockServletContext());
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().get("crash.auth")).isEqualTo("simple");
}
@Test
public void testJaasAuthenticationProvider() {
this.context = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.shell.auth.type=jaas",
"management.shell.auth.jaas.domain=my-test-domain");
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().get("crash.auth")).isEqualTo("jaas");
assertThat(lifeCycle.getConfig().get("crash.auth.jaas.domain"))
.isEqualTo("my-test-domain");
}
@Test
public void testKeyAuthenticationProvider() {
this.context = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.shell.auth.type=key",
"management.shell.auth.key.path=~/test.pem");
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().get("crash.auth")).isEqualTo("key");
assertThat(lifeCycle.getConfig().get("crash.auth.key.path"))
.isEqualTo("~/test.pem");
}
@Test
public void testSimpleAuthenticationProvider() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.shell.auth.type=simple",
"management.shell.auth.simple.user.name=user",
"management.shell.auth.simple.user.password=password");
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
assertThat(lifeCycle.getConfig().get("crash.auth")).isEqualTo("simple");
AuthenticationPlugin<String> authenticationPlugin = null;
String authentication = lifeCycle.getConfig().getProperty("crash.auth");
assertThat(authentication).isNotNull();
for (AuthenticationPlugin plugin : lifeCycle.getContext()
.getPlugins(AuthenticationPlugin.class)) {
if (authentication.equals(plugin.getName())) {
authenticationPlugin = plugin;
break;
}
}
assertThat(authenticationPlugin).isNotNull();
assertThat(authenticationPlugin.authenticate("user", "password")).isTrue();
assertThat(authenticationPlugin.authenticate(UUID.randomUUID().toString(),
"password")).isFalse();
}
@Test
public void testSpringAuthenticationProvider() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.shell.auth.type=spring");
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
AuthenticationPlugin<String> authenticationPlugin = null;
String authentication = lifeCycle.getConfig().getProperty("crash.auth");
assertThat(authentication).isNotNull();
for (AuthenticationPlugin plugin : lifeCycle.getContext()
.getPlugins(AuthenticationPlugin.class)) {
if (authentication.equals(plugin.getName())) {
authenticationPlugin = plugin;
break;
}
}
assertThat(authenticationPlugin.authenticate(SecurityConfiguration.USERNAME,
SecurityConfiguration.PASSWORD)).isTrue();
assertThat(authenticationPlugin.authenticate(UUID.randomUUID().toString(),
SecurityConfiguration.PASSWORD)).isFalse();
}
@Test
public void testSpringAuthenticationProviderAsDefaultConfiguration()
throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(ManagementServerPropertiesAutoConfiguration.class);
this.context.register(SecurityAutoConfiguration.class);
this.context.register(SecurityConfiguration.class);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class);
AuthenticationPlugin<String> authenticationPlugin = null;
String authentication = lifeCycle.getConfig().getProperty("crash.auth");
assertThat(authentication).isNotNull();
for (AuthenticationPlugin plugin : lifeCycle.getContext()
.getPlugins(AuthenticationPlugin.class)) {
if (authentication.equals(plugin.getName())) {
authenticationPlugin = plugin;
break;
}
}
assertThat(authenticationPlugin.authenticate(SecurityConfiguration.USERNAME,
SecurityConfiguration.PASSWORD)).isTrue();
assertThat(authenticationPlugin.authenticate(UUID.randomUUID().toString(),
SecurityConfiguration.PASSWORD)).isFalse();
}
private void load(String... environment) {
this.context = new AnnotationConfigWebApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, environment);
this.context.register(CrshAutoConfiguration.class);
this.context.refresh();
}
@Configuration
public static class SecurityConfiguration {
public static final String USERNAME = UUID.randomUUID().toString();
public static final String PASSWORD = UUID.randomUUID().toString();
@Bean
public AuthenticationManager authenticationManager() {
return new AuthenticationManager() {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (authentication.getName().equals(USERNAME)
&& authentication.getCredentials().equals(PASSWORD)) {
authentication = new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
authentication.getCredentials(), Collections
.singleton(new SimpleGrantedAuthority("ADMIN")));
}
else {
throw new BadCredentialsException(
"Invalid username and password");
}
return authentication;
}
};
}
@Bean
public AccessDecisionManager shellAccessDecisionManager() {
List<AccessDecisionVoter<?>> voters = new ArrayList<AccessDecisionVoter<?>>();
RoleVoter voter = new RoleVoter();
voter.setRolePrefix("");
voters.add(voter);
return new UnanimousBased(voters);
}
}
}
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
import java.util.Properties;
import java.util.UUID;
import org.crsh.plugin.PluginLifeCycle;
import org.junit.After;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.CrshShellProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.JaasAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.KeyAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.SimpleAuthenticationProperties;
import org.springframework.boot.actuate.autoconfigure.ShellProperties.SpringAuthenticationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ShellProperties}.
*
* @author Christian Dupuis
* @author Stephane Nicoll
*/
@Deprecated
public class ShellPropertiesTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testBindingAuth() {
ShellProperties props = load(ShellProperties.class,
"management.shell.auth.type=spring");
assertThat(props.getAuth().getType()).isEqualTo("spring");
}
@Test
public void testBindingAuthIfEmpty() {
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("Auth type must not be empty");
load(ShellProperties.class, "management.shell.auth.type= ");
}
@Test
public void testBindingCommandRefreshInterval() {
ShellProperties props = load(ShellProperties.class,
"management.shell.command-refresh-interval=1");
assertThat(props.getCommandRefreshInterval()).isEqualTo(1);
}
@Test
public void testBindingCommandPathPatterns() {
ShellProperties props = load(ShellProperties.class,
"management.shell.command-path-patterns=pattern1, pattern2");
assertThat(props.getCommandPathPatterns().length).isEqualTo(2);
Assert.assertArrayEquals(new String[] { "pattern1", "pattern2" },
props.getCommandPathPatterns());
}
@Test
public void testBindingConfigPathPatterns() {
ShellProperties props = load(ShellProperties.class,
"management.shell.config-path-patterns=pattern1, pattern2");
assertThat(props.getConfigPathPatterns().length).isEqualTo(2);
Assert.assertArrayEquals(new String[] { "pattern1", "pattern2" },
props.getConfigPathPatterns());
}
@Test
public void testBindingDisabledPlugins() {
ShellProperties props = load(ShellProperties.class,
"management.shell.disabled-plugins=pattern1, pattern2");
assertThat(props.getDisabledPlugins().length).isEqualTo(2);
assertThat(props.getDisabledPlugins()).containsExactly("pattern1", "pattern2");
}
@Test
public void testBindingDisabledCommands() {
ShellProperties props = load(ShellProperties.class,
"management.shell.disabled-commands=pattern1, pattern2");
assertThat(props.getDisabledCommands()).containsExactly("pattern1", "pattern2");
}
@Test
public void testBindingSsh() {
ShellProperties props = load(ShellProperties.class,
"management.shell.ssh.enabled=true", "management.shell.ssh.port=2222",
"management.shell.ssh.key-path=~/.ssh/test.pem");
Properties p = props.asCrshShellConfig();
assertThat(p.get("crash.ssh.port")).isEqualTo("2222");
assertThat(p.get("crash.ssh.keypath")).isEqualTo("~/.ssh/test.pem");
}
@Test
public void testBindingSshIgnored() {
ShellProperties props = load(ShellProperties.class,
"management.shell.ssh.enabled=false", "management.shell.ssh.port=2222",
"management.shell.ssh.key-path=~/.ssh/test.pem");
Properties p = props.asCrshShellConfig();
assertThat(p.get("crash.ssh.port")).isNull();
assertThat(p.get("crash.ssh.keypath")).isNull();
}
@Test
public void testBindingTelnet() {
ShellProperties props = load(ShellProperties.class,
"management.shell.telnet.enabled=true",
"management.shell.telnet.port=2222");
Properties p = props.asCrshShellConfig();
assertThat(p.get("crash.telnet.port")).isEqualTo("2222");
}
@Test
public void testBindingTelnetIgnored() {
ShellProperties props = load(ShellProperties.class,
"management.shell.telnet.enabled=false",
"management.shell.telnet.port=2222");
Properties p = props.asCrshShellConfig();
assertThat(p.get("crash.telnet.port")).isNull();
}
@Test
public void testBindingJaas() {
JaasAuthenticationProperties props = load(JaasAuthenticationProperties.class,
"management.shell.auth.jaas.domain=my-test-domain");
Properties p = new Properties();
props.applyToCrshShellConfig(p);
assertThat(p.get("crash.auth.jaas.domain")).isEqualTo("my-test-domain");
}
@Test
public void testBindingKey() {
KeyAuthenticationProperties props = load(KeyAuthenticationProperties.class,
"management.shell.auth.key.path=~/.ssh/test.pem");
Properties p = new Properties();
props.applyToCrshShellConfig(p);
assertThat(p.get("crash.auth.key.path")).isEqualTo("~/.ssh/test.pem");
}
@Test
public void testBindingKeyIgnored() {
KeyAuthenticationProperties props = load(KeyAuthenticationProperties.class);
Properties p = new Properties();
props.applyToCrshShellConfig(p);
assertThat(p.get("crash.auth.key.path")).isNull();
}
@Test
public void testBindingSimple() {
SimpleAuthenticationProperties props = load(SimpleAuthenticationProperties.class,
"management.shell.auth.simple.user.name=username123",
"management.shell.auth.simple.user.password=password123");
Properties p = new Properties();
props.applyToCrshShellConfig(p);
assertThat(p.get("crash.auth.simple.username")).isEqualTo("username123");
assertThat(p.get("crash.auth.simple.password")).isEqualTo("password123");
}
@Test
public void testDefaultPasswordAutoGeneratedIfUnresolvedPlaceholder() {
SimpleAuthenticationProperties security = load(
SimpleAuthenticationProperties.class,
"management.shell.auth.simple.user.password=${ADMIN_PASSWORD}");
assertThat(security.getUser().isDefaultPassword()).isTrue();
}
@Test
public void testDefaultPasswordAutoGeneratedIfEmpty() {
SimpleAuthenticationProperties security = load(
SimpleAuthenticationProperties.class,
"management.shell.auth.simple.user.password=");
assertThat(security.getUser().isDefaultPassword()).isTrue();
}
@Test
public void testBindingSpring() {
SpringAuthenticationProperties props = load(SpringAuthenticationProperties.class,
"management.shell.auth.spring.roles=role1,role2");
Properties p = new Properties();
props.applyToCrshShellConfig(p);
assertThat(p.get("crash.auth.spring.roles")).isEqualTo("role1,role2");
}
@Test
public void testCustomShellProperties() throws Exception {
MockEnvironment env = new MockEnvironment();
env.setProperty("management.shell.auth.type", "simple");
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.setEnvironment(env);
ctx.setServletContext(new MockServletContext());
ctx.register(TestShellConfiguration.class);
ctx.register(CrshAutoConfiguration.class);
ctx.refresh();
PluginLifeCycle lifeCycle = ctx.getBean(PluginLifeCycle.class);
String uuid = lifeCycle.getConfig().getProperty("test.uuid");
assertThat(uuid).isEqualTo(TestShellConfiguration.uuid);
ctx.close();
}
private <T> T load(Class<T> type, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
ctx.register(TestConfiguration.class);
ctx.refresh();
this.context = ctx;
return this.context.getBean(type);
}
@Configuration
@EnableConfigurationProperties({ ShellProperties.class,
JaasAuthenticationProperties.class, KeyAuthenticationProperties.class,
SimpleAuthenticationProperties.class, SpringAuthenticationProperties.class })
static class TestConfiguration {
}
@Configuration
public static class TestShellConfiguration {
public static String uuid = UUID.randomUUID().toString();
@Bean
public CrshShellProperties testProperties() {
return new CrshShellProperties() {
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("test.uuid", uuid);
}
};
}
}
}
...@@ -51,12 +51,6 @@ public class ReproIntegrationTests { ...@@ -51,12 +51,6 @@ public class ReproIntegrationTests {
assertThat(this.cli.getOutput()).contains("Hello World"); assertThat(this.cli.getOutput()).contains("Hello World");
} }
@Test
public void shellDependencies() throws Exception {
this.cli.run("crsh.groovy");
assertThat(this.cli.getHttpOutput()).contains("{\"message\":\"Hello World\"}");
}
@Test @Test
public void dataJpaDependencies() throws Exception { public void dataJpaDependencies() throws Exception {
this.cli.run("data-jpa.groovy"); this.cli.run("data-jpa.groovy");
...@@ -67,7 +61,7 @@ public class ReproIntegrationTests { ...@@ -67,7 +61,7 @@ public class ReproIntegrationTests {
public void jarFileExtensionNeeded() throws Exception { public void jarFileExtensionNeeded() throws Exception {
this.thrown.expect(IllegalStateException.class); this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("is not a JAR file"); this.thrown.expectMessage("is not a JAR file");
this.cli.jar("secure.groovy", "crsh.groovy"); this.cli.jar("secure.groovy");
} }
} }
package org.test
@Grab("spring-boot-starter-remote-shell")
@RestController
class SampleController {
@RequestMapping("/")
public def hello() {
[message: "Hello World"]
}
}
...@@ -63,7 +63,6 @@ ...@@ -63,7 +63,6 @@
<commons-pool2.version>2.4.2</commons-pool2.version> <commons-pool2.version>2.4.2</commons-pool2.version>
<couchbase-client.version>2.2.8</couchbase-client.version> <couchbase-client.version>2.2.8</couchbase-client.version>
<couchbase-cache-client.version>2.0.0</couchbase-cache-client.version> <couchbase-cache-client.version>2.0.0</couchbase-cache-client.version>
<crashub.version>1.3.2</crashub.version>
<derby.version>10.12.1.1</derby.version> <derby.version>10.12.1.1</derby.version>
<dom4j.version>1.6.1</dom4j.version> <dom4j.version>1.6.1</dom4j.version>
<dropwizard-metrics.version>3.1.2</dropwizard-metrics.version> <dropwizard-metrics.version>3.1.2</dropwizard-metrics.version>
...@@ -442,11 +441,6 @@ ...@@ -442,11 +441,6 @@
<artifactId>spring-boot-starter-jta-narayana</artifactId> <artifactId>spring-boot-starter-jta-narayana</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version> <version>2.0.0.BUILD-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
...@@ -1454,47 +1448,6 @@ ...@@ -1454,47 +1448,6 @@
<artifactId>janino</artifactId> <artifactId>janino</artifactId>
<version>${janino.version}</version> <version>${janino.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.cli</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.ssh</artifactId>
<version>${crashub.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.telnet</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.embed.spring</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.plugins.cron</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.plugins.mail</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.shell</artifactId>
<version>${crashub.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId> <artifactId>jetty-annotations</artifactId>
......
...@@ -348,22 +348,6 @@ ...@@ -348,22 +348,6 @@
<artifactId>groovy-templates</artifactId> <artifactId>groovy-templates</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.cli</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.shell</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<artifactId>groovy-all</artifactId>
<groupId>org.codehaus.groovy</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
......
...@@ -1042,26 +1042,6 @@ content into your application; rather pick only the properties that you need. ...@@ -1042,26 +1042,6 @@ content into your application; rather pick only the properties that you need.
management.info.git.enabled=true # Enable git info. management.info.git.enabled=true # Enable git info.
management.info.git.mode=simple # Mode to use to expose git information. management.info.git.mode=simple # Mode to use to expose git information.
# REMOTE SHELL ({sc-spring-boot-actuator}/autoconfigure/ShellProperties.{sc-ext}[ShellProperties])
management.shell.auth.type=simple # Authentication type. Auto-detected according to the environment.
management.shell.auth.jaas.domain=my-domain # JAAS domain.
management.shell.auth.key.path= # Path to the authentication key. This should point to a valid ".pem" file.
management.shell.auth.simple.user.name=user # Login user.
management.shell.auth.simple.user.password= # Login password.
management.shell.auth.spring.roles=ADMIN # Comma-separated list of required roles to login to the CRaSH console.
management.shell.command-path-patterns=classpath*:/commands/**,classpath*:/crash/commands/** # Patterns to use to look for commands.
management.shell.command-refresh-interval=-1 # Scan for changes and update the command if necessary (in seconds).
management.shell.config-path-patterns=classpath*:/crash/* # Patterns to use to look for configurations.
management.shell.disabled-commands=jpa*,jdbc*,jndi* # Comma-separated list of commands to disable.
management.shell.disabled-plugins= # Comma-separated list of plugins to disable. Certain plugins are disabled by default based on the environment.
management.shell.ssh.auth-timeout = # Number of milliseconds after user will be prompted to login again.
management.shell.ssh.enabled=true # Enable CRaSH SSH support.
management.shell.ssh.idle-timeout = # Number of milliseconds after which unused connections are closed.
management.shell.ssh.key-path= # Path to the SSH server key.
management.shell.ssh.port=2000 # SSH port.
management.shell.telnet.enabled=false # Enable CRaSH telnet support. Enabled by default if the TelnetPlugin is available.
management.shell.telnet.port=5000 # Telnet port.
# TRACING ({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties]) # TRACING ({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties])
management.trace.include=request-headers,response-headers,cookies,errors # Items to be included in the trace. management.trace.include=request-headers,response-headers,cookies,errors # Items to be included in the trace.
......
...@@ -124,7 +124,6 @@ When you're ready to push your Spring Boot application to production, we've got ...@@ -124,7 +124,6 @@ When you're ready to push your Spring Boot application to production, we've got
* *Connection options:* * *Connection options:*
<<production-ready-features.adoc#production-ready-monitoring, HTTP>> | <<production-ready-features.adoc#production-ready-monitoring, HTTP>> |
<<production-ready-features.adoc#production-ready-jmx, JMX>> | <<production-ready-features.adoc#production-ready-jmx, JMX>> |
<<production-ready-features.adoc#production-ready-remote-shell, SSH>>
* *Monitoring:* * *Monitoring:*
<<production-ready-features.adoc#production-ready-metrics, Metrics>> | <<production-ready-features.adoc#production-ready-metrics, Metrics>> |
<<production-ready-features.adoc#production-ready-auditing, Auditing>> | <<production-ready-features.adoc#production-ready-auditing, Auditing>> |
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
-- --
Spring Boot includes a number of additional features to help you monitor and manage your Spring Boot includes a number of additional features to help you monitor and manage your
application when it's pushed to production. You can choose to manage and monitor your application when it's pushed to production. You can choose to manage and monitor your
application using HTTP endpoints, with JMX or even by remote shell (SSH or Telnet). application using HTTP endpoints or with JMX. Auditing, health and metrics gathering can
Auditing, health and metrics gathering can be automatically applied to your application. be automatically applied to your application.
Actuator HTTP endpoints are only available with a Spring MVC-based application. In Actuator HTTP endpoints are only available with a Spring MVC-based application. In
particular, it will not work with Jersey <<howto.adoc#howto-use-actuator-with-jersey, particular, it will not work with Jersey <<howto.adoc#howto-use-actuator-with-jersey,
...@@ -802,148 +802,6 @@ If you are using Jolokia but you don't want Spring Boot to configure it, simply ...@@ -802,148 +802,6 @@ If you are using Jolokia but you don't want Spring Boot to configure it, simply
[[production-ready-remote-shell]]
== Monitoring and management using a remote shell (deprecated)
Spring Boot supports an integrated Java shell called '`CRaSH`'. You can use CRaSH to
`ssh` or `telnet` into your running application. To enable remote shell support, add
the following dependency to your project:
[source,xml,indent=0]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>
----
TIP: If you want to also enable telnet access you will additionally need a dependency
on `org.crsh:crsh.shell.telnet`.
NOTE: CRaSH requires to run with a JDK as it compiles commands on the fly. If a basic
`help` command fails, you are probably running with a JRE.
[[production-ready-connecting-to-the-remote-shell]]
=== Connecting to the remote shell
By default the remote shell will listen for connections on port `2000`. The default user
is `user` and the default password will be randomly generated and displayed in the log
output. If your application is using Spring Security, the shell will use
<<boot-features-security, the same configuration>> by default. If not, a simple
authentication will be applied and you should see a message like this:
[indent=0]
----
Using default password for shell access: ec03e16c-4cf4-49ee-b745-7c8255c1dd7e
----
Linux and OSX users can use `ssh` to connect to the remote shell, Windows users can
download and install http://www.putty.org/[PuTTY].
[indent=0,subs="attributes"]
----
$ ssh -p 2000 user@localhost
user@localhost's password:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v{spring-boot-version}) on myhost
----
Type `help` for a list of commands. Spring Boot provides `metrics`, `beans`, `autoconfig`
and `endpoint` commands.
[[production-ready-remote-shell-credentials]]
==== Remote shell credentials
You can use the `management.shell.auth.simple.user.name` and
`management.shell.auth.simple.user.password` properties to configure custom connection
credentials. It is also possible to use a '`Spring Security`' `AuthenticationManager` to
handle login duties. See the
{dc-spring-boot-actuator}/autoconfigure/CrshAutoConfiguration.{dc-ext}[`CrshAutoConfiguration`]
and {dc-spring-boot-actuator}/autoconfigure/ShellProperties.{dc-ext}[`ShellProperties`]
Javadoc for full details.
[[production-ready-extending-the-remote-shell]]
=== Extending the remote shell
The remote shell can be extended in a number of interesting ways.
[[production-ready-remote-commands]]
==== Remote shell commands
You can write additional shell commands using Groovy or Java (see the CRaSH documentation
for details). By default Spring Boot will search for commands in the following locations:
* `+classpath*:/commands/**+`
* `+classpath*:/crash/commands/**+`
TIP: You can change the search path by settings a `shell.command-path-patterns` property.
NOTE: If you are using an executable archive, any classes that a shell command depends
upon must be packaged in a nested jar rather than directly in the executable jar or war.
Here is a simple '`hello`' command that could be loaded from
`src/main/resources/commands/hello.groovy`
[source,groovy,indent=0]
----
package commands
import org.crsh.cli.Command
import org.crsh.cli.Usage
import org.crsh.command.InvocationContext
class hello {
@Usage("Say Hello")
@Command
def main(InvocationContext context) {
return "Hello"
}
}
----
Spring Boot adds some additional attributes to `InvocationContext` that you can access
from your command:
[cols="2,3"]
|===
| Attribute Name | Description
|`spring.boot.version`
|The version of Spring Boot
|`spring.version`
|The version of the core Spring Framework
|`spring.beanfactory`
|Access to the Spring `BeanFactory`
|`spring.environment`
|Access to the Spring `Environment`
|===
[[production-ready-remote-shell-plugins]]
==== Remote shell plugins
In addition to new commands, it is also possible to extend other CRaSH shell features.
All Spring Beans that extend `org.crsh.plugin.CRaSHPlugin` will be automatically
registered with the shell.
For more information please refer to the http://www.crashub.org/[CRaSH reference
documentation].
[[production-ready-metrics]] [[production-ready-metrics]]
== Metrics == Metrics
Spring Boot Actuator includes a metrics service with '`gauge`' and '`counter`' support. Spring Boot Actuator includes a metrics service with '`gauge`' and '`counter`' support.
......
...@@ -29,7 +29,7 @@ boolean isTechnicalStarter(def starter) { ...@@ -29,7 +29,7 @@ boolean isTechnicalStarter(def starter) {
} }
boolean isProductionStarter(def starter) { boolean isProductionStarter(def starter) {
starter.name in ['spring-boot-starter-actuator', 'spring-boot-starter-remote-shell'] starter.name in ['spring-boot-starter-actuator']
} }
boolean isStarter(def dependencies) { boolean isStarter(def dependencies) {
......
...@@ -2,14 +2,6 @@ ...@@ -2,14 +2,6 @@
#server.port=8080 #server.port=8080
#management.port=8080 #management.port=8080
management.address=127.0.0.1 management.address=127.0.0.1
management.shell.ssh.enabled=true
management.shell.ssh.port=2222
#management.shell.telnet.enabled=false
#management.shell.telnet.port=1111
management.shell.auth.type=spring
#management.shell.auth.type=key
#management.shell.auth.key.path=${user.home}/test/id_rsa.pub.pem
#management.shell.auth.type=simple
endpoints.shutdown.enabled=true endpoints.shutdown.enabled=true
server.tomcat.basedir=target/tomcat server.tomcat.basedir=target/tomcat
......
...@@ -23,10 +23,6 @@ ...@@ -23,10 +23,6 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
......
service.name=Phil service.name=Phil
management.shell.ssh.enabled=true
management.shell.ssh.port=2222
management.shell.auth.type=simple
management.shell.auth.simple.user.password=password
...@@ -35,10 +35,6 @@ ...@@ -35,10 +35,6 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId> <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-remote-shell</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
...@@ -48,20 +44,6 @@ ...@@ -48,20 +44,6 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- <dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.telnet</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency> -->
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
......
...@@ -2,14 +2,6 @@ ...@@ -2,14 +2,6 @@
# logging.level.org.springframework.security=DEBUG # logging.level.org.springframework.security=DEBUG
management.address=127.0.0.1 management.address=127.0.0.1
#management.port=8181 #management.port=8181
management.shell.ssh.enabled=true
management.shell.ssh.port=2222
#management.shell.telnet.enabled=false
#management.shell.telnet.port=1111
management.shell.auth.type=spring
#management.shell.auth.type=key
#management.shell.auth.key.path=${user.home}/test/id_rsa.pub.pem
#management.shell.auth.type=simple
management.info.build.mode=full management.info.build.mode=full
......
...@@ -60,7 +60,6 @@ ...@@ -60,7 +60,6 @@
<module>spring-boot-starter-social-facebook</module> <module>spring-boot-starter-social-facebook</module>
<module>spring-boot-starter-social-twitter</module> <module>spring-boot-starter-social-twitter</module>
<module>spring-boot-starter-social-linkedin</module> <module>spring-boot-starter-social-linkedin</module>
<module>spring-boot-starter-remote-shell</module>
<module>spring-boot-starter-test</module> <module>spring-boot-starter-test</module>
<module>spring-boot-starter-thymeleaf</module> <module>spring-boot-starter-thymeleaf</module>
<module>spring-boot-starter-tomcat</module> <module>spring-boot-starter-tomcat</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-remote-shell</artifactId>
<name>spring-boot-starter-remote-shell (DEPRECATED)</name>
<description>Starter for using the CRaSH remote shell to monitor and manage your
application over SSH. Deprecated since 1.5</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.cli</artifactId>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.ssh</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.connectors.telnet</artifactId>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.embed.spring</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-web</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.plugins.cron</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.plugins.mail</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.crashub</groupId>
<artifactId>crash.shell</artifactId>
<exclusions>
<exclusion>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<executions>
<execution>
<id>duplicate-dependencies</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<ignoredResourcePatterns>
<ignoredResourcePattern>crash/crash.properties</ignoredResourcePattern>
</ignoredResourcePatterns>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.starter.remote.shell;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} to print a deprecation warning about
* the starter.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
@Configuration
@Deprecated
public class RemoteShellStarterDeprecatedWarningAutoConfiguration {
private static final Log logger = LogFactory
.getLog(RemoteShellStarterDeprecatedWarningAutoConfiguration.class);
@PostConstruct
public void logWarning() {
logger.warn("spring-boot-starter-remote-shell is deprecated since Spring Boot " +
"1.5 and will be removed in Spring Boot 2.0");
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.starter.remote.shell.RemoteShellStarterDeprecatedWarningAutoConfiguration
\ No newline at end of file
provides: crash.cli,crash.shell,crash.plugins.cron,crash.embed.spring,crash.connectors.ssh
\ No newline at end of file
package commands
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint
class autoconfig {
@Usage("Display auto configuration report from ApplicationContext")
@Command
void main(InvocationContext context) {
context.attributes['spring.beanfactory'].getBeansOfType(AutoConfigurationReportEndpoint.class).each { name, endpoint ->
def report = endpoint.invoke()
out.println "Endpoint: " + name + "\n\nPositive Matches:\n================\n"
report.positiveMatches.each { key, list ->
out.println key + ":"
list.each { mandc ->
out.println " " + mandc.condition + ": " + mandc.message
}
}
out.println "\nNegative Matches\n================\n"
report.negativeMatches.each { key, list ->
out.println key + ":"
list.each { mandc ->
out.println " " + mandc.condition + ": " + mandc.message
}
}
}
}
}
\ No newline at end of file
package commands
import org.springframework.boot.actuate.endpoint.BeansEndpoint
class beans {
@Usage("Display beans in ApplicationContext")
@Command
def main(InvocationContext context) {
def result = [:]
context.attributes['spring.beanfactory'].getBeansOfType(BeansEndpoint.class).each { name, endpoint ->
result.put(name, endpoint.invoke())
}
result.size() == 1 ? result.values()[0] : result
}
}
\ No newline at end of file
package commands
import org.springframework.boot.actuate.endpoint.Endpoint
import org.springframework.boot.actuate.endpoint.jmx.*
@Usage("Invoke actuator endpoints")
class endpoint {
@Usage("List all available and enabled actuator endpoints")
@Command
def list(InvocationContext context) {
context.attributes['spring.beanfactory'].getBeansOfType(Endpoint.class).each { name, endpoint ->
if (endpoint.isEnabled()) {
out.println name
}
}
""
}
@Usage("Invoke provided actuator endpoint")
@Command
def invoke(InvocationContext context, @Usage("The name of the Endpoint to invoke") @Required @Argument String name) {
// Don't require passed argument to end with 'Endpoint'
if (!name.endsWith("Endpoint")) {
name = name + "Endpoint"
}
context.attributes['spring.beanfactory'].getBeansOfType(Endpoint.class).each { n, endpoint ->
if (n.equals(name) && endpoint.isEnabled()) {
EndpointMBean mbean = context.attributes['spring.beanfactory'].getBean(EndpointMBeanExporter.class).getEndpointMBean(name, endpoint)
if (mbean instanceof DataEndpointMBean) {
out.println mbean.getData()
}
else {
out.println mbean.endpoint.invoke()
}
}
}
""
}
}
welcome = { ->
def environment = crash.context.attributes['spring.environment']
def propertyResolver = new org.springframework.boot.bind.RelaxedPropertyResolver(environment, "spring.main.");
def beanFactory = crash.context.attributes['spring.beanfactory']
if (!propertyResolver.getProperty("show-banner", Boolean.class, Boolean.TRUE)) {
return ""
}
// Try to print using the banner interface
if (beanFactory != null) {
try {
def banner = beanFactory.getBean("springBootBanner")
def out = new java.io.ByteArrayOutputStream()
banner.printBanner(environment, null, new java.io.PrintStream(out))
return out.toString()
} catch (Exception ex) {
// Ignore
}
}
// Resolve hostname
def hostName;
try {
hostName = java.net.InetAddress.getLocalHost().getHostName();
}
catch (java.net.UnknownHostException ignore) {
hostName = "localhost";
}
// Get Spring Boot version from context
def version = crash.context.attributes.get("spring.boot.version")
return """\
. ____ _ __ _ _
/\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
\\\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v$version) on $hostName
""";
}
prompt = { ->
return "> ";
}
package commands
import org.crsh.text.ui.UIBuilder
import org.springframework.boot.actuate.endpoint.MetricsEndpoint
class metrics {
@Usage("Display metrics provided by Spring Boot")
@Command
public void main(InvocationContext context) {
context.takeAlternateBuffer();
try {
while (!Thread.interrupted()) {
out.cls()
out.show(new UIBuilder().table(columns:[1]) {
header {
table(columns:[1], separator: dashed) {
header(bold: true, fg: black, bg: white) { label("metrics"); }
}
}
row {
table(columns:[1, 1]) {
header(bold: true, fg: black, bg: white) {
label("NAME")
label("VALUE")
}
context.attributes['spring.beanfactory'].getBeansOfType(MetricsEndpoint.class).each { name, metrics ->
metrics.invoke().each { k, v ->
row {
label(k)
label(v)
}
}
}
}
}
}
);
out.flush();
Thread.sleep(1000);
}
}
finally {
context.releaseAlternateBuffer();
}
}
}
\ No newline at end of file
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -101,12 +101,8 @@ class DefaultLogbackConfiguration { ...@@ -101,12 +101,8 @@ class DefaultLogbackConfiguration {
config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN); config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);
config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN); config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN);
config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN); config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN);
config.logger("org.crsh.plugin", Level.WARN);
config.logger("org.crsh.ssh", Level.WARN);
config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR); config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR);
config.logger("org.hibernate.validator.internal.util.Version", Level.WARN); config.logger("org.hibernate.validator.internal.util.Version", Level.WARN);
config.logger("org.springframework.boot.actuate.autoconfigure."
+ "CrshAutoConfiguration", Level.WARN);
config.logger("org.springframework.boot.actuate.endpoint.jmx", null, false, config.logger("org.springframework.boot.actuate.endpoint.jmx", null, false,
debugRemapAppender); debugRemapAppender);
config.logger("org.thymeleaf", null, false, debugRemapAppender); config.logger("org.thymeleaf", null, false, debugRemapAppender);
......
...@@ -13,7 +13,6 @@ java.util.logging.ConsoleHandler.level = ALL ...@@ -13,7 +13,6 @@ java.util.logging.ConsoleHandler.level = ALL
org.hibernate.validator.internal.util.Version.level = WARNING org.hibernate.validator.internal.util.Version.level = WARNING
org.apache.coyote.http11.Http11NioProtocol.level = WARNING org.apache.coyote.http11.Http11NioProtocol.level = WARNING
org.crsh.plugin.level = WARNING
org.apache.tomcat.util.net.NioSelectorPool.level = WARNING org.apache.tomcat.util.net.NioSelectorPool.level = WARNING
org.apache.catalina.startup.DigesterFactory.level = SEVERE org.apache.catalina.startup.DigesterFactory.level = SEVERE
org.apache.catalina.util.LifecycleBase.level = SEVERE org.apache.catalina.util.LifecycleBase.level = SEVERE
......
...@@ -6,7 +6,6 @@ java.util.logging.ConsoleHandler.level = ALL ...@@ -6,7 +6,6 @@ java.util.logging.ConsoleHandler.level = ALL
org.hibernate.validator.internal.util.Version.level = WARNING org.hibernate.validator.internal.util.Version.level = WARNING
org.apache.coyote.http11.Http11NioProtocol.level = WARNING org.apache.coyote.http11.Http11NioProtocol.level = WARNING
org.crsh.plugin.level = WARNING
org.apache.tomcat.util.net.NioSelectorPool.level = WARNING org.apache.tomcat.util.net.NioSelectorPool.level = WARNING
org.apache.catalina.startup.DigesterFactory.level = SEVERE org.apache.catalina.startup.DigesterFactory.level = SEVERE
org.apache.catalina.util.LifecycleBase.level = SEVERE org.apache.catalina.util.LifecycleBase.level = SEVERE
......
...@@ -25,11 +25,8 @@ ...@@ -25,11 +25,8 @@
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" /> <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
<logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/> <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" /> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
<Logger name="org.crsh.plugin" level="warn" />
<logger name="org.crsh.ssh" level="warn"/>
<Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" /> <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
<Logger name="org.hibernate.validator.internal.util.Version" level="warn" /> <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
<logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
<logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/> <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
<logger name="org.thymeleaf" level="warn"/> <logger name="org.thymeleaf" level="warn"/>
<Root level="info"> <Root level="info">
......
...@@ -17,11 +17,8 @@ ...@@ -17,11 +17,8 @@
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" /> <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
<logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/> <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" /> <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
<Logger name="org.crsh.plugin" level="warn" />
<logger name="org.crsh.ssh" level="warn"/>
<Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" /> <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
<Logger name="org.hibernate.validator.internal.util.Version" level="warn" /> <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
<logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
<logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/> <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
<logger name="org.thymeleaf" level="warn"/> <logger name="org.thymeleaf" level="warn"/>
<Root level="info"> <Root level="info">
......
...@@ -21,11 +21,8 @@ initialization performed by Boot ...@@ -21,11 +21,8 @@ initialization performed by Boot
<logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/> <logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
<logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/> <logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
<logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/> <logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
<logger name="org.crsh.plugin" level="WARN"/>
<logger name="org.crsh.ssh" level="WARN"/>
<logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/> <logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
<logger name="org.hibernate.validator.internal.util.Version" level="WARN"/> <logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
<logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="WARN"/>
<logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false"> <logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false">
<appender-ref ref="DEBUG_LEVEL_REMAPPER"/> <appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
</logger> </logger>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment