Merge dcfd16a1b5 into 4b79a5facc
This commit is contained in:
commit
05b9890426
|
|
@ -147,6 +147,10 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
|
|||
this(url, null, isUrlFromRegistry);
|
||||
}
|
||||
|
||||
public AbstractDirectory(URL url, RouterChain<T> routerChain, boolean isUrlFromRegistry, URL consumerUrl) {
|
||||
this(addConsumerUrl(url, consumerUrl), null, isUrlFromRegistry);
|
||||
}
|
||||
|
||||
public AbstractDirectory(URL url, RouterChain<T> routerChain, boolean isUrlFromRegistry) {
|
||||
if (url == null) {
|
||||
throw new IllegalArgumentException("url == null");
|
||||
|
|
@ -252,6 +256,13 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
|
|||
}
|
||||
}
|
||||
|
||||
private static URL addConsumerUrl(URL url, URL consumerUrl) {
|
||||
Map<String, String> referMap = new HashMap<>();
|
||||
referMap.put(CONSUMER_URL_KEY, consumerUrl.toString());
|
||||
url = url.putAttribute(REFER_KEY, referMap);
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
|
|
|
|||
|
|
@ -19,5 +19,6 @@ package org.apache.dubbo.common.ssl;
|
|||
public enum AuthPolicy {
|
||||
NONE,
|
||||
SERVER_AUTH,
|
||||
CLIENT_AUTH
|
||||
CLIENT_AUTH_STRICT,
|
||||
CLIENT_AUTH_PERMISSIVE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class SSLConfigCertProvider implements CertProvider {
|
|||
? IOUtils.toByteArray(sslConfig.getServerTrustCertCollectionPathStream())
|
||||
: null,
|
||||
sslConfig.getServerKeyPassword(),
|
||||
AuthPolicy.CLIENT_AUTH);
|
||||
AuthPolicy.CLIENT_AUTH_STRICT);
|
||||
} catch (IOException e) {
|
||||
logger.warn(
|
||||
LoggerCodeConstants.CONFIG_SSL_PATH_LOAD_FAILED,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
*/
|
||||
package org.apache.dubbo.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_COMPATIBLE;
|
||||
import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP_WHITELIST_COMPATIBLE;
|
||||
import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE_COMPATIBLE;
|
||||
|
|
@ -156,4 +160,15 @@ public interface Constants {
|
|||
String DEFAULT_NATIVE_PROXY = "jdk";
|
||||
|
||||
String DEFAULT_APP_NAME = "DEFAULT_DUBBO_APP";
|
||||
|
||||
String MESH_KEY = "mesh";
|
||||
String SECURITY_KEY = "security";
|
||||
String XDS_CLUSTER_KEY = "cluster";
|
||||
String XDS_CLUSTER_VALUE = "xds";
|
||||
|
||||
Set<String> SUPPORT_MESH_TYPE = new HashSet<String>() {
|
||||
{
|
||||
addAll(Arrays.asList("istio"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,10 +251,5 @@
|
|||
<artifactId>resteasy-jackson-provider</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol;
|
|||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.apache.dubbo.rpc.stub.StubSuppliers;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
import org.apache.dubbo.xds.PilotExchanger;
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -477,7 +476,8 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
|
|||
private T createProxy(Map<String, String> referenceParameters) {
|
||||
urls.clear();
|
||||
|
||||
meshModeHandleUrl(referenceParameters);
|
||||
// TODO: Maybe not need this logic.
|
||||
// meshModeHandleUrl(referenceParameters);
|
||||
|
||||
if (StringUtils.isNotEmpty(url)) {
|
||||
// user specified URL, could be peer-to-peer address, or register center's address.
|
||||
|
|
@ -631,6 +631,7 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
|
|||
if (isInjvm() != null && isInjvm()) {
|
||||
u = u.addParameter(LOCAL_PROTOCOL, true);
|
||||
}
|
||||
ConfigValidationUtils.loadMeshConfig(u, referenceParameters);
|
||||
urls.add(u.putAttribute(REFER_KEY, referenceParameters));
|
||||
}
|
||||
}
|
||||
|
|
@ -656,16 +657,6 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
|
|||
private void createInvoker() {
|
||||
if (urls.size() == 1) {
|
||||
URL curUrl = urls.get(0);
|
||||
|
||||
if (curUrl.getParameter("registry", "null").startsWith("xds")) {
|
||||
// TODO: The PilotExchanger requests xds resources asynchronously,
|
||||
// and the xdsDirectory call filter chain may have an exception with invoker null,
|
||||
// which needs to be synchronized later.
|
||||
// move to deployer
|
||||
curUrl = curUrl.addParameter("xds", true);
|
||||
PilotExchanger.initialize(curUrl);
|
||||
}
|
||||
|
||||
invoker = protocolSPI.refer(interfaceClass, curUrl);
|
||||
// registry url, mesh-enable and unloadClusterRelated is true, not need Cluster.
|
||||
if (!UrlUtils.isRegistry(curUrl) && !curUrl.getParameter(UNLOAD_CLUSTER_RELATED, false)) {
|
||||
|
|
|
|||
|
|
@ -607,6 +607,10 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
|
|||
ProtocolConfig protocolConfig, List<URL> registryURLs, RegisterTypeEnum registerType) {
|
||||
Map<String, String> map = buildAttributes(protocolConfig);
|
||||
|
||||
for (URL u : registryURLs) {
|
||||
ConfigValidationUtils.loadMeshConfig(u, map);
|
||||
}
|
||||
|
||||
// remove null key and null value
|
||||
map.keySet().removeIf(key -> StringUtils.isEmpty(key) || StringUtils.isEmpty(map.get(key)));
|
||||
// init serviceMetadata attachments
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.apache.dubbo.config.AbstractConfig;
|
|||
import org.apache.dubbo.config.AbstractInterfaceConfig;
|
||||
import org.apache.dubbo.config.ApplicationConfig;
|
||||
import org.apache.dubbo.config.ConfigCenterConfig;
|
||||
import org.apache.dubbo.config.Constants;
|
||||
import org.apache.dubbo.config.ConsumerConfig;
|
||||
import org.apache.dubbo.config.MetadataReportConfig;
|
||||
import org.apache.dubbo.config.MethodConfig;
|
||||
|
|
@ -126,6 +127,8 @@ import static org.apache.dubbo.config.Constants.ORGANIZATION;
|
|||
import static org.apache.dubbo.config.Constants.OWNER;
|
||||
import static org.apache.dubbo.config.Constants.REGISTER_KEY;
|
||||
import static org.apache.dubbo.config.Constants.STATUS_KEY;
|
||||
import static org.apache.dubbo.config.Constants.XDS_CLUSTER_KEY;
|
||||
import static org.apache.dubbo.config.Constants.XDS_CLUSTER_VALUE;
|
||||
import static org.apache.dubbo.monitor.Constants.LOGSTAT_PROTOCOL;
|
||||
import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
|
||||
import static org.apache.dubbo.registry.Constants.SUBSCRIBE_KEY;
|
||||
|
|
@ -219,7 +222,6 @@ public class ConfigValidationUtils {
|
|||
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
|
||||
}
|
||||
List<URL> urls = UrlUtils.parseURLs(address, map);
|
||||
|
||||
for (URL url : urls) {
|
||||
url = URLBuilder.from(url)
|
||||
.addParameter(REGISTRY_KEY, url.getProtocol())
|
||||
|
|
@ -240,6 +242,17 @@ public class ConfigValidationUtils {
|
|||
return genCompatibleRegistries(interfaceConfig.getScopeModel(), registryList, provider);
|
||||
}
|
||||
|
||||
public static void loadMeshConfig(URL url, Map<String, String> map) {
|
||||
String registry = url.getParameter(REGISTRY_KEY);
|
||||
if (StringUtils.isNotEmpty(registry) && Constants.SUPPORT_MESH_TYPE.contains(registry)) {
|
||||
map.put(Constants.MESH_KEY, registry);
|
||||
map.put(XDS_CLUSTER_KEY, XDS_CLUSTER_VALUE);
|
||||
if (url.hasParameter(Constants.SECURITY_KEY)) {
|
||||
map.put(Constants.SECURITY_KEY, url.getParameter(Constants.SECURITY_KEY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<URL> genCompatibleRegistries(ScopeModel scopeModel, List<URL> registryList, boolean provider) {
|
||||
List<URL> result = new ArrayList<>(registryList.size());
|
||||
registryList.forEach(registryURL -> {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@
|
|||
<artifactId>dubbo-serialization-fastjson2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-plugin-cluster-mergeable</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public interface RestDemoService {
|
|||
Boolean testBody2(Boolean b);
|
||||
|
||||
@POST
|
||||
@Path("/testBody4")
|
||||
@Path("/testBody3")
|
||||
@Consumes({MediaType.TEXT_PLAIN})
|
||||
TestPO testBody2(TestPO b);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
<curator5_version>5.1.0</curator5_version>
|
||||
<zookeeper_version>3.8.4</zookeeper_version>
|
||||
<zookeeper_version>3.8.3</zookeeper_version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -157,7 +157,7 @@
|
|||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<version>1.4.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<version>0.10.1</version>
|
||||
<version>0.10.0</version>
|
||||
<configuration>
|
||||
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
|
||||
<metadataRepository>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
<curator5_version>5.1.0</curator5_version>
|
||||
<zookeeper_version>3.8.4</zookeeper_version>
|
||||
<zookeeper_version>3.8.3</zookeeper_version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -157,7 +157,7 @@
|
|||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<version>1.4.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<version>0.10.1</version>
|
||||
<version>0.10.0</version>
|
||||
<configuration>
|
||||
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
|
||||
<metadataRepository>
|
||||
|
|
|
|||
|
|
@ -37,12 +37,6 @@
|
|||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-zookeeper</artifactId>
|
||||
|
|
@ -107,12 +101,6 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -32,4 +32,7 @@ dubbo:
|
|||
metadata-report:
|
||||
address: zookeeper://127.0.0.1:2181
|
||||
|
||||
logging:
|
||||
pattern:
|
||||
level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.springboot.demo;
|
||||
|
||||
public interface DemoService2 {
|
||||
String sayHello(String name);
|
||||
}
|
||||
|
|
@ -31,6 +31,18 @@
|
|||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-triple</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-spring-boot-interface</artifactId>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import org.apache.dubbo.springboot.demo.DemoService;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@DubboService
|
||||
@DubboService(parameters = {"security", "mTLS,sa_jwt"})
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.springboot.demo.provider;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.apache.dubbo.springboot.demo.DemoService2;
|
||||
import org.apache.dubbo.xds.istio.IstioConstant;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDubbo(scanBasePackages = {"org.apache.dubbo.springboot.demo.provider"})
|
||||
public class FooApplication {
|
||||
|
||||
// @DubboReference(cluster = "xds", providedBy = "httpbin")
|
||||
@DubboReference(
|
||||
lazy = true,
|
||||
parameters = {"security", "mTLS,sa_jwt"})
|
||||
private DemoService2 demoService;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.setProperty(IstioConstant.WORKLOAD_NAMESPACE_KEY, "foo");
|
||||
IstioConstant.KUBERNETES_SA_PATH = "/Users/nameles/Desktop/test_secrets/kubernetes.io/serviceaccount/token_foo";
|
||||
System.setProperty(IstioConstant.SERVICE_NAME_KEY, "httpbin");
|
||||
|
||||
System.setProperty("NAMESPACE", "foo");
|
||||
System.setProperty("SERVICE_NAME", "httpbin");
|
||||
System.setProperty("API_SERVER_PATH", "https://127.0.0.1:6443");
|
||||
System.setProperty("SA_CA_PATH", "/Users/nameles/Desktop/test_secrets/kubernetes.io/serviceaccount/ca.crt");
|
||||
System.setProperty(
|
||||
"SA_TOKEN_PATH", "/Users/nameles/Desktop/test_secrets/kubernetes.io/serviceaccount/token_foo");
|
||||
|
||||
ConfigurableApplicationContext context = SpringApplication.run(FooApplication.class, args);
|
||||
FooApplication application = context.getBean(FooApplication.class);
|
||||
application.sayHello();
|
||||
new CountDownLatch(1).await();
|
||||
}
|
||||
|
||||
public void sayHello() throws InterruptedException {
|
||||
new Scanner(System.in).nextLine();
|
||||
while (true) {
|
||||
Thread.sleep(2000);
|
||||
System.out.println(demoService.sayHello("hello from foo"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,22 +22,9 @@ dubbo:
|
|||
application:
|
||||
name: ${spring.application.name}
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
name: tri
|
||||
port: 12346
|
||||
host: 192.168.0.103
|
||||
registry:
|
||||
id: zk-registry
|
||||
address: zookeeper://127.0.0.1:2181
|
||||
config-center:
|
||||
address: zookeeper://127.0.0.1:2181
|
||||
metadata-report:
|
||||
address: zookeeper://127.0.0.1:2181
|
||||
metrics:
|
||||
protocol: prometheus
|
||||
enable-jvm: true
|
||||
enable-registry: true
|
||||
aggregation:
|
||||
enabled: true
|
||||
prometheus:
|
||||
exporter:
|
||||
enabled: true
|
||||
enable-metadata: true
|
||||
id: isitod
|
||||
address: istio://istiod.istio-system.svc:15012?security=mTLS
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
<spring-boot.version>2.7.18</spring-boot.version>
|
||||
<spring-boot-maven-plugin.version>2.7.18</spring-boot-maven-plugin.version>
|
||||
<micrometer-core.version>1.12.4</micrometer-core.version>
|
||||
<micrometer-core.version>1.12.2</micrometer-core.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You 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.
|
||||
|
||||
FROM openjdk:8-jdk
|
||||
ADD ./target/dubbo-demo-xds-consumer-3.3.0-beta.2-SNAPSHOT.jar dubbo-demo-xds-consumer-3.3.0-beta.2-SNAPSHOT.jar
|
||||
CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-demo-xds-consumer-3.3.0-beta.2-SNAPSHOT.jar
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<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.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-xds</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-demo-xds-consumer</artifactId>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-triple</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-xds-interface</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-configcenter-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-metadata-report-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-config-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-remoting-netty4</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-hessian2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-fastjson2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Observability -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-tracing-otel-zipkin-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-qos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.demo.consumer;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.apache.dubbo.xds.demo.DemoService;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@SpringBootApplication
|
||||
@Service
|
||||
@EnableDubbo
|
||||
public class XdsConsumerApplication {
|
||||
@DubboReference(providedBy = "dubbo-demo-xds-provider")
|
||||
private DemoService demoService;
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// System.setProperty(IstioConstant.WORKLOAD_NAMESPACE_KEY, "dubbo-demo");
|
||||
// // System.setProperty("API_SERVER_PATH", "https://127.0.0.1:6443");
|
||||
// System.setProperty("SA_CA_PATH", "/Users/smzdm/hjf/xds/resources/ca.crt");
|
||||
// System.setProperty("SA_TOKEN_PATH", "/Users/smzdm/hjf/xds/resources/token");
|
||||
// System.setProperty("NAMESPACE", "dubbo-demo");
|
||||
// IstioConstant.KUBERNETES_SA_PATH = "/Users/smzdm/hjf/xds/resources/token";
|
||||
// System.setProperty(IstioConstant.PILOT_CERT_PROVIDER_KEY, "istiod");
|
||||
ConfigurableApplicationContext context = SpringApplication.run(XdsConsumerApplication.class, args);
|
||||
XdsConsumerApplication application = context.getBean(XdsConsumerApplication.class);
|
||||
while (true) {
|
||||
try {
|
||||
String result = application.doSayHello("world");
|
||||
System.out.println("result: " + result);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
public String doSayHello(String name) {
|
||||
return demoService.sayHello(name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: dubbo-demo-xds-consumer
|
||||
|
||||
dubbo:
|
||||
application:
|
||||
name: ${spring.application.name}
|
||||
# qos-enable: false
|
||||
protocol:
|
||||
name: tri
|
||||
port: 50051
|
||||
registry:
|
||||
address: istio://istiod.istio-system.svc:15012?security=mTLS # istio://istiod.istio-system.svc:15012
|
||||
# config-center:
|
||||
# address: zookeeper://127.0.0.1:2181
|
||||
# metadata-report:
|
||||
# address: zookeeper://127.0.0.1:2181
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<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.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-xds</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-demo-xds-interface</artifactId>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.demo;
|
||||
|
||||
public interface DemoService {
|
||||
|
||||
String sayHello(String name);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You 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.
|
||||
|
||||
FROM openjdk:8-jdk
|
||||
ADD ./target/dubbo-demo-xds-provider-3.3.0-beta.2-SNAPSHOT.jar dubbo-demo-xds-provider-3.3.0-beta.2-SNAPSHOT.jar
|
||||
CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=31001 /dubbo-demo-xds-provider-3.3.0-beta.2-SNAPSHOT.jar
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<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.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-xds</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-demo-xds-provider</artifactId>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-triple</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-xds</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo-xds-interface</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-configcenter-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-metadata-report-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-config-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-remoting-netty4</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-hessian2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-fastjson2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Observability -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-tracing-otel-zipkin-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-qos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.demo.provider;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.xds.demo.DemoService;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@DubboService
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
|
||||
|
||||
@Override
|
||||
public String sayHello(String name) {
|
||||
logger.info("Hello " + name + ", request from consumer: "
|
||||
+ RpcContext.getContext().getRemoteAddress());
|
||||
return "hello" + name;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.springboot.demo.provider;
|
||||
package org.apache.dubbo.xds.demo.provider;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
|
||||
|
|
@ -24,10 +24,11 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDubbo(scanBasePackages = {"org.apache.dubbo.springboot.demo.provider"})
|
||||
public class ProviderApplication {
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(ProviderApplication.class, args);
|
||||
@EnableDubbo(scanBasePackages = {"org.apache.dubbo.xds.demo.provider"})
|
||||
public class XdsProviderApplication {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// System.setProperty(IstioConstant.PILOT_CERT_PROVIDER_KEY, "istiod");
|
||||
SpringApplication.run(XdsProviderApplication.class, args);
|
||||
System.out.println("dubbo service started");
|
||||
new CountDownLatch(1).await();
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: dubbo-demo-xds-provider
|
||||
|
||||
dubbo:
|
||||
application:
|
||||
name: ${spring.application.name}
|
||||
# qos-enable: false
|
||||
protocol:
|
||||
name: tri
|
||||
port: 50051
|
||||
registry:
|
||||
address: istio://istiod.istio-system.svc:15012?security=mTLS # istio://istiod.istio-system.svc:15012
|
||||
# config-center:
|
||||
# address: zookeeper://127.0.0.1:2181
|
||||
# metadata-report:
|
||||
# address: zookeeper://127.0.0.1:2181
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You 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.
|
||||
-->
|
||||
<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.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-demo</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dubbo-demo-xds</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>dubbo-demo-xds-interface</module>
|
||||
<module>dubbo-demo-xds-consumer</module>
|
||||
<module>dubbo-demo-xds-provider</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>true</skip_maven_deploy>
|
||||
<spring-boot.version>2.7.18</spring-boot.version>
|
||||
<spring-boot-maven-plugin.version>2.7.18</spring-boot-maven-plugin.version>
|
||||
<micrometer-core.version>1.12.4</micrometer-core.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-triple</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<version>${micrometer-core.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
<module>dubbo-demo-triple</module>
|
||||
<module>dubbo-demo-native</module>
|
||||
<module>dubbo-demo-spring-boot</module>
|
||||
<module>dubbo-demo-xds</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
|
|
|||
|
|
@ -1032,6 +1032,46 @@
|
|||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.bootstrap.XdsCertificateSigner</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.listener.LdsListener</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.listener.CdsListener</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.api.CertSource</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.api.RequestAuthorizer</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.api.ServiceIdentitySource</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.api.TrustSource</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.authz.rule.source.RuleProvider</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.authz.rule.source.RuleFactory</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.xds.security.authz.resolver.CredentialResolver</resource>
|
||||
</transformer>
|
||||
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.api.ChannelContextListener</resource>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
class CertDeployerListenerTest {
|
||||
class CertDeployerLdsListenerTest {
|
||||
@Test
|
||||
void testEmpty1() {
|
||||
AtomicReference<DubboCertManager> reference = new AtomicReference<>();
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.remoting.api;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
|
||||
/**
|
||||
* Listeners listening to connection events.
|
||||
* Do not do heavy jobs in listeners to avoid blocking the workers.
|
||||
*/
|
||||
@SPI(scope = ExtensionScope.APPLICATION)
|
||||
public interface ChannelContextListener {
|
||||
|
||||
/**
|
||||
* On connection established
|
||||
* @param channelContext channelContext
|
||||
*/
|
||||
void onConnect(Object channelContext);
|
||||
|
||||
/**
|
||||
* On connection disconnected
|
||||
* @param channelContext channelContext
|
||||
*/
|
||||
void onDisconnect(Object channelContext);
|
||||
}
|
||||
|
|
@ -17,30 +17,39 @@
|
|||
package org.apache.dubbo.remoting.transport.netty4;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.remoting.Channel;
|
||||
import org.apache.dubbo.remoting.ChannelHandler;
|
||||
import org.apache.dubbo.remoting.api.ChannelContextListener;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
public class NettyChannelHandler extends ChannelInboundHandlerAdapter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NettyChannelHandler.class);
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(NettyChannelHandler.class);
|
||||
|
||||
private final Map<String, Channel> dubboChannels;
|
||||
|
||||
private final URL url;
|
||||
private final ChannelHandler handler;
|
||||
|
||||
public NettyChannelHandler(Map<String, Channel> dubboChannels, URL url, ChannelHandler handler) {
|
||||
private final List<ChannelContextListener> contextListeners;
|
||||
|
||||
public NettyChannelHandler(
|
||||
Map<String, Channel> dubboChannels,
|
||||
URL url,
|
||||
ChannelHandler handler,
|
||||
List<ChannelContextListener> listeners) {
|
||||
this.dubboChannels = dubboChannels;
|
||||
this.url = url;
|
||||
this.handler = handler;
|
||||
this.contextListeners = listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -51,7 +60,13 @@ public class NettyChannelHandler extends ChannelInboundHandlerAdapter {
|
|||
dubboChannels.put(
|
||||
NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
|
||||
handler.connected(channel);
|
||||
|
||||
contextListeners.forEach(listener -> {
|
||||
try {
|
||||
listener.onConnect(ctx);
|
||||
} catch (Exception e) {
|
||||
logger.warn("99-1", "", "", "", "Failed to invoke listener when channel connect:", e);
|
||||
}
|
||||
});
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
|
||||
+ " is established.");
|
||||
|
|
@ -75,6 +90,13 @@ public class NettyChannelHandler extends ChannelInboundHandlerAdapter {
|
|||
}
|
||||
} finally {
|
||||
NettyChannel.removeChannel(ctx.channel());
|
||||
contextListeners.forEach(listener -> {
|
||||
try {
|
||||
listener.onDisconnect(ctx);
|
||||
} catch (Exception e) {
|
||||
logger.warn("99-1", "", "", "", "Failed to invoke listener when channel disconnect:", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,13 +26,17 @@ import org.apache.dubbo.remoting.Channel;
|
|||
import org.apache.dubbo.remoting.ChannelHandler;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.remoting.RemotingException;
|
||||
import org.apache.dubbo.remoting.api.ChannelContextListener;
|
||||
import org.apache.dubbo.remoting.api.WireProtocol;
|
||||
import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer;
|
||||
import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
|
@ -76,6 +80,8 @@ public class NettyPortUnificationServer extends AbstractPortUnificationServer {
|
|||
private EventLoopGroup workerGroup;
|
||||
private final Map<String, Channel> dubboChannels = new ConcurrentHashMap<>();
|
||||
|
||||
private final List<ChannelContextListener> listeners;
|
||||
|
||||
public NettyPortUnificationServer(URL url, ChannelHandler handler) throws RemotingException {
|
||||
super(url, ChannelHandlers.wrap(handler, url));
|
||||
|
||||
|
|
@ -84,6 +90,11 @@ public class NettyPortUnificationServer extends AbstractPortUnificationServer {
|
|||
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
|
||||
// read config before destroy
|
||||
serverShutdownTimeoutMills = ConfigurationUtils.getServerShutdownTimeout(getUrl().getOrDefaultModuleModel());
|
||||
listeners = (url.getScopeModel() == null
|
||||
? FrameworkModel.defaultModel().defaultApplication()
|
||||
: ((ModuleModel) url.getScopeModel()).getApplicationModel())
|
||||
.getExtensionLoader(ChannelContextListener.class)
|
||||
.getActivateExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -124,8 +135,8 @@ public class NettyPortUnificationServer extends AbstractPortUnificationServer {
|
|||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
// Do not add idle state handler here, because it should be added in the protocol handler.
|
||||
final ChannelPipeline p = ch.pipeline();
|
||||
NettyChannelHandler nettyChannelHandler =
|
||||
new NettyChannelHandler(dubboChannels, getUrl(), NettyPortUnificationServer.this);
|
||||
NettyChannelHandler nettyChannelHandler = new NettyChannelHandler(
|
||||
dubboChannels, getUrl(), NettyPortUnificationServer.this, listeners);
|
||||
NettyPortUnificationServerHandler puHandler = new NettyPortUnificationServerHandler(
|
||||
getUrl(),
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ public class NettyServer extends AbstractServer {
|
|||
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
protected void initChannel(SocketChannel ch) {
|
||||
int closeTimeout = UrlUtils.getCloseTimeout(getUrl());
|
||||
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
|
||||
ch.pipeline().addLast("negotiation", new SslServerTlsHandler(getUrl()));
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class NettyServerHandler extends ChannelDuplexHandler {
|
|||
}
|
||||
handler.connected(channel);
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
if (logger.isInfoEnabled() && channel != null) {
|
||||
logger.info("The connection of " + channel.getRemoteAddress() + " -> " + channel.getLocalAddress()
|
||||
+ " is established.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class SslContexts {
|
|||
|
||||
if (serverTrustCertStream != null) {
|
||||
sslClientContextBuilder.trustManager(serverTrustCertStream);
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH) {
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH_STRICT) {
|
||||
sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
|
||||
} else {
|
||||
sslClientContextBuilder.clientAuth(ClientAuth.OPTIONAL);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@ public class SslServerTlsHandler extends ByteToMessageDecoder {
|
|||
return;
|
||||
}
|
||||
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.NONE) {
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.NONE
|
||||
|| providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH_PERMISSIVE) {
|
||||
channelHandlerContext.pipeline().remove(this);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.dubbo.rpc;
|
||||
|
||||
public interface BaseFilter {
|
||||
|
||||
/**
|
||||
* Always call invoker.invoke() in the implementation to hand over the request to the next filter node.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ public interface Constants {
|
|||
|
||||
String TOKEN_KEY = "token";
|
||||
|
||||
String ID_TOKEN_KEY = "identity.token";
|
||||
|
||||
String INTERFACE = "interface";
|
||||
|
||||
String INTERFACES = "interfaces";
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class SslContexts {
|
|||
|
||||
if (serverTrustCertStream != null) {
|
||||
sslClientContextBuilder.trustManager(serverTrustCertStream);
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH) {
|
||||
if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH_STRICT) {
|
||||
sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
|
||||
} else {
|
||||
sslClientContextBuilder.clientAuth(ClientAuth.OPTIONAL);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The xds module of dubbo project</description>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
<protobuf-java_version>3.25.1</protobuf-java_version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
@ -86,12 +88,7 @@
|
|||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-api</artifactId>
|
||||
<version>1.61.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
|
|
@ -141,6 +138,97 @@
|
|||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.kubernetes</groupId>
|
||||
<artifactId>client-java</artifactId>
|
||||
<version>10.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.18.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>jwks-rsa</artifactId>
|
||||
<version>0.18.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-config-api</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-triple</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-zookeeper</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-hessian2</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.12.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>${maven_protobuf_plugin_version}</version>
|
||||
<configuration>
|
||||
<protocArtifact>com.google.protobuf:protoc:${protobuf-java_version}:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc_version}:exe:${os.detected.classifier}</pluginArtifact>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>compile-custom</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>9</source>
|
||||
<target>9</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>${maven_os_plugin_version}</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,12 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
|
|||
import org.apache.dubbo.xds.protocol.AbstractProtocol;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
|
|
@ -46,6 +49,8 @@ public class AdsObserver {
|
|||
|
||||
protected StreamObserver<DiscoveryRequest> requestObserver;
|
||||
|
||||
private CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
private final Map<String, DiscoveryRequest> observedResources = new ConcurrentHashMap<>();
|
||||
|
||||
public AdsObserver(URL url, Node node) {
|
||||
|
|
@ -61,22 +66,42 @@ public class AdsObserver {
|
|||
|
||||
public void request(DiscoveryRequest discoveryRequest) {
|
||||
if (requestObserver == null) {
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this));
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this, future));
|
||||
}
|
||||
requestObserver.onNext(discoveryRequest);
|
||||
observedResources.put(discoveryRequest.getTypeUrl(), discoveryRequest);
|
||||
try {
|
||||
// TODO:This is to make the child thread receive the information.
|
||||
// Maybe Using CountDownLatch would be better
|
||||
String name = Thread.currentThread().getName();
|
||||
if ("main".equals(name)) {
|
||||
future.get(600, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (TimeoutException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResponseObserver implements StreamObserver<DiscoveryResponse> {
|
||||
private AdsObserver adsObserver;
|
||||
|
||||
public ResponseObserver(AdsObserver adsObserver) {
|
||||
private CompletableFuture future;
|
||||
|
||||
public ResponseObserver(AdsObserver adsObserver, CompletableFuture future) {
|
||||
this.adsObserver = adsObserver;
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(DiscoveryResponse discoveryResponse) {
|
||||
System.out.println("Receive message from server");
|
||||
logger.info("Receive message from server");
|
||||
if (future != null) {
|
||||
future.complete(null);
|
||||
}
|
||||
XdsListener xdsListener = adsObserver.listeners.get(discoveryResponse.getTypeUrl());
|
||||
xdsListener.process(discoveryResponse);
|
||||
adsObserver.requestObserver.onNext(buildAck(discoveryResponse));
|
||||
|
|
@ -122,7 +147,8 @@ public class AdsObserver {
|
|||
try {
|
||||
xdsChannel = new XdsChannel(url);
|
||||
if (xdsChannel.getChannel() != null) {
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this));
|
||||
// Child thread not need to wait other child thread.
|
||||
requestObserver = xdsChannel.createDeltaDiscoveryRequest(new ResponseObserver(this, null));
|
||||
observedResources.values().forEach(requestObserver::onNext);
|
||||
return;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ package org.apache.dubbo.xds;
|
|||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.xds.istio.IstioEnv;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
|
||||
public class NodeBuilder {
|
||||
|
|
@ -32,10 +37,22 @@ public class NodeBuilder {
|
|||
String podName = IstioEnv.getInstance().getPodName();
|
||||
String podNamespace = IstioEnv.getInstance().getWorkloadNameSpace();
|
||||
String svcName = IstioEnv.getInstance().getIstioMetaClusterId();
|
||||
String saName = IstioEnv.getInstance().getServiceAccountName();
|
||||
|
||||
Map<String, Value> metadataMap = new HashMap<>(2);
|
||||
|
||||
metadataMap.put(
|
||||
"ISTIO_META_NAMESPACE",
|
||||
Value.newBuilder().setStringValue(podNamespace).build());
|
||||
metadataMap.put(
|
||||
"SERVICE_ACCOUNT", Value.newBuilder().setStringValue(saName).build());
|
||||
|
||||
Struct metadata = Struct.newBuilder().putAllFields(metadataMap).build();
|
||||
|
||||
// id -> sidecar~ip~{POD_NAME}~{NAMESPACE_NAME}.svc.cluster.local
|
||||
// cluster -> {SVC_NAME}
|
||||
return Node.newBuilder()
|
||||
.setMetadata(metadata)
|
||||
.setId("sidecar~" + NetUtils.getLocalHost() + "~" + podName + "~" + podNamespace + SVC_CLUSTER_LOCAL)
|
||||
.setCluster(svcName)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ package org.apache.dubbo.xds;
|
|||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.xds.directory.XdsDirectory;
|
||||
import org.apache.dubbo.xds.protocol.XdsResourceListener;
|
||||
import org.apache.dubbo.xds.protocol.impl.CdsProtocol;
|
||||
import org.apache.dubbo.xds.protocol.impl.EdsProtocol;
|
||||
import org.apache.dubbo.xds.protocol.impl.LdsProtocol;
|
||||
import org.apache.dubbo.xds.protocol.impl.RdsProtocol;
|
||||
import org.apache.dubbo.xds.resource.XdsCluster;
|
||||
import org.apache.dubbo.xds.resource.XdsEndpoint;
|
||||
import org.apache.dubbo.xds.resource.XdsRouteConfiguration;
|
||||
import org.apache.dubbo.xds.resource.XdsVirtualHost;
|
||||
|
||||
|
|
@ -31,7 +33,10 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint;
|
||||
|
||||
public class PilotExchanger {
|
||||
|
||||
|
|
@ -61,23 +66,31 @@ public class PilotExchanger {
|
|||
int pollingTimeout = url.getParameter("pollingTimeout", 10);
|
||||
adsObserver = new AdsObserver(url, NodeBuilder.build());
|
||||
|
||||
// rds resources callback
|
||||
Consumer<List<XdsRouteConfiguration>> rdsCallback = (xdsRouteConfigurations) -> {
|
||||
xdsRouteConfigurations.forEach(xdsRouteConfiguration -> {
|
||||
xdsRouteConfiguration.getVirtualHosts().forEach((serviceName, xdsVirtualHost) -> {
|
||||
this.xdsVirtualHostMap.put(serviceName, xdsVirtualHost);
|
||||
// when resource update, notify subscribers
|
||||
if (rdsListeners.containsKey(serviceName)) {
|
||||
for (XdsDirectory listener : rdsListeners.get(serviceName)) {
|
||||
listener.onRdsChange(serviceName, xdsVirtualHost);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
this.rdsProtocol =
|
||||
new RdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, url.getOrDefaultApplicationModel());
|
||||
this.edsProtocol =
|
||||
new EdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, url.getOrDefaultApplicationModel());
|
||||
this.ldsProtocol =
|
||||
new LdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, url.getOrDefaultApplicationModel());
|
||||
this.cdsProtocol =
|
||||
new CdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, url.getOrDefaultApplicationModel());
|
||||
|
||||
// eds resources callback
|
||||
Consumer<List<XdsCluster>> edsCallback = (xdsClusters) -> {
|
||||
XdsResourceListener<XdsRouteConfiguration> pilotRdsListener =
|
||||
xdsRouteConfigurations -> xdsRouteConfigurations.forEach(xdsRouteConfiguration -> xdsRouteConfiguration
|
||||
.getVirtualHosts()
|
||||
.forEach((serviceName, xdsVirtualHost) -> {
|
||||
this.xdsVirtualHostMap.put(serviceName, xdsVirtualHost);
|
||||
// when resource update, notify subscribers
|
||||
if (rdsListeners.containsKey(serviceName)) {
|
||||
for (XdsDirectory listener : rdsListeners.get(serviceName)) {
|
||||
listener.onRdsChange(serviceName, xdsVirtualHost);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
XdsResourceListener<ClusterLoadAssignment> pilotEdsListener = clusterLoadAssignments -> {
|
||||
List<XdsCluster> xdsClusters =
|
||||
clusterLoadAssignments.stream().map(this::parseCluster).collect(Collectors.toList());
|
||||
xdsClusters.forEach(xdsCluster -> {
|
||||
this.xdsClusterMap.put(xdsCluster.getName(), xdsCluster);
|
||||
if (cdsListeners.containsKey(xdsCluster.getName())) {
|
||||
|
|
@ -87,21 +100,16 @@ public class PilotExchanger {
|
|||
}
|
||||
});
|
||||
};
|
||||
this.rdsProtocol = new RdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, rdsCallback);
|
||||
this.edsProtocol = new EdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout, edsCallback);
|
||||
|
||||
this.ldsProtocol = new LdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout);
|
||||
this.cdsProtocol = new CdsProtocol(adsObserver, NodeBuilder.build(), pollingTimeout);
|
||||
|
||||
this.rdsProtocol.registerListen(pilotRdsListener);
|
||||
this.edsProtocol.registerListen(pilotEdsListener);
|
||||
// lds resources callback,listen to all rds resources in the callback function
|
||||
Consumer<Set<String>> ldsCallback = rdsProtocol::subscribeResource;
|
||||
ldsProtocol.setUpdateCallback(ldsCallback);
|
||||
ldsProtocol.subscribeListeners();
|
||||
this.ldsProtocol.registerListen(rdsProtocol.getLdsListener());
|
||||
this.cdsProtocol.registerListen(edsProtocol.getCdsListener());
|
||||
|
||||
// cds resources callback,listen to all cds resources in the callback function
|
||||
Consumer<Set<String>> cdsCallback = edsProtocol::subscribeResource;
|
||||
cdsProtocol.setUpdateCallback(cdsCallback);
|
||||
cdsProtocol.subscribeClusters();
|
||||
this.cdsProtocol.subscribeClusters();
|
||||
this.ldsProtocol.subscribeListeners();
|
||||
}
|
||||
|
||||
public static Map<String, XdsVirtualHost> getXdsVirtualHostMap() {
|
||||
|
|
@ -151,6 +159,10 @@ public class PilotExchanger {
|
|||
}
|
||||
}
|
||||
|
||||
public static PilotExchanger createInstance(URL url) {
|
||||
return new PilotExchanger(url);
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return GLOBAL_PILOT_EXCHANGER != null;
|
||||
}
|
||||
|
|
@ -158,4 +170,27 @@ public class PilotExchanger {
|
|||
public void destroy() {
|
||||
this.adsObserver.destroy();
|
||||
}
|
||||
|
||||
private XdsCluster parseCluster(ClusterLoadAssignment cluster) {
|
||||
XdsCluster xdsCluster = new XdsCluster();
|
||||
|
||||
xdsCluster.setName(cluster.getClusterName());
|
||||
|
||||
List<XdsEndpoint> xdsEndpoints = cluster.getEndpointsList().stream()
|
||||
.flatMap(e -> e.getLbEndpointsList().stream())
|
||||
.map(LbEndpoint::getEndpoint)
|
||||
.map(this::parseEndpoint)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
xdsCluster.setXdsEndpoints(xdsEndpoints);
|
||||
|
||||
return xdsCluster;
|
||||
}
|
||||
|
||||
private XdsEndpoint parseEndpoint(io.envoyproxy.envoy.config.endpoint.v3.Endpoint endpoint) {
|
||||
XdsEndpoint xdsEndpoint = new XdsEndpoint();
|
||||
xdsEndpoint.setAddress(endpoint.getAddress().getSocketAddress().getAddress());
|
||||
xdsEndpoint.setPortValue(endpoint.getAddress().getSocketAddress().getPortValue());
|
||||
return xdsEndpoint;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ import org.apache.dubbo.common.logger.LoggerFactory;
|
|||
import org.apache.dubbo.common.url.component.URLAddress;
|
||||
import org.apache.dubbo.xds.bootstrap.Bootstrapper;
|
||||
import org.apache.dubbo.xds.bootstrap.BootstrapperImpl;
|
||||
import org.apache.dubbo.xds.bootstrap.XdsCertificateSigner;
|
||||
import org.apache.dubbo.xds.security.api.CertPair;
|
||||
import org.apache.dubbo.xds.security.api.CertSource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
|
@ -52,7 +53,7 @@ public class XdsChannel {
|
|||
|
||||
private URL url;
|
||||
|
||||
private static final String SECURE = "secure";
|
||||
private static final String SECURITY = "security";
|
||||
|
||||
private static final String PLAINTEXT = "plaintext";
|
||||
|
||||
|
|
@ -71,15 +72,16 @@ public class XdsChannel {
|
|||
this.url = url;
|
||||
try {
|
||||
if (!url.getParameter(USE_AGENT, false)) {
|
||||
if (PLAINTEXT.equals(url.getParameter(SECURE))) {
|
||||
// TODO:Need to consider situation where only user sa_jwt
|
||||
if (PLAINTEXT.equals(url.getParameter(SECURITY))) {
|
||||
managedChannel = NettyChannelBuilder.forAddress(url.getHost(), url.getPort())
|
||||
.usePlaintext()
|
||||
.build();
|
||||
} else {
|
||||
XdsCertificateSigner signer = url.getOrDefaultApplicationModel()
|
||||
.getExtensionLoader(XdsCertificateSigner.class)
|
||||
.getExtension(url.getParameter("signer", "istio"));
|
||||
XdsCertificateSigner.CertPair certPair = signer.GenerateCert(url);
|
||||
CertSource signer = url.getOrDefaultApplicationModel()
|
||||
.getExtensionLoader(CertSource.class)
|
||||
.getExtension(url.getProtocol());
|
||||
CertPair certPair = signer.getCert(url, null);
|
||||
SslContext context = GrpcSslContexts.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
.keyManager(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds;
|
||||
|
||||
public class XdsException extends RuntimeException {
|
||||
|
||||
private Type type;
|
||||
|
||||
public XdsException(Type type, String message) {
|
||||
super("[" + type + "]" + message);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public XdsException(Type type, String message, Throwable cause) {
|
||||
super("[" + type + "]" + message, cause);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public XdsException(Type type, Throwable cause) {
|
||||
super("[" + type + "]", cause);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
EDS,
|
||||
CDS,
|
||||
SDS,
|
||||
LDS,
|
||||
RDS,
|
||||
ADS,
|
||||
SECURITY,
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
@ -31,4 +31,8 @@ public class XdsCluster extends AbstractCluster {
|
|||
XdsDirectory<T> xdsDirectory = new XdsDirectory<>(directory);
|
||||
return new XdsClusterInvoker<>(xdsDirectory);
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,23 +38,30 @@ public class XdsClusterInvoker<T> extends AbstractClusterInvoker<T> {
|
|||
@Override
|
||||
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance)
|
||||
throws RpcException {
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
try {
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
|
||||
throw (RpcException) e;
|
||||
while (true) {
|
||||
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
|
||||
try {
|
||||
return invokeWithContext(invoker, invocation);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
|
||||
throw (RpcException) e;
|
||||
}
|
||||
throw new RpcException(
|
||||
e instanceof RpcException ? ((RpcException) e).getCode() : 0,
|
||||
"Xds invoke providers " + invoker.getUrl() + " "
|
||||
+ loadbalance.getClass().getSimpleName()
|
||||
+ " for service " + getInterface().getName()
|
||||
+ " method " + RpcUtils.getMethodName(invocation) + " on consumer "
|
||||
+ NetUtils.getLocalHost()
|
||||
+ " use dubbo version " + Version.getVersion()
|
||||
+ ", but no luck to perform the invocation. Last error is: " + e.getMessage(),
|
||||
e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
throw new RpcException(
|
||||
e instanceof RpcException ? ((RpcException) e).getCode() : 0,
|
||||
"Xds invoke providers " + invoker.getUrl() + " "
|
||||
+ loadbalance.getClass().getSimpleName()
|
||||
+ " for service " + getInterface().getName()
|
||||
+ " method " + RpcUtils.getMethodName(invocation) + " on consumer "
|
||||
+ NetUtils.getLocalHost()
|
||||
+ " use dubbo version " + Version.getVersion()
|
||||
+ ", but no luck to perform the invocation. Last error is: " + e.getMessage(),
|
||||
e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.config;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.deploy.ApplicationDeployListener;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.config.RegistryConfig;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.PilotExchanger;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.apache.dubbo.config.Constants.SUPPORT_MESH_TYPE;
|
||||
|
||||
public class XdsApplicationDeployListener implements ApplicationDeployListener {
|
||||
@Override
|
||||
public void onInitialize(ApplicationModel scopeModel) {}
|
||||
|
||||
@Override
|
||||
public void onStarting(ApplicationModel scopeModel) {
|
||||
Collection<RegistryConfig> registryConfigs =
|
||||
scopeModel.getApplicationConfigManager().getRegistries();
|
||||
for (RegistryConfig registryConfig : registryConfigs) {
|
||||
String protocol = registryConfig.getProtocol();
|
||||
if (StringUtils.isNotEmpty(protocol) && SUPPORT_MESH_TYPE.contains(protocol)) {
|
||||
URL url = URL.valueOf(registryConfig.getAddress());
|
||||
url = url.setScopeModel(scopeModel);
|
||||
scopeModel.getFrameworkModel().getBeanFactory().registerBean(PilotExchanger.createInstance(url));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStarted(ApplicationModel scopeModel) {}
|
||||
|
||||
@Override
|
||||
public void onStopping(ApplicationModel scopeModel) {}
|
||||
|
||||
@Override
|
||||
public void onStopped(ApplicationModel scopeModel) {}
|
||||
|
||||
@Override
|
||||
public void onFailure(ApplicationModel scopeModel, Throwable cause) {}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
package org.apache.dubbo.xds.directory;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
|
@ -50,7 +52,7 @@ public class XdsDirectory<T> extends AbstractDirectory<T> {
|
|||
|
||||
private final String protocolName;
|
||||
|
||||
PilotExchanger pilotExchanger = PilotExchanger.getInstance();
|
||||
PilotExchanger pilotExchanger;
|
||||
|
||||
private Protocol protocol;
|
||||
|
||||
|
|
@ -58,14 +60,18 @@ public class XdsDirectory<T> extends AbstractDirectory<T> {
|
|||
|
||||
private final Map<String, XdsCluster<T>> xdsClusterMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(XdsDirectory.class);
|
||||
|
||||
public XdsDirectory(Directory<T> directory) {
|
||||
super(directory.getConsumerUrl(), true);
|
||||
super(directory.getUrl(), null, true, directory.getConsumerUrl());
|
||||
this.serviceType = directory.getInterface();
|
||||
this.url = directory.getConsumerUrl();
|
||||
this.applicationNames = url.getParameter("provided-by").split(",");
|
||||
this.protocolName = url.getParameter("protocol", "dubbo");
|
||||
this.protocolName = url.getParameter("protocol", "tri");
|
||||
this.protocol = directory.getProtocol();
|
||||
super.routerChain = directory.getRouterChain();
|
||||
this.pilotExchanger =
|
||||
url.getOrDefaultApplicationModel().getBeanFactory().getBean(PilotExchanger.class);
|
||||
|
||||
// subscribe resource
|
||||
for (String applicationName : applicationNames) {
|
||||
|
|
@ -159,23 +165,25 @@ public class XdsDirectory<T> extends AbstractDirectory<T> {
|
|||
xdsEndpoints.forEach(e -> {
|
||||
String ip = e.getAddress();
|
||||
int port = e.getPortValue();
|
||||
URL url = new URL(this.protocolName, ip, port);
|
||||
URL url = new URL(this.protocolName, ip, port, this.url.getParameters());
|
||||
// set cluster name
|
||||
url = url.addParameter("clusterID", clusterName);
|
||||
// set load balance policy
|
||||
url = url.addParameter("loadbalance", lbPolicy);
|
||||
url.setPath(this.serviceType.getName());
|
||||
// cluster to invoker
|
||||
Invoker<T> invoker = this.protocol.refer(this.serviceType, url);
|
||||
invokers.add(invoker);
|
||||
});
|
||||
// TODO: Consider cases where some clients are not available
|
||||
super.getInvokers().addAll(invokers);
|
||||
// super.setInvokers(invokers);
|
||||
// super.getInvokers().addAll(invokers);
|
||||
// TODO: Need add new api which can add invokers, because a XdsDirectory need monitor multi clusters.
|
||||
super.setInvokers(invokers);
|
||||
xdsCluster.setInvokers(invokers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
package org.apache.dubbo.xds.istio;
|
||||
|
||||
public class IstioConstant {
|
||||
|
||||
public static final String ISTIO_NAME = "istio";
|
||||
|
||||
/**
|
||||
* Address of the spiffe certificate provider. Defaults to discoveryAddress
|
||||
*/
|
||||
|
|
@ -25,7 +28,7 @@ public class IstioConstant {
|
|||
/**
|
||||
* CA and xDS services
|
||||
*/
|
||||
public static final String DEFAULT_CA_ADDR = "localhost:15012";
|
||||
public static final String DEFAULT_CA_ADDR = "istiod.istio-system.svc:15012";
|
||||
|
||||
/**
|
||||
* The trust domain for spiffe certificates
|
||||
|
|
@ -44,13 +47,13 @@ public class IstioConstant {
|
|||
/**
|
||||
* k8s jwt token
|
||||
*/
|
||||
public static final String KUBERNETES_SA_PATH = "";
|
||||
public static String KUBERNETES_SA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
||||
|
||||
public static final String KUBERNETES_CA_PATH = "E:/k8s/ca.crt";
|
||||
public static final String KUBERNETES_CA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
|
||||
|
||||
public static final String ISTIO_SA_PATH = "/var/run/secrets/tokens/istio-token";
|
||||
public static String ISTIO_SA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
||||
|
||||
public static final String ISTIO_CA_PATH = "/var/run/secrets/istio/root-cert.pem";
|
||||
public static final String ISTIO_CA_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
|
||||
|
||||
public static final String KUBERNETES_NAMESPACE_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
|
||||
|
||||
|
|
@ -70,11 +73,19 @@ public class IstioConstant {
|
|||
*/
|
||||
public static final String SECRET_TTL_KEY = "SECRET_TTL";
|
||||
|
||||
public static final String TRUST_TTL_KEY = "TRUST_TTL";
|
||||
|
||||
public static final String SERVICE_NAME_KEY = "SERVICE_NAME";
|
||||
|
||||
private static final String DEFAULT_SERVICE_NAME = "default";
|
||||
|
||||
/**
|
||||
* The cert lifetime default value 24h0m0s
|
||||
*/
|
||||
public static final String DEFAULT_SECRET_TTL = "86400"; // 24 * 60 * 60
|
||||
|
||||
public static final String DEFAULT_TRUST_TTL = "86400";
|
||||
|
||||
/**
|
||||
* The grace period ratio for the cert rotation
|
||||
*/
|
||||
|
|
@ -89,7 +100,7 @@ public class IstioConstant {
|
|||
|
||||
public static final String PILOT_CERT_PROVIDER_KEY = "PILOT_CERT_PROVIDER";
|
||||
|
||||
public static final String ISTIO_PILOT_CERT_PROVIDER = "istiod";
|
||||
public static final String PILOT_CERT_PROVIDER_ISTIO = "istiod";
|
||||
|
||||
public static final String DEFAULT_ISTIO_META_CLUSTER_ID = "Kubernetes";
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,34 @@ import java.util.Optional;
|
|||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_READ_FILE_ISTIO;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.CA_ADDR_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_CA_ADDR;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_ECC_SIG_ALG;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_ISTIO_META_CLUSTER_ID;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_JWT_POLICY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_RSA_KEY_SIZE;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_SECRET_TTL;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_TRUST_DOMAIN;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.DEFAULT_TRUST_TTL;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.ECC_SIG_ALG_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.ISTIO_META_CLUSTER_ID_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.JWT_POLICY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.NS;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.RSA_KEY_SIZE_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.SA;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.SECRET_TTL_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.SPIFFE;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.TRUST_DOMAIN_KEY;
|
||||
import static org.apache.dubbo.xds.istio.IstioConstant.TRUST_TTL_KEY;
|
||||
|
||||
public class IstioEnv implements XdsEnv {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(IstioEnv.class);
|
||||
|
||||
private static final IstioEnv INSTANCE = new IstioEnv();
|
||||
|
||||
/**
|
||||
* TODO this can auto read from sa jwt
|
||||
*/
|
||||
private String podName;
|
||||
|
||||
private String caAddr;
|
||||
|
|
@ -45,56 +64,82 @@ public class IstioEnv implements XdsEnv {
|
|||
|
||||
private String trustDomain;
|
||||
|
||||
/**
|
||||
* TODO this can auto read from sa jwt
|
||||
*/
|
||||
private String workloadNameSpace;
|
||||
|
||||
private int rasKeySize;
|
||||
|
||||
private String eccSigAlg;
|
||||
|
||||
private int secretTTL;
|
||||
|
||||
private float secretGracePeriodRatio;
|
||||
|
||||
private String istioMetaClusterId;
|
||||
|
||||
/**
|
||||
* Who provides cert for istio pilot
|
||||
*/
|
||||
private String pilotCertProvider;
|
||||
|
||||
/**
|
||||
* TTL of cert pair. This will affect the frequency of cert refresh.
|
||||
*/
|
||||
private int secretTTL;
|
||||
|
||||
/**
|
||||
* The time start to try to refresh certs
|
||||
*/
|
||||
private long tryRefreshBeforeCertExpireAt;
|
||||
|
||||
/**
|
||||
* TTL of trust storage. This will affect the frequency of trust refresh.
|
||||
* In istio, trust always refresh with cert pair
|
||||
* because istio use cert chains as response for an CSR request.
|
||||
*/
|
||||
private long trustTTL;
|
||||
|
||||
private String serviceAccountJwt;
|
||||
|
||||
/**
|
||||
* TODO this can auto read from sa jwt
|
||||
*/
|
||||
private String serviceAccountName;
|
||||
|
||||
private boolean haveServiceAccount;
|
||||
|
||||
private IstioEnv() {
|
||||
jwtPolicy =
|
||||
Optional.ofNullable(System.getenv(IstioConstant.JWT_POLICY)).orElse(IstioConstant.DEFAULT_JWT_POLICY);
|
||||
podName = Optional.ofNullable(System.getenv("POD_NAME")).orElse(System.getenv("HOSTNAME"));
|
||||
trustDomain = Optional.ofNullable(System.getenv(IstioConstant.TRUST_DOMAIN_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_TRUST_DOMAIN);
|
||||
workloadNameSpace = Optional.ofNullable(System.getenv(IstioConstant.WORKLOAD_NAMESPACE_KEY))
|
||||
.orElseGet(() -> {
|
||||
File namespaceFile = new File(IstioConstant.KUBERNETES_NAMESPACE_PATH);
|
||||
if (namespaceFile.canRead()) {
|
||||
try {
|
||||
return FileUtils.readFileToString(namespaceFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error(REGISTRY_ERROR_READ_FILE_ISTIO, "", "", "read namespace file error", e);
|
||||
}
|
||||
}
|
||||
return IstioConstant.DEFAULT_WORKLOAD_NAMESPACE;
|
||||
});
|
||||
caAddr = Optional.ofNullable(System.getenv(IstioConstant.CA_ADDR_KEY)).orElse(IstioConstant.DEFAULT_CA_ADDR);
|
||||
rasKeySize = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.RSA_KEY_SIZE_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_RSA_KEY_SIZE));
|
||||
eccSigAlg = Optional.ofNullable(System.getenv(IstioConstant.ECC_SIG_ALG_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_ECC_SIG_ALG);
|
||||
secretTTL = Integer.parseInt(Optional.ofNullable(System.getenv(IstioConstant.SECRET_TTL_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_SECRET_TTL));
|
||||
jwtPolicy = getStringProp(JWT_POLICY, DEFAULT_JWT_POLICY);
|
||||
podName = Optional.ofNullable(getStringProp("POD_NAME", (String) null)).orElse(getStringProp("HOSTNAME", ""));
|
||||
trustDomain = getStringProp(TRUST_DOMAIN_KEY, DEFAULT_TRUST_DOMAIN);
|
||||
|
||||
workloadNameSpace = getStringProp(IstioConstant.WORKLOAD_NAMESPACE_KEY, () -> {
|
||||
File namespaceFile = new File(IstioConstant.KUBERNETES_NAMESPACE_PATH);
|
||||
if (namespaceFile.canRead()) {
|
||||
try {
|
||||
return FileUtils.readFileToString(namespaceFile, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error(REGISTRY_ERROR_READ_FILE_ISTIO, "", "", "read namespace file error", e);
|
||||
}
|
||||
}
|
||||
return IstioConstant.DEFAULT_WORKLOAD_NAMESPACE;
|
||||
});
|
||||
caAddr = getStringProp(CA_ADDR_KEY, DEFAULT_CA_ADDR);
|
||||
|
||||
rasKeySize = getIntProp(RSA_KEY_SIZE_KEY, DEFAULT_RSA_KEY_SIZE);
|
||||
eccSigAlg = getStringProp(ECC_SIG_ALG_KEY, DEFAULT_ECC_SIG_ALG);
|
||||
secretTTL = getIntProp(SECRET_TTL_KEY, DEFAULT_SECRET_TTL);
|
||||
trustTTL = getIntProp(TRUST_TTL_KEY, DEFAULT_TRUST_TTL);
|
||||
|
||||
secretGracePeriodRatio =
|
||||
Float.parseFloat(Optional.ofNullable(System.getenv(IstioConstant.SECRET_GRACE_PERIOD_RATIO_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_SECRET_GRACE_PERIOD_RATIO));
|
||||
istioMetaClusterId = Optional.ofNullable(System.getenv(IstioConstant.ISTIO_META_CLUSTER_ID_KEY))
|
||||
.orElse(IstioConstant.DEFAULT_ISTIO_META_CLUSTER_ID);
|
||||
pilotCertProvider = Optional.ofNullable(System.getenv(IstioConstant.PILOT_CERT_PROVIDER_KEY))
|
||||
.orElse("");
|
||||
|
||||
istioMetaClusterId = getStringProp(ISTIO_META_CLUSTER_ID_KEY, DEFAULT_ISTIO_META_CLUSTER_ID);
|
||||
pilotCertProvider = getStringProp(IstioConstant.PILOT_CERT_PROVIDER_KEY, "");
|
||||
serviceAccountName = getStringProp(IstioConstant.SERVICE_NAME_KEY, "default");
|
||||
if (getServiceAccount() == null) {
|
||||
throw new UnsupportedOperationException("Unable to found kubernetes service account token file. "
|
||||
+ "Please check if work in Kubernetes and mount service account token file correctly.");
|
||||
haveServiceAccount = false;
|
||||
logger.info("Unable to found kubernetes service account token. Some istio-XDS feature may disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,13 +177,21 @@ public class IstioEnv implements XdsEnv {
|
|||
e);
|
||||
}
|
||||
}
|
||||
// TODO:Subsequent implementation in the security section
|
||||
return "null";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getServiceAccountJwt() {
|
||||
return serviceAccountJwt;
|
||||
}
|
||||
|
||||
public String getCsrHost() {
|
||||
// spiffe://<trust_domain>/ns/<namespace>/sa/<service_account>
|
||||
return SPIFFE + trustDomain + NS + workloadNameSpace + SA + getServiceAccount();
|
||||
return SPIFFE + trustDomain + NS + workloadNameSpace + SA + getServiceAccountName();
|
||||
}
|
||||
|
||||
public String getIstioMetaNamespace() {
|
||||
return getCsrHost();
|
||||
}
|
||||
|
||||
public String getTrustDomain() {
|
||||
|
|
@ -159,7 +212,7 @@ public class IstioEnv implements XdsEnv {
|
|||
}
|
||||
|
||||
public boolean isECCFirst() {
|
||||
return IstioConstant.DEFAULT_ECC_SIG_ALG.equals(eccSigAlg);
|
||||
return DEFAULT_ECC_SIG_ALG.equals(eccSigAlg);
|
||||
}
|
||||
|
||||
public int getSecretTTL() {
|
||||
|
|
@ -174,9 +227,31 @@ public class IstioEnv implements XdsEnv {
|
|||
return istioMetaClusterId;
|
||||
}
|
||||
|
||||
public Long getTryRefreshBeforeCertExpireAt() {
|
||||
return tryRefreshBeforeCertExpireAt;
|
||||
}
|
||||
|
||||
public String getPilotCertProvider() {
|
||||
return pilotCertProvider;
|
||||
}
|
||||
|
||||
public long getTrustTTL() {
|
||||
return trustTTL;
|
||||
}
|
||||
|
||||
public String getServiceAccountName() {
|
||||
return serviceAccountName;
|
||||
}
|
||||
|
||||
// for test
|
||||
@Deprecated
|
||||
public void setToken(String saJwtToken) {
|
||||
serviceAccountJwt = saJwtToken;
|
||||
}
|
||||
|
||||
public String getCaCert() {
|
||||
File caFile;
|
||||
if (IstioConstant.ISTIO_PILOT_CERT_PROVIDER.equals(pilotCertProvider)) {
|
||||
if (IstioConstant.PILOT_CERT_PROVIDER_ISTIO.equals(pilotCertProvider)) {
|
||||
caFile = new File(IstioConstant.ISTIO_CA_PATH);
|
||||
} else {
|
||||
return null;
|
||||
|
|
@ -191,4 +266,8 @@ public class IstioEnv implements XdsEnv {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean haveServiceAccount() {
|
||||
return haveServiceAccount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,42 @@
|
|||
*/
|
||||
package org.apache.dubbo.xds.istio;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface XdsEnv {
|
||||
|
||||
String getCluster();
|
||||
|
||||
default String getStringProp(String key, String defaultVal) {
|
||||
String val = System.getenv(key);
|
||||
if (val == null) {
|
||||
val = System.getProperty(key);
|
||||
}
|
||||
if (val == null) {
|
||||
val = defaultVal;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
default String getStringProp(String key, Supplier<String> defaultValSupplier) {
|
||||
String val = System.getenv(key);
|
||||
if (val == null) {
|
||||
val = System.getProperty(key);
|
||||
}
|
||||
if (val == null) {
|
||||
val = defaultValSupplier.get();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
default Integer getIntProp(String key, String defaultVal) {
|
||||
String val = System.getenv(key);
|
||||
if (val == null) {
|
||||
val = System.getProperty(key);
|
||||
}
|
||||
if (val == null) {
|
||||
val = defaultVal;
|
||||
}
|
||||
return Integer.valueOf(val);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
|
||||
import io.kubernetes.client.util.ClientBuilder;
|
||||
import io.kubernetes.client.util.Watch;
|
||||
import io.kubernetes.client.util.Watch.Response;
|
||||
import io.kubernetes.client.util.credentials.AccessTokenAuthentication;
|
||||
|
||||
public class KubeApiClient {
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private final ErrorTypeAwareLogger errorTypeAwareLogger =
|
||||
LoggerFactory.getErrorTypeAwareLogger(KubeApiClient.class);
|
||||
|
||||
public KubeApiClient(ApplicationModel applicationModel) throws IOException {
|
||||
KubeEnv kubeEnv = applicationModel.getBeanFactory().getBean(KubeEnv.class);
|
||||
|
||||
apiClient = new ClientBuilder()
|
||||
.setBasePath(kubeEnv.getApiServerPath())
|
||||
.setVerifyingSsl(kubeEnv.isEnableSsl())
|
||||
.setCertificateAuthority(kubeEnv.getServiceAccountCa())
|
||||
.setAuthentication(new AccessTokenAuthentication(
|
||||
new String(kubeEnv.getServiceAccountToken(), StandardCharsets.UTF_8)))
|
||||
.build();
|
||||
|
||||
apiClient.setConnectTimeout(kubeEnv.apiClientConnectTimeout());
|
||||
apiClient.setReadTimeout(kubeEnv.apiClientReadTimeout());
|
||||
|
||||
Configuration.setDefaultApiClient(apiClient);
|
||||
}
|
||||
|
||||
public Map<String, Object> getResourceAsMap(String apiGroup, String version, String namespace, String plural) {
|
||||
CustomObjectsApi apiInstance = new CustomObjectsApi();
|
||||
try {
|
||||
return (Map<String, Object>) apiInstance.listNamespacedCustomObject(
|
||||
apiGroup, version, namespace, plural, null, null, null, null, null, null, null, null);
|
||||
} catch (ApiException apiException) {
|
||||
// log
|
||||
throw new RuntimeException("Failed to get resource from ApiServer.", apiException);
|
||||
}
|
||||
}
|
||||
|
||||
public Watch<Object> listenResource(String apiGroup, String version, String namespace, String plural) {
|
||||
try {
|
||||
CustomObjectsApi api = new CustomObjectsApi();
|
||||
return Watch.createWatch(
|
||||
apiClient,
|
||||
api.listNamespacedCustomObjectCall(
|
||||
apiGroup, version, namespace, plural, null, null, null, null, null, null, null, true, null),
|
||||
new TypeToken<Response<Object>>() {}.getType());
|
||||
} catch (ApiException apiException) {
|
||||
throw new RuntimeException("Failed to listen resource from ApiServer.", apiException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.kubernetes;
|
||||
|
||||
import org.apache.dubbo.common.io.Bytes;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.istio.XdsEnv;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class KubeEnv implements XdsEnv {
|
||||
|
||||
private String apiServerPath;
|
||||
|
||||
private Boolean enableSsl;
|
||||
|
||||
private String serviceAccountCaPath;
|
||||
|
||||
private String serviceAccountTokenPath;
|
||||
|
||||
private String namespace;
|
||||
|
||||
private String serviceName;
|
||||
|
||||
private String cluster;
|
||||
|
||||
private Integer apiClientConnectTimeout;
|
||||
|
||||
private Integer apiClientReadTimeout;
|
||||
|
||||
public KubeEnv(ApplicationModel applicationModel) {
|
||||
// get config from applicationModel ...
|
||||
setDefault();
|
||||
}
|
||||
|
||||
public void setDefault() {
|
||||
if (StringUtils.isEmpty(apiServerPath)) {
|
||||
apiServerPath = getStringProp("API_SERVER_PATH", "https://kubernetes.default.svc");
|
||||
}
|
||||
if (enableSsl != null) {
|
||||
enableSsl = true;
|
||||
}
|
||||
if (StringUtils.isEmpty(serviceAccountCaPath)) {
|
||||
serviceAccountCaPath = getStringProp("SA_CA_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
|
||||
}
|
||||
if (StringUtils.isEmpty(serviceAccountTokenPath)) {
|
||||
serviceAccountTokenPath =
|
||||
getStringProp("SA_TOKEN_PATH", "/var/run/secrets/kubernetes.io/serviceaccount/token");
|
||||
}
|
||||
if (StringUtils.isEmpty(namespace)) {
|
||||
namespace = getStringProp("NAMESPACE", "dubbo-demo");
|
||||
}
|
||||
if (StringUtils.isEmpty(serviceName)) {
|
||||
serviceName = getStringProp("SERVICE_NAME", "");
|
||||
}
|
||||
if (apiClientConnectTimeout == null) {
|
||||
apiClientConnectTimeout = getIntProp("API_CLIENT_CONNECT_TIMEOUT", "10000");
|
||||
}
|
||||
if (apiClientReadTimeout == null) {
|
||||
apiClientReadTimeout = getIntProp("API_CLIENT_READ_TIMEOUT", "30000");
|
||||
}
|
||||
if (StringUtils.isEmpty(cluster)) {
|
||||
cluster = getStringProp("CLUSTER", "cluster.local");
|
||||
}
|
||||
if (enableSsl == null) {
|
||||
enableSsl = true;
|
||||
}
|
||||
}
|
||||
|
||||
public String getApiServerPath() {
|
||||
return apiServerPath;
|
||||
}
|
||||
|
||||
public String getServiceAccountCaPath() {
|
||||
return serviceAccountCaPath;
|
||||
}
|
||||
|
||||
public String getServiceAccountTokenPath() {
|
||||
return serviceAccountTokenPath;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public byte[] getServiceAccountToken() throws IOException {
|
||||
return readFileAsBytes(getServiceAccountTokenPath());
|
||||
}
|
||||
|
||||
public byte[] getServiceAccountCa() throws IOException {
|
||||
return readFileAsBytes(getServiceAccountCaPath());
|
||||
}
|
||||
|
||||
private byte[] readFileAsBytes(String path) throws IOException {
|
||||
File file = new File(path);
|
||||
byte[] value = new byte[4096];
|
||||
if (!file.exists()) {
|
||||
return new byte[0];
|
||||
}
|
||||
try (FileInputStream in = new FileInputStream(file); ) {
|
||||
int readBytes = in.read(value);
|
||||
if (readBytes > 4096) {
|
||||
throw new RuntimeException("Security resource size > 4096: Too long");
|
||||
}
|
||||
value = Bytes.copyOf(value, readBytes);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public int apiClientConnectTimeout() {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
public int apiClientReadTimeout() {
|
||||
return 30000;
|
||||
}
|
||||
|
||||
public boolean isEnableSsl() {
|
||||
return enableSsl;
|
||||
}
|
||||
|
||||
public int getApiClientConnectTimeout() {
|
||||
return apiClientConnectTimeout;
|
||||
}
|
||||
|
||||
public int getApiClientReadTimeout() {
|
||||
return apiClientReadTimeout;
|
||||
}
|
||||
|
||||
public void setApiServerPath(String apiServerPath) {
|
||||
this.apiServerPath = apiServerPath;
|
||||
}
|
||||
|
||||
public void setEnableSsl(boolean enableSsl) {
|
||||
this.enableSsl = enableSsl;
|
||||
}
|
||||
|
||||
public void setServiceAccountCaPath(String serviceAccountPath) {
|
||||
this.serviceAccountCaPath = serviceAccountPath;
|
||||
}
|
||||
|
||||
public void setServiceAccountTokenPath(String serviceAccountTokenPath) {
|
||||
this.serviceAccountTokenPath = serviceAccountTokenPath;
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public void setApiClientConnectTimeout(int apiClientConnectTimeout) {
|
||||
this.apiClientConnectTimeout = apiClientConnectTimeout;
|
||||
}
|
||||
|
||||
public void setApiClientReadTimeout(int apiClientReadTimeout) {
|
||||
this.apiClientReadTimeout = apiClientReadTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCluster() {
|
||||
return cluster;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.xds.protocol.XdsResourceListener;
|
||||
|
||||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
|
||||
@SPI(scope = ExtensionScope.APPLICATION)
|
||||
public interface CdsListener extends XdsResourceListener<Cluster> {}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.XdsException;
|
||||
import org.apache.dubbo.xds.security.authn.DownstreamTlsConfig;
|
||||
import org.apache.dubbo.xds.security.authn.GeneralTlsConfig;
|
||||
import org.apache.dubbo.xds.security.authn.TlsResourceResolver;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.listener.v3.FilterChain;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext;
|
||||
|
||||
@Activate
|
||||
public class DownstreamTlsConfigListener implements LdsListener {
|
||||
|
||||
protected static final String TLS = "tls";
|
||||
|
||||
protected static final String LDS_VIRTUAL_INBOUND = "virtualInbound";
|
||||
|
||||
protected static final String DOWNSTREAM_TLS_CONTEXT_TYPE =
|
||||
"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext";
|
||||
|
||||
protected static final String TRANSPORT_SOCKET_NAME_TLS = "envoy.transport_sockets.tls";
|
||||
|
||||
protected static final String TRANSPORT_SOCKET_NAME_PLAINTEXT = "envoy.transport_sockets.raw_buffer";
|
||||
|
||||
private final XdsTlsConfigRepository repo;
|
||||
|
||||
public DownstreamTlsConfigListener(ApplicationModel applicationModel) {
|
||||
this.repo = applicationModel.getBeanFactory().getOrRegisterBean(XdsTlsConfigRepository.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceUpdate(List<Listener> listeners) {
|
||||
if (CollectionUtils.isEmpty(listeners)) {
|
||||
return;
|
||||
}
|
||||
Map<String, DownstreamTlsConfig> downstreamConfigs = new HashMap<>(4);
|
||||
for (Listener listener : listeners) {
|
||||
// only choose inbound listeners
|
||||
if (!LDS_VIRTUAL_INBOUND.equals(listener.getName())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
int port = listener.getAddress().getSocketAddress().getPortValue();
|
||||
List<FilterChain> filterChains = listener.getFilterChainsList();
|
||||
boolean supportTls = false;
|
||||
boolean supportPlainText = false;
|
||||
DownstreamTlsConfig downstreamTlsConfig = null;
|
||||
|
||||
for (FilterChain filterChain : filterChains) {
|
||||
if (TRANSPORT_SOCKET_NAME_TLS.equals(
|
||||
filterChain.getTransportSocket().getName())) {
|
||||
supportTls = true;
|
||||
}
|
||||
|
||||
if (TRANSPORT_SOCKET_NAME_PLAINTEXT.equals(
|
||||
filterChain.getTransportSocket().getName())) {
|
||||
supportPlainText = true;
|
||||
}
|
||||
|
||||
Any any = filterChain.getTransportSocket().getTypedConfig();
|
||||
|
||||
if (DOWNSTREAM_TLS_CONTEXT_TYPE.equals(any.getTypeUrl())) {
|
||||
DownstreamTlsContext downstreamTlsContext;
|
||||
try {
|
||||
downstreamTlsContext = any.unpack(DownstreamTlsContext.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
CommonTlsContext commonTlsContext = downstreamTlsContext.getCommonTlsContext();
|
||||
GeneralTlsConfig tlsConfig =
|
||||
TlsResourceResolver.resolveCommonTlsConfig(String.valueOf(port), commonTlsContext);
|
||||
|
||||
downstreamTlsConfig = new DownstreamTlsConfig(
|
||||
tlsConfig,
|
||||
downstreamTlsContext
|
||||
.getRequireClientCertificate()
|
||||
.getValue(),
|
||||
downstreamTlsContext.getRequireSni().getValue(),
|
||||
downstreamTlsContext.getSessionTimeout().getNanos());
|
||||
downstreamConfigs.put(String.valueOf(port), downstreamTlsConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (downstreamTlsConfig == null) {
|
||||
downstreamConfigs.put(String.valueOf(port), new DownstreamTlsConfig(TlsType.DISABLE));
|
||||
} else {
|
||||
if (supportTls && supportPlainText) {
|
||||
downstreamTlsConfig.setTlsType(TlsType.PERMISSIVE);
|
||||
}
|
||||
if (!supportTls) {
|
||||
downstreamTlsConfig.setTlsType(TlsType.DISABLE);
|
||||
}
|
||||
if (supportTls && !supportPlainText) {
|
||||
downstreamTlsConfig.setTlsType(TlsType.STRICT);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new XdsException(
|
||||
XdsException.Type.LDS,
|
||||
"Invalid UpstreamTlsContext config provided for port:"
|
||||
+ listener.getAddress().getSocketAddress().getPortValue(),
|
||||
e);
|
||||
}
|
||||
repo.updateInbound(downstreamConfigs);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TlsType {
|
||||
STRICT(0, "Strict Mode"),
|
||||
PERMISSIVE(1, "Permissive Mode"),
|
||||
DISABLE(2, "Disable Mode"),
|
||||
;
|
||||
public static Map<Integer, TlsType> map = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (TlsType tlsEnum : TlsType.values()) {
|
||||
map.put(tlsEnum.code, tlsEnum);
|
||||
}
|
||||
}
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
|
||||
TlsType(int code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public static TlsType getFromCode(int code) {
|
||||
return map.get(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TlsType{" + "code=" + code + ", msg='" + msg + '\'' + '}';
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.xds.protocol.XdsResourceListener;
|
||||
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
|
||||
@SPI(scope = ExtensionScope.APPLICATION)
|
||||
public interface LdsListener extends XdsResourceListener<Listener> {}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
public class ListenerConstants {
|
||||
|
||||
public static final String LDS_VIRTUAL_INBOUND = "virtualInbound";
|
||||
|
||||
public static final String LDS_CONNECTION_MANAGER = "envoy.filters.network.http_connection_manager";
|
||||
|
||||
public static final String LDS_JWT_FILTER = "envoy.filters.http.jwt_authn";
|
||||
|
||||
public static final String LDS_RBAC_FILTER = "envoy.filters.http.rbac";
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.XdsException;
|
||||
import org.apache.dubbo.xds.XdsException.Type;
|
||||
import org.apache.dubbo.xds.security.authn.TlsResourceResolver;
|
||||
import org.apache.dubbo.xds.security.authn.UpstreamTlsConfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext;
|
||||
|
||||
@Activate
|
||||
public class UpstreamTlsConfigListener implements CdsListener {
|
||||
|
||||
private static final String TRANSPORT_SOCKET_NAME = "envoy.transport_sockets.tls";
|
||||
|
||||
private static final String UPSTREAM_TLS_CONFIG_NAME =
|
||||
"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext";
|
||||
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(UpstreamTlsConfigListener.class);
|
||||
|
||||
private final XdsTlsConfigRepository tlsConfigRepository;
|
||||
|
||||
public UpstreamTlsConfigListener(ApplicationModel application) {
|
||||
this.tlsConfigRepository = application.getBeanFactory().getOrRegisterBean(XdsTlsConfigRepository.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceUpdate(List<Cluster> resource) {
|
||||
Map<String, UpstreamTlsConfig> configs = new ConcurrentHashMap<>(16);
|
||||
for (Cluster cluster : resource) {
|
||||
String serviceName = cluster.getName();
|
||||
try {
|
||||
if (!TRANSPORT_SOCKET_NAME.equals(cluster.getTransportSocket().getName())) {
|
||||
// No TLS config found in this cluster.
|
||||
configs.put(serviceName, new UpstreamTlsConfig());
|
||||
logger.debug(
|
||||
"No TLS config provided for this service to connect upstream cluster:" + cluster.getName());
|
||||
continue;
|
||||
}
|
||||
String typeUrl = cluster.getTransportSocket().getTypedConfig().getTypeUrl();
|
||||
|
||||
if (!UPSTREAM_TLS_CONFIG_NAME.equals(typeUrl)) {
|
||||
logger.info("Unknown TLS config type:" + typeUrl);
|
||||
continue;
|
||||
}
|
||||
|
||||
UpstreamTlsContext tlsContext =
|
||||
cluster.getTransportSocket().getTypedConfig().unpack(UpstreamTlsContext.class);
|
||||
CommonTlsContext commonTlsContext = tlsContext.getCommonTlsContext();
|
||||
|
||||
configs.put(
|
||||
serviceName,
|
||||
new UpstreamTlsConfig(
|
||||
TlsResourceResolver.resolveCommonTlsConfig(serviceName, commonTlsContext),
|
||||
tlsContext.getSni(),
|
||||
tlsContext.getAllowRenegotiation()));
|
||||
tlsConfigRepository.updateOutbound(configs);
|
||||
} catch (InvalidProtocolBufferException invalidProtocolBufferException) {
|
||||
throw new XdsException(
|
||||
Type.CDS, "Invalid UpstreamTlsContext config provided for cluster:" + cluster.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.listener;
|
||||
|
||||
import org.apache.dubbo.xds.security.authn.DownstreamTlsConfig;
|
||||
import org.apache.dubbo.xds.security.authn.UpstreamTlsConfig;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class XdsTlsConfigRepository {
|
||||
|
||||
public XdsTlsConfigRepository() {}
|
||||
|
||||
/**
|
||||
* inbound ports -> configs
|
||||
* Indicates the TLS configuration for inbound connections.
|
||||
*/
|
||||
private volatile Map<String, DownstreamTlsConfig> downstreamConfigs = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* clusterName -> configs
|
||||
* Indicates the TLS configuration for outbound connection to certain cluster.
|
||||
*/
|
||||
private volatile Map<String, UpstreamTlsConfig> upstreamConfigs = Collections.emptyMap();
|
||||
|
||||
public void updateInbound(Map<String, DownstreamTlsConfig> downstreamType) {
|
||||
this.downstreamConfigs = downstreamType;
|
||||
}
|
||||
|
||||
public void updateOutbound(Map<String, UpstreamTlsConfig> upstreamType) {
|
||||
this.upstreamConfigs = upstreamType;
|
||||
}
|
||||
|
||||
public DownstreamTlsConfig getDownstreamConfig(String port) {
|
||||
return downstreamConfigs.get(port);
|
||||
}
|
||||
|
||||
public UpstreamTlsConfig getUpstreamConfig(String clusterName) {
|
||||
return upstreamConfigs.get(clusterName);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
|||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.AdsObserver;
|
||||
import org.apache.dubbo.xds.XdsListener;
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
|
|
@ -70,13 +72,22 @@ public abstract class AbstractProtocol<T> implements XdsProtocol, XdsListener {
|
|||
|
||||
protected Map<String, T> resourcesMap = new ConcurrentHashMap<>();
|
||||
|
||||
public AbstractProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
protected List<XdsResourceListener<T>> resourceListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected ApplicationModel applicationModel;
|
||||
|
||||
public AbstractProtocol(AdsObserver adsObserver, Node node, int checkInterval, ApplicationModel applicationModel) {
|
||||
this.adsObserver = adsObserver;
|
||||
this.node = node;
|
||||
this.checkInterval = checkInterval;
|
||||
this.applicationModel = applicationModel;
|
||||
adsObserver.addListener(this);
|
||||
}
|
||||
|
||||
public void registerListen(XdsResourceListener<T> listener) {
|
||||
this.resourceListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to obtain Type-URL from sub-class
|
||||
*
|
||||
|
|
@ -163,13 +174,18 @@ public abstract class AbstractProtocol<T> implements XdsProtocol, XdsListener {
|
|||
.build();
|
||||
}
|
||||
|
||||
// protected abstract Map<String, T> decodeDiscoveryResponse(DiscoveryResponse response);
|
||||
|
||||
protected abstract Map<String, T> decodeDiscoveryResponse(DiscoveryResponse response);
|
||||
|
||||
@Override
|
||||
public final void process(DiscoveryResponse discoveryResponse) {
|
||||
Map<String, T> newResult = decodeDiscoveryResponse(discoveryResponse);
|
||||
// Map<String, T> newResult = decodeDiscoveryResponse(discoveryResponse);
|
||||
Map<String, T> oldResource = resourcesMap;
|
||||
// discoveryResponseListener(oldResource, newResult);
|
||||
|
||||
Map<String, T> newResult = decodeDiscoveryResponse(discoveryResponse);
|
||||
resourceListeners.forEach(l -> l.onResourceUpdate(new ArrayList<>(newResult.values())));
|
||||
resourcesMap = newResult;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.protocol;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface XdsResourceListener<T> {
|
||||
|
||||
void onResourceUpdate(List<T> resource);
|
||||
}
|
||||
|
|
@ -18,26 +18,34 @@ package org.apache.dubbo.xds.protocol.impl;
|
|||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.AdsObserver;
|
||||
import org.apache.dubbo.xds.listener.CdsListener;
|
||||
import org.apache.dubbo.xds.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.xds.resource.XdsCluster;
|
||||
import org.apache.dubbo.xds.resource.XdsEndpoint;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class CdsProtocol extends AbstractProtocol<String> {
|
||||
public class CdsProtocol extends AbstractProtocol<Cluster> {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(CdsProtocol.class);
|
||||
|
||||
public void setUpdateCallback(Consumer<Set<String>> updateCallback) {
|
||||
|
|
@ -46,8 +54,11 @@ public class CdsProtocol extends AbstractProtocol<String> {
|
|||
|
||||
private Consumer<Set<String>> updateCallback;
|
||||
|
||||
public CdsProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
public CdsProtocol(AdsObserver adsObserver, Node node, int checkInterval, ApplicationModel applicationModel) {
|
||||
super(adsObserver, node, checkInterval, applicationModel);
|
||||
List<CdsListener> ldsListeners =
|
||||
applicationModel.getExtensionLoader(CdsListener.class).getActivateExtensions();
|
||||
ldsListeners.forEach(this::registerListen);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -59,20 +70,63 @@ public class CdsProtocol extends AbstractProtocol<String> {
|
|||
subscribeResource(null);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected Map<String, String> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
// if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
// Set<String> set = response.getResourcesList().stream()
|
||||
// .map(CdsProtocol::unpackCluster)
|
||||
// .filter(Objects::nonNull)
|
||||
// .map(Cluster::getName)
|
||||
// .collect(Collectors.toSet());
|
||||
// updateCallback.accept(set);
|
||||
// // Map<String, ListenerResult> listenerDecodeResult = new ConcurrentHashMap<>();
|
||||
// // listenerDecodeResult.put(emptyResourceName, new ListenerResult(set));
|
||||
// // return listenerDecodeResult;
|
||||
// }
|
||||
// return new HashMap<>();
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected Map<String, String> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
protected Map<String, Cluster> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
Set<String> set = response.getResourcesList().stream()
|
||||
return response.getResourcesList().stream()
|
||||
.map(CdsProtocol::unpackCluster)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Cluster::getName)
|
||||
.collect(Collectors.toSet());
|
||||
updateCallback.accept(set);
|
||||
// Map<String, ListenerResult> listenerDecodeResult = new ConcurrentHashMap<>();
|
||||
// listenerDecodeResult.put(emptyResourceName, new ListenerResult(set));
|
||||
// return listenerDecodeResult;
|
||||
.collect(Collectors.toMap(Cluster::getName, Function.identity()));
|
||||
}
|
||||
return new HashMap<>();
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
private ClusterLoadAssignment unpackClusterLoadAssignment(Any any) {
|
||||
try {
|
||||
return any.unpack(ClusterLoadAssignment.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public XdsCluster parseCluster(ClusterLoadAssignment cluster) {
|
||||
XdsCluster xdsCluster = new XdsCluster();
|
||||
|
||||
xdsCluster.setName(cluster.getClusterName());
|
||||
|
||||
List<XdsEndpoint> xdsEndpoints = cluster.getEndpointsList().stream()
|
||||
.flatMap(e -> e.getLbEndpointsList().stream())
|
||||
.map(LbEndpoint::getEndpoint)
|
||||
.map(this::parseEndpoint)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
xdsCluster.setXdsEndpoints(xdsEndpoints);
|
||||
|
||||
return xdsCluster;
|
||||
}
|
||||
|
||||
public XdsEndpoint parseEndpoint(io.envoyproxy.envoy.config.endpoint.v3.Endpoint endpoint) {
|
||||
XdsEndpoint xdsEndpoint = new XdsEndpoint();
|
||||
xdsEndpoint.setAddress(endpoint.getAddress().getSocketAddress().getAddress());
|
||||
xdsEndpoint.setPortValue(endpoint.getAddress().getSocketAddress().getPortValue());
|
||||
return xdsEndpoint;
|
||||
}
|
||||
|
||||
private static Cluster unpackCluster(Any any) {
|
||||
|
|
|
|||
|
|
@ -18,16 +18,15 @@ package org.apache.dubbo.xds.protocol.impl;
|
|||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.AdsObserver;
|
||||
import org.apache.dubbo.xds.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.xds.resource.XdsCluster;
|
||||
import org.apache.dubbo.xds.resource.XdsEndpoint;
|
||||
import org.apache.dubbo.xds.protocol.XdsResourceListener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
|
|
@ -35,25 +34,21 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class EdsProtocol extends AbstractProtocol<String> {
|
||||
public class EdsProtocol extends AbstractProtocol<ClusterLoadAssignment> {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(EdsProtocol.class);
|
||||
|
||||
public void setUpdateCallback(Consumer<List<XdsCluster>> updateCallback) {
|
||||
this.updateCallback = updateCallback;
|
||||
}
|
||||
private XdsResourceListener<Cluster> clusterListener = clusters -> {
|
||||
Set<String> clusterNames = clusters.stream().map(Cluster::getName).collect(Collectors.toSet());
|
||||
this.subscribeResource(clusterNames);
|
||||
};
|
||||
|
||||
private Consumer<List<XdsCluster>> updateCallback;
|
||||
|
||||
public EdsProtocol(
|
||||
AdsObserver adsObserver, Node node, int checkInterval, Consumer<List<XdsCluster>> updateCallback) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
this.updateCallback = updateCallback;
|
||||
public EdsProtocol(AdsObserver adsObserver, Node node, int checkInterval, ApplicationModel applicationModel) {
|
||||
super(adsObserver, node, checkInterval, applicationModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -61,54 +56,19 @@ public class EdsProtocol extends AbstractProtocol<String> {
|
|||
return "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
List<XdsCluster> clusters = parse(response);
|
||||
updateCallback.accept(clusters);
|
||||
|
||||
// if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
// return response.getResourcesList().stream()
|
||||
// .map(EdsProtocol::unpackClusterLoadAssignment)
|
||||
// .filter(Objects::nonNull)
|
||||
// .collect(Collectors.toConcurrentMap(
|
||||
// ClusterLoadAssignment::getClusterName, this::decodeResourceToEndpoint));
|
||||
// }
|
||||
return new HashMap<>();
|
||||
public XdsResourceListener<Cluster> getCdsListener() {
|
||||
return clusterListener;
|
||||
}
|
||||
|
||||
public List<XdsCluster> parse(DiscoveryResponse response) {
|
||||
@Override
|
||||
protected Map<String, ClusterLoadAssignment> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (!getTypeUrl().equals(response.getTypeUrl())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.getResourcesList().stream()
|
||||
.map(EdsProtocol::unpackClusterLoadAssignment)
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::parseCluster)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public XdsCluster parseCluster(ClusterLoadAssignment cluster) {
|
||||
XdsCluster xdsCluster = new XdsCluster();
|
||||
|
||||
xdsCluster.setName(cluster.getClusterName());
|
||||
|
||||
List<XdsEndpoint> xdsEndpoints = cluster.getEndpointsList().stream()
|
||||
.flatMap(e -> e.getLbEndpointsList().stream())
|
||||
.map(LbEndpoint::getEndpoint)
|
||||
.map(this::parseEndpoint)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
xdsCluster.setXdsEndpoints(xdsEndpoints);
|
||||
|
||||
return xdsCluster;
|
||||
}
|
||||
|
||||
public XdsEndpoint parseEndpoint(io.envoyproxy.envoy.config.endpoint.v3.Endpoint endpoint) {
|
||||
XdsEndpoint xdsEndpoint = new XdsEndpoint();
|
||||
xdsEndpoint.setAddress(endpoint.getAddress().getSocketAddress().getAddress());
|
||||
xdsEndpoint.setPortValue(endpoint.getAddress().getSocketAddress().getPortValue());
|
||||
return xdsEndpoint;
|
||||
.collect(Collectors.toConcurrentMap(ClusterLoadAssignment::getClusterName, Function.identity()));
|
||||
}
|
||||
|
||||
private static ClusterLoadAssignment unpackClusterLoadAssignment(Any any) {
|
||||
|
|
|
|||
|
|
@ -18,15 +18,17 @@ package org.apache.dubbo.xds.protocol.impl;
|
|||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.AdsObserver;
|
||||
import org.apache.dubbo.xds.listener.LdsListener;
|
||||
import org.apache.dubbo.xds.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.xds.resource.XdsVirtualHost;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
|
|
@ -40,17 +42,15 @@ import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
|||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class LdsProtocol extends AbstractProtocol<XdsVirtualHost> {
|
||||
public class LdsProtocol extends AbstractProtocol<Listener> {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(LdsProtocol.class);
|
||||
|
||||
public void setUpdateCallback(Consumer<Set<String>> updateCallback) {
|
||||
this.updateCallback = updateCallback;
|
||||
}
|
||||
|
||||
private Consumer<Set<String>> updateCallback;
|
||||
|
||||
public LdsProtocol(AdsObserver adsObserver, Node node, int checkInterval) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
public LdsProtocol(AdsObserver adsObserver, Node node, int checkInterval, ApplicationModel applicationModel) {
|
||||
super(adsObserver, node, checkInterval, applicationModel);
|
||||
List<LdsListener> ldsListeners =
|
||||
applicationModel.getExtensionLoader(LdsListener.class).getActivateExtensions();
|
||||
ldsListeners.forEach(this::registerListen);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -63,19 +63,14 @@ public class LdsProtocol extends AbstractProtocol<XdsVirtualHost> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, XdsVirtualHost> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
protected Map<String, Listener> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
Set<String> set = response.getResourcesList().stream()
|
||||
return response.getResourcesList().stream()
|
||||
.map(LdsProtocol::unpackListener)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(e -> decodeResourceToListener(e).stream())
|
||||
.collect(Collectors.toSet());
|
||||
updateCallback.accept(set);
|
||||
// Map<String, ListenerResult> listenerDecodeResult = new ConcurrentHashMap<>();
|
||||
// listenerDecodeResult.put(emptyResourceName, new ListenerResult(set));
|
||||
return null;
|
||||
.collect(Collectors.toConcurrentMap(Listener::getName, Function.identity()));
|
||||
}
|
||||
return new HashMap<>();
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
private Set<String> decodeResourceToListener(Listener resource) {
|
||||
|
|
|
|||
|
|
@ -18,46 +18,46 @@ package org.apache.dubbo.xds.protocol.impl;
|
|||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.AdsObserver;
|
||||
import org.apache.dubbo.xds.protocol.AbstractProtocol;
|
||||
import org.apache.dubbo.xds.protocol.XdsResourceListener;
|
||||
import org.apache.dubbo.xds.resource.XdsRoute;
|
||||
import org.apache.dubbo.xds.resource.XdsRouteAction;
|
||||
import org.apache.dubbo.xds.resource.XdsRouteConfiguration;
|
||||
import org.apache.dubbo.xds.resource.XdsRouteMatch;
|
||||
import org.apache.dubbo.xds.resource.XdsVirtualHost;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Filter;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteAction;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_RESPONSE_XDS;
|
||||
|
||||
public class RdsProtocol extends AbstractProtocol<String> {
|
||||
public class RdsProtocol extends AbstractProtocol<XdsRouteConfiguration> {
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RdsProtocol.class);
|
||||
|
||||
protected Consumer<List<XdsRouteConfiguration>> updateCallback;
|
||||
|
||||
public RdsProtocol(
|
||||
AdsObserver adsObserver,
|
||||
Node node,
|
||||
int checkInterval,
|
||||
Consumer<List<XdsRouteConfiguration>> updateCallback) {
|
||||
super(adsObserver, node, checkInterval);
|
||||
this.updateCallback = updateCallback;
|
||||
public RdsProtocol(AdsObserver adsObserver, Node node, int checkInterval, ApplicationModel applicationModel) {
|
||||
super(adsObserver, node, checkInterval, applicationModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -65,19 +65,31 @@ public class RdsProtocol extends AbstractProtocol<String> {
|
|||
return "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected Map<String, String> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
// List<XdsRouteConfiguration> xdsRouteConfigurations = parse(response);
|
||||
// System.out.println(xdsRouteConfigurations);
|
||||
// updateCallback.accept(xdsRouteConfigurations);
|
||||
// // if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
// // return response.getResourcesList().stream()
|
||||
// // .map(RdsProtocol::unpackRouteConfiguration)
|
||||
// // .filter(Objects::nonNull)
|
||||
// // .collect(Collectors.toConcurrentMap(RouteConfiguration::getName,
|
||||
// // this::decodeResourceToListener));
|
||||
// // }
|
||||
// return new HashMap<>();
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected Map<String, String> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
List<XdsRouteConfiguration> xdsRouteConfigurations = parse(response);
|
||||
System.out.println(xdsRouteConfigurations);
|
||||
updateCallback.accept(xdsRouteConfigurations);
|
||||
// if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
// return response.getResourcesList().stream()
|
||||
// .map(RdsProtocol::unpackRouteConfiguration)
|
||||
// .filter(Objects::nonNull)
|
||||
// .collect(Collectors.toConcurrentMap(RouteConfiguration::getName,
|
||||
// this::decodeResourceToListener));
|
||||
// }
|
||||
return new HashMap<>();
|
||||
protected Map<String, XdsRouteConfiguration> decodeDiscoveryResponse(DiscoveryResponse response) {
|
||||
if (getTypeUrl().equals(response.getTypeUrl())) {
|
||||
return response.getResourcesList().stream()
|
||||
.map(RdsProtocol::unpackRouteConfiguration)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toConcurrentMap(RouteConfiguration::getName, this::parseRouteConfiguration));
|
||||
}
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
public List<XdsRouteConfiguration> parse(DiscoveryResponse response) {
|
||||
|
|
@ -164,6 +176,40 @@ public class RdsProtocol extends AbstractProtocol<String> {
|
|||
return xdsRouteAction;
|
||||
}
|
||||
|
||||
public XdsResourceListener<Listener> getLdsListener() {
|
||||
return ldsListener;
|
||||
}
|
||||
|
||||
private final XdsResourceListener<Listener> ldsListener = resource -> {
|
||||
Set<String> set = resource.stream()
|
||||
.flatMap(e -> listenerToConnectionManagerNames(e).stream())
|
||||
.collect(Collectors.toSet());
|
||||
this.subscribeResource(set);
|
||||
};
|
||||
|
||||
private Set<String> listenerToConnectionManagerNames(Listener resource) {
|
||||
return resource.getFilterChainsList().stream()
|
||||
.flatMap(e -> e.getFiltersList().stream())
|
||||
.map(Filter::getTypedConfig)
|
||||
.map(this::unpackHttpConnectionManager)
|
||||
.filter(Objects::nonNull)
|
||||
.map(HttpConnectionManager::getRds)
|
||||
.map(Rds::getRouteConfigName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private HttpConnectionManager unpackHttpConnectionManager(Any any) {
|
||||
try {
|
||||
if (!any.is(HttpConnectionManager.class)) {
|
||||
return null;
|
||||
}
|
||||
return any.unpack(HttpConnectionManager.class);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
logger.error(REGISTRY_ERROR_RESPONSE_XDS, "", "", "Error occur when decode xDS response.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static RouteConfiguration unpackRouteConfiguration(Any any) {
|
||||
try {
|
||||
return any.unpack(RouteConfiguration.class);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import org.apache.dubbo.registry.client.ReflectionBasedServiceDiscovery;
|
|||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.PilotExchanger;
|
||||
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_ERROR_INITIALIZE_XDS;
|
||||
|
||||
public class XdsServiceDiscovery extends ReflectionBasedServiceDiscovery {
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(XdsServiceDiscovery.class);
|
||||
|
||||
|
|
@ -36,21 +34,21 @@ public class XdsServiceDiscovery extends ReflectionBasedServiceDiscovery {
|
|||
}
|
||||
|
||||
public void doInitialize(URL registryURL) {
|
||||
try {
|
||||
exchanger = PilotExchanger.initialize(registryURL);
|
||||
} catch (Throwable t) {
|
||||
logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
}
|
||||
// try {
|
||||
// exchanger = PilotExchanger.initialize(registryURL);
|
||||
// } catch (Throwable t) {
|
||||
// logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
// }
|
||||
}
|
||||
|
||||
public void doDestroy() {
|
||||
try {
|
||||
if (exchanger == null) {
|
||||
return;
|
||||
}
|
||||
exchanger.destroy();
|
||||
} catch (Throwable t) {
|
||||
logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
}
|
||||
// try {
|
||||
// if (exchanger == null) {
|
||||
// return;
|
||||
// }
|
||||
// exchanger.destroy();
|
||||
// } catch (Throwable t) {
|
||||
// logger.error(REGISTRY_ERROR_INITIALIZE_XDS, "", "", t.getMessage(), t);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.dubbo.xds.router;
|
|||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.Holder;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
|
@ -37,9 +38,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.dubbo.config.Constants.MESH_KEY;
|
||||
|
||||
public class XdsRouter<T> extends AbstractStateRouter<T> {
|
||||
|
||||
private final PilotExchanger pilotExchanger = PilotExchanger.getInstance();
|
||||
private final PilotExchanger pilotExchanger;
|
||||
|
||||
private Map<String, XdsVirtualHost> xdsVirtualHostMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
|
@ -47,6 +50,7 @@ public class XdsRouter<T> extends AbstractStateRouter<T> {
|
|||
|
||||
public XdsRouter(URL url) {
|
||||
super(url);
|
||||
pilotExchanger = url.getOrDefaultApplicationModel().getBeanFactory().getBean(PilotExchanger.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -60,8 +64,8 @@ public class XdsRouter<T> extends AbstractStateRouter<T> {
|
|||
throws RpcException {
|
||||
|
||||
// return all invokers directly if xds is not used
|
||||
// TODO:need to consider where to set ‘xds’ param
|
||||
if (!url.getParameter("xds", false)) {
|
||||
String meshType = url.getParameter(MESH_KEY);
|
||||
if (StringUtils.isEmpty(meshType)) {
|
||||
return invokers;
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +80,7 @@ public class XdsRouter<T> extends AbstractStateRouter<T> {
|
|||
|
||||
private String matchCluster(Invocation invocation) {
|
||||
String cluster = null;
|
||||
String serviceName = invocation.getInvoker().getUrl().getParameter("providedBy");
|
||||
String serviceName = invocation.getInvoker().getUrl().getParameter("provided-by");
|
||||
XdsVirtualHost xdsVirtualHost = pilotExchanger.getXdsVirtualHostMap().get(serviceName);
|
||||
|
||||
// match route
|
||||
|
|
@ -85,12 +89,12 @@ public class XdsRouter<T> extends AbstractStateRouter<T> {
|
|||
String path = "/" + invocation.getInvoker().getUrl().getPath() + "/" + RpcUtils.getMethodName(invocation);
|
||||
if (xdsRoute.getRouteMatch().isMatch(path)) {
|
||||
cluster = xdsRoute.getRouteAction().getCluster();
|
||||
|
||||
// if weighted cluster
|
||||
if (cluster == null) {
|
||||
cluster = computeWeightCluster(xdsRoute.getRouteAction().getClusterWeights());
|
||||
}
|
||||
}
|
||||
if (cluster != null) break;
|
||||
}
|
||||
|
||||
return cluster;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
|
||||
public class CertificateConvertor {
|
||||
|
||||
private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";
|
||||
|
||||
public static List<X509Certificate> readPemX509CertificateChains(List<String> x590CertChains)
|
||||
throws IOException, CertificateException {
|
||||
List<String> certs = new ArrayList<>();
|
||||
|
||||
for (String certChain : x590CertChains) {
|
||||
String[] split = certChain.split(END_CERTIFICATE);
|
||||
for (String c : split) {
|
||||
certs.add(c + END_CERTIFICATE);
|
||||
}
|
||||
}
|
||||
return readPemX509Certificates(certs);
|
||||
}
|
||||
|
||||
public static List<X509Certificate> readPemX509Certificates(List<String> x509Certs)
|
||||
throws IOException, CertificateException {
|
||||
List<X509Certificate> certs = new ArrayList<>();
|
||||
JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
|
||||
|
||||
for (String cert : x509Certs) {
|
||||
X509CertificateHolder holder = readX509Certificate(cert);
|
||||
certs.add(converter.getCertificate(holder));
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
public static X509CertificateHolder readX509Certificate(File x509Cert) throws IOException {
|
||||
PEMParser pemParser = new PEMParser(new FileReader(x509Cert));
|
||||
return (X509CertificateHolder) pemParser.readObject();
|
||||
}
|
||||
|
||||
public static X509CertificateHolder readX509Certificate(String x509Cert) throws IOException {
|
||||
PEMParser pemParser = new PEMParser(new StringReader(x509Cert));
|
||||
return (X509CertificateHolder) pemParser.readObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security;
|
||||
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.security.api.RequestAuthorizer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Activate(group = CommonConstants.PROVIDER)
|
||||
public class ProviderAuthFilter implements Filter {
|
||||
|
||||
private final List<RequestAuthorizer> requestAuthorizers;
|
||||
|
||||
public ProviderAuthFilter(ApplicationModel applicationModel) {
|
||||
this.requestAuthorizers =
|
||||
applicationModel.getExtensionLoader(RequestAuthorizer.class).getActivateExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
|
||||
String localSecurityConfig = invoker.getUrl().getParameter("security");
|
||||
if (StringUtils.isNotEmpty(localSecurityConfig)) {
|
||||
List<String> parts = Arrays.asList(localSecurityConfig.split(","));
|
||||
boolean enable = parts.stream().anyMatch("sa_jwt"::equals);
|
||||
if (enable) {
|
||||
for (RequestAuthorizer requestAuthorizer : requestAuthorizers) {
|
||||
requestAuthorizer.validate(invocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelInitializer;
|
||||
import org.apache.dubbo.xds.kubernetes.KubeApiClient;
|
||||
import org.apache.dubbo.xds.kubernetes.KubeEnv;
|
||||
import org.apache.dubbo.xds.security.api.XdsCertProvider;
|
||||
import org.apache.dubbo.xds.security.authz.rule.source.MapRuleFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SecurityBeanConfig implements ScopeModelInitializer {
|
||||
|
||||
private ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SecurityBeanConfig.class);
|
||||
|
||||
@Override
|
||||
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
|
||||
frameworkModel.getBeanFactory().getOrRegisterBean(XdsCertProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeApplicationModel(ApplicationModel applicationModel) {
|
||||
KubeEnv env = applicationModel.getBeanFactory().getOrRegisterBean(KubeEnv.class);
|
||||
try {
|
||||
if (env.getServiceAccountToken().length > 0) {
|
||||
applicationModel.getBeanFactory().getOrRegisterBean(KubeApiClient.class);
|
||||
applicationModel.getBeanFactory().getOrRegisterBean(MapRuleFactory.class);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.info("SecurityBeanConfig are not initialized because SA token not found.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeModuleModel(ModuleModel moduleModel) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
public class AuthorizationException extends RuntimeException {
|
||||
|
||||
public AuthorizationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public AuthorizationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AuthorizationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
public class CertPair {
|
||||
|
||||
private final String privateKey;
|
||||
private final String publicKey;
|
||||
private final String password;
|
||||
private final long createTime;
|
||||
private final long expireTime;
|
||||
|
||||
public CertPair(String privateKey, String publicKey, long createTime, long expireTime) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.createTime = createTime;
|
||||
this.expireTime = expireTime;
|
||||
this.password = null;
|
||||
}
|
||||
|
||||
public CertPair(String privateKey, String publicKey, String password, long createTime, long expireTime) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.password = password;
|
||||
this.createTime = createTime;
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public boolean isExpire() {
|
||||
return System.currentTimeMillis() < expireTime;
|
||||
}
|
||||
|
||||
public long getExpireTime() {
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SPI(scope = ExtensionScope.FRAMEWORK)
|
||||
public interface CertSource {
|
||||
|
||||
/**
|
||||
* Use selected config to generate cert pair
|
||||
*/
|
||||
CertPair getCert(URL url, SecretConfig secretConfig);
|
||||
|
||||
/**
|
||||
* Select one supported cert config for CertSource. Returns null if no supported cert config found.
|
||||
*/
|
||||
SecretConfig selectSupportedCertConfig(URL url, List<SecretConfig> secretConfig);
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.IOUtils;
|
||||
import org.apache.dubbo.common.utils.Pair;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.DataSource;
|
||||
|
||||
public enum DataSources {
|
||||
|
||||
/**
|
||||
* this DataSource represents a file path
|
||||
*/
|
||||
LOCAL_FILE,
|
||||
|
||||
/**
|
||||
* this DataSource represents an environment variable
|
||||
*/
|
||||
ENVIRONMENT_VARIABLE,
|
||||
|
||||
/**
|
||||
* this DataSource represents an inline string
|
||||
*/
|
||||
INLINE_STRING,
|
||||
|
||||
/**
|
||||
* this DataSource represents inline bytes
|
||||
*/
|
||||
INLINE_BYTES;
|
||||
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DataSources.class);
|
||||
|
||||
public static Pair<String, DataSources> resolveDataSource(DataSource dataSource) {
|
||||
if (dataSource.hasFilename()) {
|
||||
return new Pair<>(dataSource.getFilename(), LOCAL_FILE);
|
||||
}
|
||||
if (dataSource.hasEnvironmentVariable()) {
|
||||
return new Pair<>(dataSource.getEnvironmentVariable(), ENVIRONMENT_VARIABLE);
|
||||
}
|
||||
if (dataSource.hasInlineString()) {
|
||||
return new Pair<>(dataSource.getInlineString(), INLINE_STRING);
|
||||
}
|
||||
if (dataSource.hasInlineBytes()) {
|
||||
return new Pair<>(dataSource.getInlineBytes().toStringUtf8(), INLINE_BYTES);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown data source type");
|
||||
}
|
||||
|
||||
public static String readActualValue(Pair<String, DataSources> dataSource) {
|
||||
return readActualValue(dataSource, null);
|
||||
}
|
||||
|
||||
public static String readActualValue(Pair<String, DataSources> dataSource, FileWatcher watcher) {
|
||||
switch (dataSource.getValue()) {
|
||||
case LOCAL_FILE:
|
||||
if (watcher != null) {
|
||||
String value = new String(watcher.readWatchedFile(dataSource.getKey()));
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
try {
|
||||
watcher.registerWatch(dataSource.getKey());
|
||||
return new String(watcher.readWatchedFile(dataSource.getKey()));
|
||||
} catch (Exception e) {
|
||||
logger.warn("99-1", "", "", "Failed to register watch for file: " + dataSource.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return IOUtils.read(
|
||||
Files.newInputStream(Paths.get(dataSource.getKey())), StandardCharsets.UTF_8.name());
|
||||
} catch (Exception e) {
|
||||
logger.error("99-1", "", "", "Failed to read file: " + dataSource.getKey(), e);
|
||||
return null;
|
||||
}
|
||||
case ENVIRONMENT_VARIABLE:
|
||||
return System.getenv(dataSource.getKey());
|
||||
case INLINE_STRING:
|
||||
case INLINE_BYTES:
|
||||
// bytes were read as UTF-8 string
|
||||
return dataSource.getKey();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown data source type");
|
||||
}
|
||||
}
|
||||
|
||||
public static String readActualValue(DataSource dataSource, FileWatcher watcher) {
|
||||
return readActualValue(resolveDataSource(dataSource), watcher);
|
||||
}
|
||||
|
||||
public static String readActualValue(DataSource dataSource) {
|
||||
return readActualValue(resolveDataSource(dataSource));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.IOUtils;
|
||||
import org.apache.dubbo.common.utils.LRUCache;
|
||||
import org.apache.dubbo.common.utils.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apache.commons.io.monitor.FileAlterationListener;
|
||||
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
|
||||
import org.apache.commons.io.monitor.FileAlterationMonitor;
|
||||
import org.apache.commons.io.monitor.FileAlterationObserver;
|
||||
|
||||
public class FileWatcher {
|
||||
|
||||
private final LRUCache<String, Pair<byte[], FileAlterationMonitor>> filesToWatch = new LRUCache<>(256);
|
||||
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
||||
public void registerWatch(String path) throws Exception {
|
||||
registerWatch(path, 3000);
|
||||
}
|
||||
|
||||
public byte[] readWatchedFile(String path) {
|
||||
Pair<byte[], FileAlterationMonitor> pair = filesToWatch.get(path);
|
||||
if (pair == null) {
|
||||
try {
|
||||
registerWatch(path);
|
||||
pair = filesToWatch.get(path);
|
||||
} catch (Exception e) {
|
||||
logger.warn("", "", "", "Failed to register watch file in path=" + path, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return pair == null ? null : pair.getLeft();
|
||||
}
|
||||
|
||||
public void registerWatch(String path, long checkInterval) throws Exception {
|
||||
FileAlterationObserver observer = new FileAlterationObserver(path);
|
||||
FileAlterationMonitor monitor = new FileAlterationMonitor(checkInterval);
|
||||
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
|
||||
@Override
|
||||
public void onStart(FileAlterationObserver observer) {
|
||||
try {
|
||||
filesToWatch.put(
|
||||
path, new Pair<>(IOUtils.toByteArray(Files.newInputStream(Paths.get(path))), monitor));
|
||||
} catch (IOException e) {
|
||||
logger.warn("", "", "", "Failed to read file in path=" + path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileChange(File file) {
|
||||
try {
|
||||
filesToWatch.put(
|
||||
path, new Pair<>(IOUtils.toByteArray(Files.newInputStream(file.toPath())), monitor));
|
||||
} catch (IOException e) {
|
||||
logger.error("", e.getCause().toString(), "", "Failed to read changed file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileCreate(File file) {
|
||||
try {
|
||||
filesToWatch.put(
|
||||
path, new Pair<>(IOUtils.toByteArray(Files.newInputStream(file.toPath())), monitor));
|
||||
} catch (IOException e) {
|
||||
logger.error("", e.getCause().toString(), "", "Failed to read newly create file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileDelete(File file) {
|
||||
Pair<byte[], FileAlterationMonitor> removed = filesToWatch.remove(path);
|
||||
try {
|
||||
removed.getRight().stop();
|
||||
} catch (Exception e) {
|
||||
logger.error("", e.getCause().toString(), "", "Failed to stop watch deleted file.", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
observer.addListener(listener);
|
||||
monitor.addObserver(observer);
|
||||
monitor.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.xds.security.authn.FileSecretConfig;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig.ConfigType;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig.Source;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Activate
|
||||
public class LocalSecretProvider implements CertSource, TrustSource {
|
||||
|
||||
private ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
|
||||
|
||||
private FileWatcher watcher = new FileWatcher();
|
||||
|
||||
@Override
|
||||
public CertPair getCert(URL url, SecretConfig secretConfig) {
|
||||
|
||||
if (!(secretConfig instanceof FileSecretConfig)) {
|
||||
throw new IllegalStateException("Given config not a FileSecret:" + secretConfig);
|
||||
}
|
||||
|
||||
FileSecretConfig fileSecretConfig = (FileSecretConfig) secretConfig;
|
||||
|
||||
if (fileSecretConfig.getCertChain() == null) {
|
||||
throw new IllegalStateException("CertChain can't be null:" + secretConfig);
|
||||
}
|
||||
if (fileSecretConfig.getPrivateKey() == null) {
|
||||
throw new IllegalStateException("PrivateKey can't be null:" + secretConfig);
|
||||
}
|
||||
|
||||
String certChain = DataSources.readActualValue(fileSecretConfig.getCertChain(), watcher);
|
||||
String privateKey = DataSources.readActualValue(fileSecretConfig.getPrivateKey(), watcher);
|
||||
String password;
|
||||
if (fileSecretConfig.getPassword() != null) {
|
||||
password = DataSources.readActualValue(fileSecretConfig.getPassword(), watcher);
|
||||
} else {
|
||||
password = null;
|
||||
}
|
||||
// TODO how to determine expire time
|
||||
return new CertPair(certChain, privateKey, password, System.currentTimeMillis(), Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretConfig selectSupportedCertConfig(URL url, List<SecretConfig> secretConfigs) {
|
||||
return selectSupportedConfig(
|
||||
secretConfigs,
|
||||
secretConfig -> ConfigType.CERT.equals(secretConfig.configType())
|
||||
&& Source.LOCAL.equals(secretConfig.source()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretConfig selectSupportedTrustConfig(URL url, List<SecretConfig> secretConfigs) {
|
||||
return selectSupportedConfig(
|
||||
secretConfigs,
|
||||
secretConfig -> ConfigType.TRUST.equals(secretConfig.configType())
|
||||
&& Source.LOCAL.equals(secretConfig.source()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509CertChains getTrustCerts(URL url, SecretConfig secretConfig) {
|
||||
|
||||
if (!(secretConfig instanceof FileSecretConfig)) {
|
||||
throw new IllegalStateException("Given config not a FileSecret:" + secretConfig);
|
||||
}
|
||||
FileSecretConfig config = (FileSecretConfig) secretConfig;
|
||||
|
||||
if (config.getTrust() == null) {
|
||||
throw new IllegalStateException("Trust can't be null:" + secretConfig);
|
||||
}
|
||||
String trust = DataSources.readActualValue(config.getTrust(), watcher);
|
||||
// TODO how to determine expire time
|
||||
return new X509CertChains(trust, System.currentTimeMillis(), Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
private SecretConfig selectSupportedConfig(List<SecretConfig> secretConfig, Predicate<SecretConfig> selector) {
|
||||
SecretConfig config = secretConfig.stream().filter(selector).findFirst().orElse(null);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
FileSecretConfig secret = (FileSecretConfig) config;
|
||||
try {
|
||||
if (DataSources.LOCAL_FILE.equals(secret.getCertChain().getValue())) {
|
||||
watcher.registerWatch(secret.getCertChain().getKey());
|
||||
}
|
||||
if (DataSources.LOCAL_FILE.equals(secret.getPrivateKey().getValue())) {
|
||||
watcher.registerWatch(secret.getPrivateKey().getKey());
|
||||
}
|
||||
if (secret.getPassword() != null
|
||||
&& DataSources.LOCAL_FILE.equals(secret.getPassword().getValue())) {
|
||||
watcher.registerWatch(secret.getPassword().getKey());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Failed to watch local file secrets, SecretConfig are removed from list. config=" + secret,
|
||||
e);
|
||||
secretConfig.remove(config);
|
||||
selectSupportedConfig(secretConfig, selector);
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
|
||||
@SPI(scope = ExtensionScope.APPLICATION)
|
||||
public interface RequestAuthorizer {
|
||||
|
||||
void validate(Invocation invocation) throws AuthorizationException;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Adaptive;
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
|
||||
/**
|
||||
* Service identity source. Provided JWT will attach to request and can be used for further authentication.
|
||||
*/
|
||||
@SPI(value = "noOp", scope = ExtensionScope.APPLICATION)
|
||||
public interface ServiceIdentitySource {
|
||||
|
||||
String SERVICE_IDENTITY_KEY = "serviceIdentity";
|
||||
|
||||
@Adaptive(value = {SERVICE_IDENTITY_KEY})
|
||||
String getToken(URL url);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SPI(scope = ExtensionScope.FRAMEWORK)
|
||||
public interface TrustSource {
|
||||
|
||||
X509CertChains getTrustCerts(URL url, SecretConfig secretConfig);
|
||||
|
||||
SecretConfig selectSupportedTrustConfig(URL url, List<SecretConfig> secretConfig);
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.xds.istio.IstioEnv;
|
||||
import org.apache.dubbo.xds.security.CertificateConvertor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class X509CertChains {
|
||||
|
||||
private final byte[] trustChainBytes;
|
||||
|
||||
private final long createTime;
|
||||
|
||||
private final long expireAt;
|
||||
|
||||
public X509CertChains(List<String> pemTrustChains) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String str : pemTrustChains) {
|
||||
builder.append(str);
|
||||
}
|
||||
|
||||
this.trustChainBytes = builder.toString().getBytes(StandardCharsets.UTF_8);
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.expireAt = createTime + IstioEnv.getInstance().getTrustTTL();
|
||||
}
|
||||
|
||||
public X509CertChains(String pemTrustChains, long createTime, long expireAt) {
|
||||
this.trustChainBytes = pemTrustChains.getBytes(StandardCharsets.UTF_8);
|
||||
this.createTime = createTime;
|
||||
this.expireAt = expireAt;
|
||||
}
|
||||
|
||||
public List<X509Certificate> readAsCerts() throws CertificateException, IOException {
|
||||
return CertificateConvertor.readPemX509CertificateChains(
|
||||
Collections.singletonList(new String(trustChainBytes, StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public byte[] readAsBytes() {
|
||||
return trustChainBytes;
|
||||
}
|
||||
|
||||
public long getExpireAt() {
|
||||
return expireAt;
|
||||
}
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.api;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.ssl.AuthPolicy;
|
||||
import org.apache.dubbo.common.ssl.Cert;
|
||||
import org.apache.dubbo.common.ssl.CertProvider;
|
||||
import org.apache.dubbo.common.ssl.ProviderCert;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.xds.PilotExchanger;
|
||||
import org.apache.dubbo.xds.istio.IstioEnv;
|
||||
import org.apache.dubbo.xds.listener.XdsTlsConfigRepository;
|
||||
import org.apache.dubbo.xds.security.authn.DownstreamTlsConfig;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig;
|
||||
import org.apache.dubbo.xds.security.authn.UpstreamTlsConfig;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
|
||||
|
||||
@Activate
|
||||
public class XdsCertProvider implements CertProvider {
|
||||
|
||||
private final List<TrustSource> trustSource;
|
||||
|
||||
private final List<CertSource> certSource;
|
||||
|
||||
private final XdsTlsConfigRepository configRepo;
|
||||
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(XdsCertProvider.class);
|
||||
|
||||
private final IstioEnv istioEnv = IstioEnv.getInstance();
|
||||
|
||||
public XdsCertProvider(FrameworkModel frameworkModel) {
|
||||
this.configRepo = frameworkModel.getBeanFactory().getOrRegisterBean(XdsTlsConfigRepository.class);
|
||||
if (frameworkModel.getBeanFactory().getBean(PilotExchanger.class) == null) {
|
||||
logger.info("XdsCertProvider won't initialize because XDS Client not found.");
|
||||
this.trustSource = Collections.emptyList();
|
||||
this.certSource = Collections.emptyList();
|
||||
return;
|
||||
}
|
||||
this.trustSource = frameworkModel.getExtensionLoader(TrustSource.class).getActivateExtensions();
|
||||
this.certSource = frameworkModel.getExtensionLoader(CertSource.class).getActivateExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupport(URL address) {
|
||||
String side = address.getSide();
|
||||
if (CONSUMER.equals(side)) {
|
||||
// TODO: If XDS URL can support version tag, key should be address.getServiceKey()
|
||||
UpstreamTlsConfig upstreamConfig = configRepo.getUpstreamConfig(address.getServiceInterface());
|
||||
if (upstreamConfig == null || upstreamConfig.getGeneralTlsConfig() == null) {
|
||||
return false;
|
||||
}
|
||||
List<SecretConfig> trustConfigs =
|
||||
upstreamConfig.getGeneralTlsConfig().trustConfigs();
|
||||
List<SecretConfig> certConfigs =
|
||||
upstreamConfig.getGeneralTlsConfig().certConfigs();
|
||||
|
||||
// At least one config provided by LDS
|
||||
return !trustConfigs.isEmpty() || !certConfigs.isEmpty();
|
||||
} else if (PROVIDER.equals(side)) {
|
||||
DownstreamTlsConfig downstreamConfig = configRepo.getDownstreamConfig(String.valueOf(address.getPort()));
|
||||
if (downstreamConfig == null) {
|
||||
return false;
|
||||
}
|
||||
List<SecretConfig> secretConfigs =
|
||||
downstreamConfig.getGeneralTlsConfig().certConfigs();
|
||||
List<SecretConfig> certConfigs =
|
||||
downstreamConfig.getGeneralTlsConfig().trustConfigs();
|
||||
|
||||
// At least one config provided by CDS
|
||||
return !secretConfigs.isEmpty() || !certConfigs.isEmpty();
|
||||
}
|
||||
throw new IllegalStateException("Can't determine side for url:" + address);
|
||||
|
||||
// seems we don't need url to check here anymore
|
||||
// if (TlsType.PERMISSIVE.equals(type)) {
|
||||
// String security = address.getParameter("security");
|
||||
// String mesh = address.getParameter("mesh");
|
||||
// return mesh != null
|
||||
// && security != null
|
||||
// && Arrays.asList(security.split(",")).contains("mTLS");
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderCert getProviderConnectionConfig(URL localAddress) {
|
||||
DownstreamTlsConfig downstreamConfig = configRepo.getDownstreamConfig(String.valueOf(localAddress.getPort()));
|
||||
|
||||
if (downstreamConfig == null || downstreamConfig.getGeneralTlsConfig() == null) {
|
||||
logger.warn("99-0", "", "", "DownstreamTlsConfig is null for localAddress:" + localAddress);
|
||||
return null;
|
||||
}
|
||||
|
||||
CertPair cert = selectCertConfig(
|
||||
localAddress, downstreamConfig.getGeneralTlsConfig().certConfigs());
|
||||
X509CertChains trust = selectTrustConfig(
|
||||
localAddress, downstreamConfig.getGeneralTlsConfig().trustConfigs());
|
||||
|
||||
AuthPolicy authPolicy;
|
||||
switch (downstreamConfig.getTlsType()) {
|
||||
case STRICT:
|
||||
authPolicy = AuthPolicy.CLIENT_AUTH_STRICT;
|
||||
break;
|
||||
case PERMISSIVE:
|
||||
authPolicy = AuthPolicy.CLIENT_AUTH_PERMISSIVE;
|
||||
break;
|
||||
case DISABLE:
|
||||
authPolicy = AuthPolicy.NONE;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected Tls type: " + downstreamConfig.getTlsType());
|
||||
}
|
||||
return new ProviderCert(
|
||||
cert == null ? null : cert.getPublicKey().getBytes(StandardCharsets.UTF_8),
|
||||
cert == null ? null : cert.getPrivateKey().getBytes(StandardCharsets.UTF_8),
|
||||
trust == null ? null : trust.readAsBytes(),
|
||||
cert == null ? null : cert.getPassword(),
|
||||
authPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cert getConsumerConnectionConfig(URL remoteAddress) {
|
||||
UpstreamTlsConfig downstreamConfig = configRepo.getUpstreamConfig(remoteAddress.getServiceInterface());
|
||||
|
||||
if (downstreamConfig == null) {
|
||||
logger.warn("99-0", "", "", "DownstreamTlsConfig is null for remoteUrl:" + remoteAddress);
|
||||
return null;
|
||||
}
|
||||
|
||||
CertPair cert = selectCertConfig(
|
||||
remoteAddress, downstreamConfig.getGeneralTlsConfig().certConfigs());
|
||||
X509CertChains trust = selectTrustConfig(
|
||||
remoteAddress, downstreamConfig.getGeneralTlsConfig().trustConfigs());
|
||||
|
||||
return new ProviderCert(
|
||||
cert == null ? null : cert.getPublicKey().getBytes(StandardCharsets.UTF_8),
|
||||
cert == null ? null : cert.getPrivateKey().getBytes(StandardCharsets.UTF_8),
|
||||
trust == null ? null : trust.readAsBytes(),
|
||||
cert == null ? null : cert.getPassword(),
|
||||
AuthPolicy.SERVER_AUTH);
|
||||
}
|
||||
|
||||
private CertPair selectCertConfig(URL address, List<SecretConfig> certConfigs) {
|
||||
for (CertSource certSource : this.certSource) {
|
||||
SecretConfig secretConfig = certSource.selectSupportedCertConfig(address, certConfigs);
|
||||
if (secretConfig != null) {
|
||||
return certSource.getCert(address, secretConfig);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private X509CertChains selectTrustConfig(URL address, List<SecretConfig> certConfigs) {
|
||||
for (TrustSource trustSource : this.trustSource) {
|
||||
SecretConfig secretConfig = trustSource.selectSupportedTrustConfig(address, certConfigs);
|
||||
if (secretConfig != null) {
|
||||
return trustSource.getTrustCerts(address, secretConfig);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
import org.apache.dubbo.xds.listener.DownstreamTlsConfigListener.TlsType;
|
||||
|
||||
/**
|
||||
* TlsConfig for inbound connection
|
||||
*/
|
||||
public class DownstreamTlsConfig {
|
||||
|
||||
private GeneralTlsConfig generalTlsConfig;
|
||||
|
||||
private boolean requireClientCertificate;
|
||||
|
||||
private boolean requireSni;
|
||||
|
||||
private long sessionTimeout;
|
||||
|
||||
private TlsType tlsType;
|
||||
|
||||
public DownstreamTlsConfig(
|
||||
GeneralTlsConfig generalTlsConfig,
|
||||
boolean requireClientCertificate,
|
||||
boolean requireSni,
|
||||
long sessionTimeout) {
|
||||
this.generalTlsConfig = generalTlsConfig;
|
||||
this.requireClientCertificate = requireClientCertificate;
|
||||
this.requireSni = requireSni;
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public DownstreamTlsConfig(TlsType tlsType) {
|
||||
this.tlsType = tlsType;
|
||||
}
|
||||
|
||||
public GeneralTlsConfig getGeneralTlsConfig() {
|
||||
return generalTlsConfig;
|
||||
}
|
||||
|
||||
public void setGeneralTlsConfig(GeneralTlsConfig generalTlsConfig) {
|
||||
this.generalTlsConfig = generalTlsConfig;
|
||||
}
|
||||
|
||||
public boolean isRequireClientCertificate() {
|
||||
return requireClientCertificate;
|
||||
}
|
||||
|
||||
public void setRequireClientCertificate(boolean requireClientCertificate) {
|
||||
this.requireClientCertificate = requireClientCertificate;
|
||||
}
|
||||
|
||||
public boolean isRequireSni() {
|
||||
return requireSni;
|
||||
}
|
||||
|
||||
public void setRequireSni(boolean requireSni) {
|
||||
this.requireSni = requireSni;
|
||||
}
|
||||
|
||||
public long getSessionTimeout() {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public void setTlsType(TlsType tlsType) {
|
||||
this.tlsType = tlsType;
|
||||
}
|
||||
|
||||
public TlsType getTlsType() {
|
||||
return tlsType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
import org.apache.dubbo.common.utils.Pair;
|
||||
import org.apache.dubbo.xds.security.api.DataSources;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.DataSource;
|
||||
|
||||
public class FileSecretConfig implements SecretConfig {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConfigType configType;
|
||||
|
||||
private final Pair<String, DataSources> certChain;
|
||||
|
||||
private final Pair<String, DataSources> privateKey;
|
||||
|
||||
private final Pair<String, DataSources> password;
|
||||
|
||||
private final Pair<String, DataSources> trust;
|
||||
|
||||
public FileSecretConfig(String name, DataSource certChain, DataSource privateKey, DataSource password) {
|
||||
this.name = name;
|
||||
this.configType = ConfigType.CERT;
|
||||
this.certChain = DataSources.resolveDataSource(certChain);
|
||||
this.privateKey = DataSources.resolveDataSource(privateKey);
|
||||
if (password != null) {
|
||||
this.password = DataSources.resolveDataSource(password);
|
||||
} else {
|
||||
this.password = null;
|
||||
}
|
||||
this.trust = null;
|
||||
}
|
||||
|
||||
public FileSecretConfig(String name, DataSource certChain, DataSource privateKey) {
|
||||
this.name = name;
|
||||
this.configType = ConfigType.CERT;
|
||||
this.certChain = DataSources.resolveDataSource(certChain);
|
||||
this.privateKey = DataSources.resolveDataSource(privateKey);
|
||||
this.password = null;
|
||||
this.trust = null;
|
||||
}
|
||||
|
||||
public FileSecretConfig(String name, DataSource trust) {
|
||||
this.name = name;
|
||||
this.configType = ConfigType.TRUST;
|
||||
this.trust = DataSources.resolveDataSource(trust);
|
||||
this.certChain = null;
|
||||
this.password = null;
|
||||
this.privateKey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigType configType() {
|
||||
return configType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Source source() {
|
||||
return Source.LOCAL;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Pair<String, DataSources> getCertChain() {
|
||||
return certChain;
|
||||
}
|
||||
|
||||
public Pair<String, DataSources> getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public Pair<String, DataSources> getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public Pair<String, DataSources> getTrust() {
|
||||
return trust;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileSecret{" + "name='" + name + '\'' + ", configType=" + configType + ", certChain=" + certChain
|
||||
+ ", privateKey=" + privateKey + ", password=" + password + '}';
|
||||
}
|
||||
|
||||
public enum DefaultNames {
|
||||
LOCAL_TRUST,
|
||||
LOCAL_CERT
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GeneralTlsConfig {
|
||||
|
||||
/**
|
||||
* Name to identify this config, like port or cluster name
|
||||
*/
|
||||
private String name;
|
||||
|
||||
private List<SecretConfig> certConfigs;
|
||||
|
||||
private List<SecretConfig> trustConfigs;
|
||||
|
||||
/**
|
||||
* L7 protocols
|
||||
*/
|
||||
private List<String> alpnProtocols;
|
||||
|
||||
public GeneralTlsConfig(String name) {
|
||||
this.name = name;
|
||||
this.certConfigs = Collections.emptyList();
|
||||
this.trustConfigs = Collections.emptyList();
|
||||
this.alpnProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
public GeneralTlsConfig(
|
||||
String name, List<SecretConfig> certConfigs, List<SecretConfig> trustConfigs, List<String> alpnProtocols) {
|
||||
this.name = name;
|
||||
this.certConfigs = certConfigs;
|
||||
this.trustConfigs = trustConfigs;
|
||||
this.alpnProtocols = alpnProtocols;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<SecretConfig> certConfigs() {
|
||||
return certConfigs;
|
||||
}
|
||||
|
||||
public List<SecretConfig> trustConfigs() {
|
||||
return trustConfigs;
|
||||
}
|
||||
|
||||
public List<String> alpnProtocols() {
|
||||
return alpnProtocols;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.ApiConfigSource;
|
||||
|
||||
public class SdsSecretConfig implements SecretConfig {
|
||||
|
||||
private String configName;
|
||||
|
||||
private ConfigType configType;
|
||||
|
||||
private ApiConfigSource apiConfigSource;
|
||||
|
||||
public SdsSecretConfig(String configName, ConfigType configType, ApiConfigSource apiConfigSource) {
|
||||
this.configName = configName;
|
||||
this.configType = configType;
|
||||
this.apiConfigSource = apiConfigSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return configName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigType configType() {
|
||||
return configType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Source source() {
|
||||
return Source.SDS;
|
||||
}
|
||||
|
||||
public String getConfigName() {
|
||||
return configName;
|
||||
}
|
||||
|
||||
public void setConfigName(String configName) {
|
||||
this.configName = configName;
|
||||
}
|
||||
|
||||
public ConfigType getConfigType() {
|
||||
return configType;
|
||||
}
|
||||
|
||||
public void setConfigType(ConfigType configType) {
|
||||
this.configType = configType;
|
||||
}
|
||||
|
||||
public ApiConfigSource getApiConfigSource() {
|
||||
return apiConfigSource;
|
||||
}
|
||||
|
||||
public void setApiConfigSource(ApiConfigSource apiConfigSource) {
|
||||
this.apiConfigSource = apiConfigSource;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
public interface SecretConfig {
|
||||
|
||||
/**
|
||||
* name of this config
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* this config indicates a cert or trust
|
||||
*/
|
||||
ConfigType configType();
|
||||
|
||||
Source source();
|
||||
|
||||
enum ConfigType {
|
||||
TRUST,
|
||||
CERT
|
||||
}
|
||||
|
||||
enum Source {
|
||||
SDS,
|
||||
LOCAL
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
import org.apache.dubbo.xds.security.authn.FileSecretConfig.DefaultNames;
|
||||
import org.apache.dubbo.xds.security.authn.SecretConfig.ConfigType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.envoyproxy.envoy.config.core.v3.DataSource;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext;
|
||||
import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext.CombinedCertificateValidationContext;
|
||||
|
||||
public class TlsResourceResolver {
|
||||
|
||||
public static GeneralTlsConfig resolveCommonTlsConfig(String configName, CommonTlsContext commonTlsContext) {
|
||||
List<SecretConfig> trustConfigs = new ArrayList<>();
|
||||
List<SecretConfig> certConfigs = new ArrayList<>();
|
||||
|
||||
// sds cert sources
|
||||
List<io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig> sdsCertConfigs =
|
||||
commonTlsContext.getTlsCertificateSdsSecretConfigsList();
|
||||
sdsCertConfigs.forEach(sdsSecretConfig -> certConfigs.add(new SdsSecretConfig(
|
||||
sdsSecretConfig.getName(),
|
||||
ConfigType.CERT,
|
||||
sdsSecretConfig.getSdsConfig().getApiConfigSource())));
|
||||
|
||||
// file cert sources
|
||||
commonTlsContext.getTlsCertificatesList().forEach(tlsCertificate -> {
|
||||
DataSource certChain = tlsCertificate.getCertificateChain();
|
||||
DataSource privateKey = tlsCertificate.getPrivateKey();
|
||||
certConfigs.add(new FileSecretConfig(
|
||||
DefaultNames.LOCAL_CERT.name(),
|
||||
privateKey,
|
||||
certChain,
|
||||
tlsCertificate.hasPassword() ? tlsCertificate.getPassword() : null));
|
||||
});
|
||||
|
||||
// sds trust sources
|
||||
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig sdsTrustConfig =
|
||||
commonTlsContext.getValidationContextSdsSecretConfig();
|
||||
if (commonTlsContext.hasValidationContextSdsSecretConfig()) {
|
||||
trustConfigs.add(new SdsSecretConfig(
|
||||
sdsTrustConfig.getName(),
|
||||
ConfigType.TRUST,
|
||||
sdsTrustConfig.getSdsConfig().getApiConfigSource()));
|
||||
}
|
||||
|
||||
// file trust sources
|
||||
if (commonTlsContext.hasValidationContext()
|
||||
&& commonTlsContext.getValidationContext().hasTrustedCa()) {
|
||||
trustConfigs.add(new FileSecretConfig(
|
||||
DefaultNames.LOCAL_TRUST.name(),
|
||||
commonTlsContext.getValidationContext().getTrustedCa()));
|
||||
}
|
||||
|
||||
CombinedCertificateValidationContext combinedConfig = commonTlsContext.getCombinedValidationContext();
|
||||
if (commonTlsContext.hasCombinedValidationContext()) {
|
||||
if (combinedConfig.hasValidationContextSdsSecretConfig()) {
|
||||
io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig sdsConfig =
|
||||
combinedConfig.getValidationContextSdsSecretConfig();
|
||||
trustConfigs.add(new SdsSecretConfig(
|
||||
sdsConfig.getName(),
|
||||
ConfigType.TRUST,
|
||||
sdsConfig.getSdsConfig().getApiConfigSource()));
|
||||
}
|
||||
if (combinedConfig.hasDefaultValidationContext()) {
|
||||
CertificateValidationContext defaultConfig = combinedConfig.getDefaultValidationContext();
|
||||
if (defaultConfig.hasTrustedCa()) {
|
||||
trustConfigs.add(
|
||||
new FileSecretConfig(DefaultNames.LOCAL_TRUST.name(), defaultConfig.getTrustedCa()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new GeneralTlsConfig(configName, trustConfigs, certConfigs, commonTlsContext.getAlpnProtocolsList());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authn;
|
||||
|
||||
/**
|
||||
* Tls config for outbound connection
|
||||
*/
|
||||
public class UpstreamTlsConfig {
|
||||
|
||||
public GeneralTlsConfig generalTlsConfig;
|
||||
|
||||
private String sni;
|
||||
|
||||
private boolean allowRenegotiation;
|
||||
|
||||
public UpstreamTlsConfig(GeneralTlsConfig generalTlsConfig, String sni, boolean allowRenegotiation) {
|
||||
this.generalTlsConfig = generalTlsConfig;
|
||||
this.sni = sni;
|
||||
this.allowRenegotiation = allowRenegotiation;
|
||||
}
|
||||
|
||||
public UpstreamTlsConfig() {}
|
||||
|
||||
public GeneralTlsConfig getGeneralTlsConfig() {
|
||||
return generalTlsConfig;
|
||||
}
|
||||
|
||||
public void setGeneralTlsConfig(GeneralTlsConfig generalTlsConfig) {
|
||||
this.generalTlsConfig = generalTlsConfig;
|
||||
}
|
||||
|
||||
public String getSni() {
|
||||
return sni;
|
||||
}
|
||||
|
||||
public void setSni(String sni) {
|
||||
this.sni = sni;
|
||||
}
|
||||
|
||||
public boolean isAllowRenegotiation() {
|
||||
return allowRenegotiation;
|
||||
}
|
||||
|
||||
public void setAllowRenegotiation(boolean allowRenegotiation) {
|
||||
this.allowRenegotiation = allowRenegotiation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authz;
|
||||
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class AuthorizationRequestContext {
|
||||
|
||||
private final Invocation invocation;
|
||||
|
||||
private final RequestCredential requestCredential;
|
||||
|
||||
private boolean failed = false;
|
||||
|
||||
private Exception validationException;
|
||||
|
||||
private boolean enableTrace = false;
|
||||
|
||||
private List<String> validateStackTrace;
|
||||
|
||||
private int depth;
|
||||
|
||||
private static final int MAX_DEPTH = 50;
|
||||
|
||||
public AuthorizationRequestContext(Invocation invocation, RequestCredential requestCredential) {
|
||||
this.invocation = invocation;
|
||||
this.requestCredential = requestCredential;
|
||||
}
|
||||
|
||||
public void depthIncrease() {
|
||||
this.depth++;
|
||||
if (depth > MAX_DEPTH) {
|
||||
throw new IllegalStateException("Rule tree depth exceed limit:" + MAX_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
public void depthDecrease() {
|
||||
this.depth--;
|
||||
}
|
||||
|
||||
public void startTrace() {
|
||||
this.enableTrace = true;
|
||||
}
|
||||
|
||||
public void endTrace() {
|
||||
this.enableTrace = false;
|
||||
}
|
||||
|
||||
public boolean enableTrace() {
|
||||
return this.enableTrace;
|
||||
}
|
||||
|
||||
public boolean isFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
public void setFailed(boolean failed) {
|
||||
this.failed = failed;
|
||||
}
|
||||
|
||||
public Exception getValidationException() {
|
||||
return validationException;
|
||||
}
|
||||
|
||||
public void setValidationException(Exception validationException) {
|
||||
this.validationException = validationException;
|
||||
}
|
||||
|
||||
public RequestCredential getRequestCredential() {
|
||||
return requestCredential;
|
||||
}
|
||||
|
||||
public void addTraceInfo(String info) {
|
||||
if (!enableTrace) {
|
||||
return;
|
||||
}
|
||||
if (validateStackTrace == null) {
|
||||
validateStackTrace = new LinkedList<>();
|
||||
}
|
||||
validateStackTrace.add(getNtab() + info);
|
||||
}
|
||||
;
|
||||
|
||||
public String getTraceInfo() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
validateStackTrace.forEach(info -> builder.append(info).append("\n"));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getNtab() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < depth; i++) {
|
||||
builder.append(" ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authz;
|
||||
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.security.api.ServiceIdentitySource;
|
||||
|
||||
import static org.apache.dubbo.rpc.Constants.ID_TOKEN_KEY;
|
||||
|
||||
@Activate(group = CommonConstants.CONSUMER, order = -10000)
|
||||
public class ConsumerServiceAccountAuthFilter implements Filter {
|
||||
|
||||
private final ServiceIdentitySource serviceIdentitySource;
|
||||
|
||||
public ConsumerServiceAccountAuthFilter(ApplicationModel applicationModel) {
|
||||
this.serviceIdentitySource = applicationModel.getAdaptiveExtension(ServiceIdentitySource.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
String token = serviceIdentitySource.getToken(invoker.getUrl());
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
// TODO Attach it based on protocol can work better with other systems,
|
||||
// like standard HTTP cookie/authorization header
|
||||
invocation.setObjectAttachment(ID_TOKEN_KEY, token);
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authz;
|
||||
|
||||
import org.apache.dubbo.xds.security.authz.rule.RequestAuthProperty;
|
||||
|
||||
public interface RequestCredential {
|
||||
|
||||
Object get(RequestAuthProperty propertyType);
|
||||
|
||||
void add(RequestAuthProperty propertyType, Object value);
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authz;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.xds.security.api.AuthorizationException;
|
||||
import org.apache.dubbo.xds.security.api.RequestAuthorizer;
|
||||
import org.apache.dubbo.xds.security.authz.resolver.CredentialResolver;
|
||||
import org.apache.dubbo.xds.security.authz.rule.CommonRequestCredential;
|
||||
import org.apache.dubbo.xds.security.authz.rule.source.RuleFactory;
|
||||
import org.apache.dubbo.xds.security.authz.rule.source.RuleProvider;
|
||||
import org.apache.dubbo.xds.security.authz.rule.tree.RuleNode.Relation;
|
||||
import org.apache.dubbo.xds.security.authz.rule.tree.RuleRoot;
|
||||
import org.apache.dubbo.xds.security.authz.rule.tree.RuleRoot.Action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.micrometer.core.instrument.config.validate.ValidationException;
|
||||
|
||||
@Activate
|
||||
public class RoleBasedAuthorizer implements RequestAuthorizer {
|
||||
|
||||
private final RuleProvider<?> ruleProvider;
|
||||
|
||||
private final List<CredentialResolver> credentialResolver;
|
||||
|
||||
private final RuleFactory ruleFactory;
|
||||
|
||||
private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RoleBasedAuthorizer.class);
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Cached rules
|
||||
* Connection Identity -> Authorization Rules
|
||||
* Here are two problems:
|
||||
* 1.How to identify remote connection (may we can use [protocol:port])
|
||||
* 2.How to remove cache when remote connection is disconnected
|
||||
*/
|
||||
private final Map<String, List<RuleRoot>> rules = new ConcurrentHashMap<>();
|
||||
|
||||
public RoleBasedAuthorizer(ApplicationModel applicationModel) {
|
||||
this.ruleProvider = applicationModel.getAdaptiveExtension(RuleProvider.class);
|
||||
this.credentialResolver = applicationModel.getActivateExtensions(CredentialResolver.class);
|
||||
this.ruleFactory = applicationModel.getAdaptiveExtension(RuleFactory.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void validate(Invocation invocation) throws AuthorizationException {
|
||||
|
||||
List rulesSources = ruleProvider.getSource(invocation.getInvoker().getUrl(), invocation);
|
||||
List<RuleRoot> roots = ruleFactory.getRules(invocation.getInvoker().getUrl(), rulesSources);
|
||||
|
||||
List<RuleRoot> logRules = roots.stream()
|
||||
.filter(root -> root.getAction().equals(Action.LOG))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
roots.removeAll(logRules);
|
||||
|
||||
List<RuleRoot> andRules = roots.stream()
|
||||
.filter(root -> Relation.AND.equals(root.getRelation()))
|
||||
.collect(Collectors.toList());
|
||||
List<RuleRoot> orRules = roots.stream()
|
||||
.filter(root -> Relation.OR.equals(root.getRelation()))
|
||||
.collect(Collectors.toList());
|
||||
List<RuleRoot> notRules = roots.stream()
|
||||
.filter(root -> Relation.NOT.equals(root.getRelation()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
RequestCredential requestCredential = new CommonRequestCredential();
|
||||
credentialResolver.forEach(resolver ->
|
||||
resolver.appendRequestCredential(invocation.getInvoker().getUrl(), invocation, requestCredential));
|
||||
|
||||
AuthorizationRequestContext context = new AuthorizationRequestContext(invocation, requestCredential);
|
||||
|
||||
if (!logRules.isEmpty()) {
|
||||
context.startTrace();
|
||||
context.addTraceInfo(":::Start validation trace for request ["
|
||||
+ invocation.getInvoker().getUrl() + "], credentials=[" + invocation.getAttachments() + "] :::");
|
||||
|
||||
for (RuleRoot logRule : logRules) {
|
||||
boolean result;
|
||||
try {
|
||||
result = logRule.evaluate(context);
|
||||
context.addTraceInfo("::: Request " + (result ? "meet" : "does not meet") + " rule ["
|
||||
+ logRule.getNodeName() + "] ::: ");
|
||||
} catch (ValidationException e) {
|
||||
context.addTraceInfo(
|
||||
"::: Got Exception evaluating rule [" + logRule.getNodeName() + "] , exception=" + e);
|
||||
}
|
||||
}
|
||||
context.addTraceInfo("::: End validation trace :::");
|
||||
context.endTrace();
|
||||
logger.info(context.getTraceInfo());
|
||||
}
|
||||
|
||||
for (RuleRoot rule : notRules) {
|
||||
try {
|
||||
if (rule.evaluate(context) && rule.getAction().boolVal()) {
|
||||
throw new AuthorizationException(
|
||||
"Request authorization failed: request credential meet one of NOT rules.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Request authorization failed, source:" + invocation.getServiceName() + // TODO get source
|
||||
", target URL:"
|
||||
+ invocation.getInvoker().getUrl(),
|
||||
e.getCause());
|
||||
if (e instanceof AuthorizationException) {
|
||||
throw (AuthorizationException) e;
|
||||
}
|
||||
throw new AuthorizationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (RuleRoot rule : andRules) {
|
||||
try {
|
||||
if (!rule.evaluate(context) && rule.getAction().boolVal()) {
|
||||
throw new AuthorizationException(
|
||||
"Request authorization failed: request credential doesn't meet all AND rules.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Request authorization failed, source:" + invocation.getServiceName() + // TODO get source
|
||||
", target URL:"
|
||||
+ invocation.getInvoker().getUrl(),
|
||||
e.getCause());
|
||||
if (e instanceof AuthorizationException) {
|
||||
throw (AuthorizationException) e;
|
||||
}
|
||||
throw new AuthorizationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean orRes = false;
|
||||
for (RuleRoot rule : orRules) {
|
||||
try {
|
||||
orRes = rule.evaluate(context) && rule.getAction().boolVal();
|
||||
if (orRes) {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Request authorization failed, source:" + invocation.getServiceName() + // TODO source
|
||||
", target URL:"
|
||||
+ invocation.getInvoker().getUrl(),
|
||||
e.getCause());
|
||||
if (e instanceof AuthorizationException) {
|
||||
throw (AuthorizationException) e;
|
||||
}
|
||||
throw new AuthorizationException(e);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isEmpty(orRules)) {
|
||||
orRes = true;
|
||||
}
|
||||
if (orRes) {
|
||||
return;
|
||||
}
|
||||
throw new AuthorizationException(
|
||||
"Request authorization failed: request credential doesn't meet any required " + "OR rules.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.dubbo.xds.security.authz.resolver;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.remoting.api.ChannelContextListener;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcContextAttachment;
|
||||
import org.apache.dubbo.xds.security.authz.RequestCredential;
|
||||
import org.apache.dubbo.xds.security.authz.rule.RequestAuthProperty;
|
||||
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.SslHandler;
|
||||
|
||||
@Activate(order = -20)
|
||||
public class ConnectionCredentialResolver implements CredentialResolver, ChannelContextListener {
|
||||
|
||||
private final Map<String, ConnectionCredential> connectionInfos = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ErrorTypeAwareLogger logger =
|
||||
LoggerFactory.getErrorTypeAwareLogger(ConnectionCredentialResolver.class);
|
||||
|
||||
@Override
|
||||
public void appendRequestCredential(URL url, Invocation invocation, RequestCredential requestCredential) {
|
||||
requestCredential.add(
|
||||
RequestAuthProperty.TARGET_VERSION,
|
||||
invocation.getInvoker().getUrl().getVersion());
|
||||
RpcContextAttachment serverContext = RpcContext.getServerContext();
|
||||
requestCredential.add(
|
||||
RequestAuthProperty.DIRECT_REMOTE_IP,
|
||||
serverContext.getRemoteAddress().getHostName());
|
||||
requestCredential.add(RequestAuthProperty.REMOTE_PORT, serverContext.getRemotePort());
|
||||
requestCredential.add(RequestAuthProperty.REMOTE_APPLICATION, serverContext.getRemoteApplicationName());
|
||||
requestCredential.add(RequestAuthProperty.REMOTE_GROUP, serverContext.getGroup());
|
||||
requestCredential.add(RequestAuthProperty.DESTINATION_IP, serverContext.getLocalHost());
|
||||
requestCredential.add(RequestAuthProperty.DESTINATION_PORT, serverContext.getLocalPort());
|
||||
|
||||
ConnectionCredential credential = connectionInfos.get(url.getIp() + ":" + url.getPort());
|
||||
if (credential != null) {
|
||||
requestCredential.add(RequestAuthProperty.CONNECTION_CREDENTIAL, credential);
|
||||
requestCredential.add(RequestAuthProperty.REQUESTED_SERVER_NAME, credential.getSni());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(Object channelContext) {
|
||||
if (channelContext instanceof ChannelHandlerContext) {
|
||||
ChannelHandlerContext context = (ChannelHandlerContext) channelContext;
|
||||
SslHandler sslHandler = context.pipeline().get(SslHandler.class);
|
||||
if (sslHandler != null) {
|
||||
SSLSession sslSession = sslHandler.engine().getSession();
|
||||
String applicationProtocol = sslSession.getProtocol();
|
||||
try {
|
||||
Certificate[] peerCertificates = sslSession.getPeerCertificates();
|
||||
List<CertificateCredential> certCredentialList = new ArrayList<>(1);
|
||||
for (Certificate certificate : peerCertificates) {
|
||||
if (!(certificate instanceof X509Certificate)) {
|
||||
logger.warn(
|
||||
"99-1",
|
||||
"",
|
||||
"",
|
||||
"One SSL certificate was ignored because it's not in X.509 format: " + certificate);
|
||||
continue;
|
||||
}
|
||||
certCredentialList.add(new CertificateCredential((X509Certificate) certificate));
|
||||
}
|
||||
String remoteAddress = NetUtils.toAddressString(
|
||||
(InetSocketAddress) context.channel().remoteAddress());
|
||||
String sniHostName = sslSession.getPeerHost();
|
||||
connectionInfos.put(
|
||||
remoteAddress,
|
||||
new ConnectionCredential(certCredentialList, applicationProtocol, sniHostName));
|
||||
} catch (Exception e) {
|
||||
logger.warn("99-1", "", "", "Got exception when resolving certificate from SSL session", e);
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No SSL/TLS handler found in pipeline:"
|
||||
+ NetUtils.toAddressString(
|
||||
(InetSocketAddress) context.channel().remoteAddress()) + "-> "
|
||||
+ NetUtils.toAddressString(
|
||||
(InetSocketAddress) context.channel().localAddress()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(Object channelContext) {
|
||||
if (channelContext instanceof ChannelHandlerContext) {
|
||||
ChannelHandlerContext context = (ChannelHandlerContext) channelContext;
|
||||
String remoteAddress = NetUtils.toAddressString(
|
||||
(InetSocketAddress) context.channel().remoteAddress());
|
||||
connectionInfos.remove(remoteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConnectionCredential {
|
||||
private final List<CertificateCredential> certificateCredentials;
|
||||
private final String applicationProtocol;
|
||||
private final String sni;
|
||||
|
||||
public ConnectionCredential(
|
||||
List<CertificateCredential> certificateCredentials, String applicationProtocol, String sni) {
|
||||
this.certificateCredentials = certificateCredentials;
|
||||
this.applicationProtocol = applicationProtocol;
|
||||
this.sni = sni;
|
||||
}
|
||||
|
||||
public List<CertificateCredential> getCertificateCredentials() {
|
||||
return certificateCredentials;
|
||||
}
|
||||
|
||||
public String getApplicationProtocol() {
|
||||
return applicationProtocol;
|
||||
}
|
||||
|
||||
public String getSni() {
|
||||
return sni;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CertificateCredential {
|
||||
private final X509Certificate certificate;
|
||||
private final String subject;
|
||||
private final String issuer;
|
||||
private final Map<SANType, List<Object>> subjectAltNames;
|
||||
private final Date certNotBefore;
|
||||
private final Date certNotAfter;
|
||||
private final String signatureAlgorithmName;
|
||||
private final String publicKeyAlgorithmName;
|
||||
private final Set<String> criticalExtensionOIDs;
|
||||
private final List<String> extendedKeyUsage;
|
||||
|
||||
public CertificateCredential(X509Certificate cert) throws Exception {
|
||||
this.subject = cert.getSubjectX500Principal().getName();
|
||||
this.issuer = cert.getIssuerX500Principal().toString();
|
||||
this.certNotBefore = cert.getNotBefore();
|
||||
this.certNotAfter = cert.getNotAfter();
|
||||
this.subjectAltNames = extractDetailedFields(cert);
|
||||
this.signatureAlgorithmName = cert.getSigAlgName();
|
||||
this.publicKeyAlgorithmName = cert.getPublicKey().getAlgorithm();
|
||||
this.criticalExtensionOIDs = cert.getCriticalExtensionOIDs();
|
||||
// e.g., TLS Web Server Authentication, TLS Web Client Authentication
|
||||
this.extendedKeyUsage = cert.getExtendedKeyUsage();
|
||||
this.certificate = cert;
|
||||
}
|
||||
|
||||
private Map<SANType, List<Object>> extractDetailedFields(X509Certificate cert) throws Exception {
|
||||
Collection<List<?>> subjectAltNames = cert.getSubjectAlternativeNames();
|
||||
if (subjectAltNames != null) {
|
||||
Map<SANType, List<Object>> sanMap = new HashMap<>();
|
||||
for (List<?> sanItem : subjectAltNames) {
|
||||
SANType type = SANType.map((Integer) sanItem.get(0));
|
||||
Object value = sanItem.get(1);
|
||||
sanMap.computeIfAbsent(type, k -> new ArrayList<>()).add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
public Map<SANType, List<Object>> getSubjectAltNames() {
|
||||
return subjectAltNames;
|
||||
}
|
||||
|
||||
public List<String> getExtendedKeyUsage() {
|
||||
return extendedKeyUsage;
|
||||
}
|
||||
|
||||
public Set<String> getCriticalExtensionOIDs() {
|
||||
return criticalExtensionOIDs;
|
||||
}
|
||||
|
||||
public String getPublicKeyAlgorithmName() {
|
||||
return publicKeyAlgorithmName;
|
||||
}
|
||||
|
||||
public String getSignatureAlgorithmName() {
|
||||
return signatureAlgorithmName;
|
||||
}
|
||||
|
||||
public Date getCertNotAfter() {
|
||||
return certNotAfter;
|
||||
}
|
||||
|
||||
public Date getCertNotBefore() {
|
||||
return certNotBefore;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public X509Certificate getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SANType {
|
||||
OTHER_NAME(0),
|
||||
RFC_822_NAME(1),
|
||||
DNS_NAME(2),
|
||||
X400_ADDRESS(3),
|
||||
DIRECTORY_NAME(4),
|
||||
EDI_PARTY_NAME(5),
|
||||
URI(6),
|
||||
IP_ADDRESS(7),
|
||||
REGISTERED_ID(8);
|
||||
|
||||
private final int value;
|
||||
|
||||
SANType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static SANType map(int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return OTHER_NAME;
|
||||
case 1:
|
||||
return RFC_822_NAME;
|
||||
case 2:
|
||||
return DNS_NAME;
|
||||
case 3:
|
||||
return X400_ADDRESS;
|
||||
case 4:
|
||||
return DIRECTORY_NAME;
|
||||
case 5:
|
||||
return EDI_PARTY_NAME;
|
||||
case 6:
|
||||
return URI;
|
||||
case 7:
|
||||
return IP_ADDRESS;
|
||||
case 8:
|
||||
return REGISTERED_ID;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown SAN value: " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue