QoS Support anonymous command allow list (#12082)

* QoS Support anonymous command allow list

* Fix test
This commit is contained in:
Albumen Kevin 2023-04-19 10:13:09 +08:00 committed by GitHub
parent 9aa6a9e08c
commit 1b838281a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 2 deletions

View File

@ -34,6 +34,8 @@ public interface QosConstants {
String ANONYMOUS_ACCESS_PERMISSION_LEVEL = "qos.anonymous.access.permission.level";
String ANONYMOUS_ACCESS_ALLOW_COMMANDS = "qos.anonymous.access.allow.commands";
String QOS_ENABLE_COMPATIBLE = "qos-enable";
String QOS_HOST_COMPATIBLE = "qos-host";

View File

@ -55,6 +55,7 @@ import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_ALLOW_COMMANDS;
import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL;
import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL_COMPATIBLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
@ -170,6 +171,11 @@ public class ApplicationConfig extends AbstractConfig {
*/
private String qosAnonymousAccessPermissionLevel;
/**
* the anonymous(any foreign ip) allow commands, default is empty, can not access any cmd
*/
private String qosAnonymousAllowCommands;
/**
* Customized parameters
*/
@ -472,6 +478,15 @@ public class ApplicationConfig extends AbstractConfig {
this.qosAnonymousAccessPermissionLevel = qosAnonymousAccessPermissionLevel;
}
@Parameter(key = ANONYMOUS_ACCESS_ALLOW_COMMANDS)
public String getQosAnonymousAllowCommands() {
return qosAnonymousAllowCommands;
}
public void setQosAnonymousAllowCommands(String qosAnonymousAllowCommands) {
this.qosAnonymousAllowCommands = qosAnonymousAllowCommands;
}
/**
* The format is the same as the springboot, including: getQosEnableCompatible(), getQosPortCompatible(), getQosAcceptForeignIpCompatible().
*

View File

@ -547,6 +547,12 @@
<![CDATA[ Anonymous (any foreign ip) access level, the default is NONE, can not access any cmd. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="qos-anonymous-allow-commands" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ Anonymous(any foreign ip) allow commands, default is empty, can not access any cmd. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="moduleType">

View File

@ -38,6 +38,9 @@ public class QosConfiguration {
// the default value is Cmd.PermissionLevel.PUBLIC, can only access PUBLIC level cmd
private PermissionLevel anonymousAccessPermissionLevel = PermissionLevel.PUBLIC;
// the allow commands for anonymous access, the delimiter is colon(,)
private String anonymousAllowCommands;
private QosConfiguration() {
}
@ -46,6 +49,7 @@ public class QosConfiguration {
this.acceptForeignIp = builder.isAcceptForeignIp();
this.acceptForeignIpWhitelist = builder.getAcceptForeignIpWhitelist();
this.anonymousAccessPermissionLevel = builder.getAnonymousAccessPermissionLevel();
this.anonymousAllowCommands = builder.getAnonymousAllowCommands();
buildPredicate();
}
@ -93,6 +97,10 @@ public class QosConfiguration {
return acceptForeignIp;
}
public String getAnonymousAllowCommands() {
return anonymousAllowCommands;
}
public static Builder builder() {
return new Builder();
}
@ -103,6 +111,7 @@ public class QosConfiguration {
private boolean acceptForeignIp;
private String acceptForeignIpWhitelist;
private PermissionLevel anonymousAccessPermissionLevel = PermissionLevel.PUBLIC;
private String anonymousAllowCommands;
private Builder() {
}
@ -127,6 +136,11 @@ public class QosConfiguration {
return this;
}
public Builder anonymousAllowCommands(String anonymousAllowCommands) {
this.anonymousAllowCommands = anonymousAllowCommands;
return this;
}
public QosConfiguration build() {
return new QosConfiguration(this);
}
@ -146,5 +160,9 @@ public class QosConfiguration {
public PermissionLevel getAnonymousAccessPermissionLevel() {
return anonymousAccessPermissionLevel;
}
public String getAnonymousAllowCommands() {
return anonymousAllowCommands;
}
}
}

View File

@ -16,13 +16,16 @@
*/
package org.apache.dubbo.qos.permission;
import io.netty.channel.Channel;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.qos.api.CommandContext;
import org.apache.dubbo.qos.api.PermissionLevel;
import org.apache.dubbo.qos.api.QosConfiguration;
import io.netty.channel.Channel;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Optional;
public class DefaultAnonymousAccessPermissionChecker implements PermissionChecker {
@ -37,6 +40,15 @@ public class DefaultAnonymousAccessPermissionChecker implements PermissionChecke
.orElse(null);
QosConfiguration qosConfiguration = commandContext.getQosConfiguration();
String anonymousAllowCommands = qosConfiguration.getAnonymousAllowCommands();
if (StringUtils.isNotEmpty(anonymousAllowCommands) &&
Arrays.stream(anonymousAllowCommands.split(","))
.filter(StringUtils::isNotEmpty)
.map(String::trim)
.anyMatch(cmd -> cmd.equals(commandContext.getCommandName()))) {
return true;
}
PermissionLevel currentLevel = qosConfiguration.getAnonymousAccessPermissionLevel();
// Local has private permission

View File

@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.QOS_FAILED_START_SERVER;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST;
import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_ALLOW_COMMANDS;
import static org.apache.dubbo.common.constants.QosConstants.ANONYMOUS_ACCESS_PERMISSION_LEVEL;
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
@ -119,6 +120,7 @@ public class QosProtocolWrapper implements Protocol, ScopeModelAware {
boolean acceptForeignIp = Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP, "false"));
String acceptForeignIpWhitelist = url.getParameter(ACCEPT_FOREIGN_IP_WHITELIST, StringUtils.EMPTY_STRING);
String anonymousAccessPermissionLevel = url.getParameter(ANONYMOUS_ACCESS_PERMISSION_LEVEL, PermissionLevel.PUBLIC.name());
String anonymousAllowCommands = url.getParameter(ANONYMOUS_ACCESS_ALLOW_COMMANDS, StringUtils.EMPTY_STRING);
Server server = frameworkModel.getBeanFactory().getBean(Server.class);
if (server.isStarted()) {
@ -130,6 +132,7 @@ public class QosProtocolWrapper implements Protocol, ScopeModelAware {
server.setAcceptForeignIp(acceptForeignIp);
server.setAcceptForeignIpWhitelist(acceptForeignIpWhitelist);
server.setAnonymousAccessPermissionLevel(anonymousAccessPermissionLevel);
server.setAnonymousAllowCommands(anonymousAllowCommands);
server.start();
} catch (Throwable throwable) {

View File

@ -58,6 +58,8 @@ public class Server {
private String anonymousAccessPermissionLevel = PermissionLevel.NONE.name();
private String anonymousAllowCommands = StringUtils.EMPTY_STRING;
private EventLoopGroup boss;
private EventLoopGroup worker;
@ -108,6 +110,7 @@ public class Server {
.acceptForeignIp(acceptForeignIp)
.acceptForeignIpWhitelist(acceptForeignIpWhitelist)
.anonymousAccessPermissionLevel(anonymousAccessPermissionLevel)
.anonymousAllowCommands(anonymousAllowCommands)
.build()
));
}
@ -167,6 +170,10 @@ public class Server {
this.anonymousAccessPermissionLevel = anonymousAccessPermissionLevel;
}
public void setAnonymousAllowCommands(String anonymousAllowCommands) {
this.anonymousAllowCommands = anonymousAllowCommands;
}
public String getWelcome() {
return welcome;
}

View File

@ -16,10 +16,11 @@
*/
package org.apache.dubbo.qos.permission;
import io.netty.channel.Channel;
import org.apache.dubbo.qos.api.CommandContext;
import org.apache.dubbo.qos.api.PermissionLevel;
import org.apache.dubbo.qos.api.QosConfiguration;
import io.netty.channel.Channel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@ -79,5 +80,26 @@ class DefaultAnonymousAccessPermissionCheckerTest {
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED));
Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE));
Mockito.when(qosConfiguration.getAcceptForeignIpWhitelistPredicate()).thenReturn(ip -> false);
Mockito.when(qosConfiguration.getAnonymousAllowCommands()).thenReturn("test1,test2");
Mockito.when(commandContext.getCommandName()).thenReturn("test1");
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PRIVATE));
Mockito.when(commandContext.getCommandName()).thenReturn("test2");
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PUBLIC));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PROTECTED));
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.PRIVATE));
Mockito.when(commandContext.getCommandName()).thenReturn("test");
Assertions.assertTrue(checker.access(commandContext, PermissionLevel.NONE));
Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PUBLIC));
Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PROTECTED));
Assertions.assertFalse(checker.access(commandContext, PermissionLevel.PRIVATE));
}
}