未验证 提交 0b67d299 编写于 作者: D Daming 提交者: GitHub

Support mTLS for gRPC channel (#7565)

上级 ee16a1d5
......@@ -40,7 +40,7 @@ jobs:
timeout-minutes: 90
strategy:
matrix:
case: [auth, ssl]
case: [auth, ssl, mtls]
env:
SW_SIMPLE_CASE: ${{ matrix.case }}
steps:
......
......@@ -51,6 +51,7 @@ Release Notes.
* Fix `ZookeeperConfigWatcherRegister.readConfig()` could cause `NPE` when `data.getData()` is null.
* Support nacos grouped dynamic configurations.
* Support for filter function filtering of int type values.
* Support mTLS for gRPC channel.
#### UI
......
# gRPC SSL transportation support for OAP server
For OAP communication, we are currently using gRPC, a multi-platform RPC framework that uses protocol buffers for message serialization. The nice part about gRPC is that it promotes the use of SSL/TLS to authenticate and encrypt exchanges. Now OAP supports enabling SSL transportation for gRPC receivers. Since 8.8.0, OAP supports enabling mutual TLS authentication between probes and OAP servers.
To enable this feature, follow the steps below.
## Preparation
By default, the communication between OAP nodes and the communication between receiver and probe share the same gRPC server. Its configuration is in `application.yml/core/default` section.
The advanced gRPC receiver is only for communication with the probes. This configuration is in `application.yml/receiver-sharing-server/default` section.
The first step is to generate certificates and private key files for encrypting communication.
### Creating SSL/TLS Certificates
The first step is to generate certificates and key files for encrypting communication. This is fairly straightforward: use `openssl` from the command line.
Use this [script](../../../../tools/TLS/tls_key_generate.sh) if you are not familiar with how to generate key files.
We need the following files:
* `ca.crt`: A certificate authority public key for a client to validate the server's certificate.
* `server.pem`, `client.pem`: A private RSA key to sign and authenticate the public key. It's either a PKCS#8(PEM) or PKCS#1(DER).
* `server.crt`, `client.crt`: Self-signed X.509 public keys for distribution.
## TLS on OAP servers
By default, the communication between OAP nodes and the communication between receiver and probe share the same gRPC server. That means once you enabling SSL for receivers and probes, the OAP nodes will enable it too.
**NOTE**: SkyWalking **does not** support to enable mTLS on `OAP server nodes communication`. That means you have to enable `receiver-sharing-server` for enabling mTLS on communication between probes ang OAP servers. More details see [Enable mTLS mode on gRPC receiver](#Enable_mTLS_mode_on_gRPC_receiver).
You can enable gRPC SSL by adding the following lines to `application.yml/core/default`.
```yaml
gRPCSslEnabled: true
gRPCSslKeyPath: /path/to/server.pem
gRPCSslCertChainPath: /path/to/server.crt
gRPCSslTrustedCAPath: /path/to/ca.crt
```
`gRPCSslKeyPath` and `gRPCSslCertChainPath` are loaded by the OAP server to encrypt communication. `gRPCSslTrustedCAPath`
helps the gRPC client to verify server certificates in cluster mode.
> There is a gRPC client and server in every OAP server node. The gRPC client comunicates with OAP servers in cluster mode. They are sharing the core module configuration.
**When new files are in place, they can be loaded dynamically, and you won't have to restart an OAP instance.**
## Enable TLS on independent gRPC receiver
If you enable `receiver-sharing-server` to ingest data from an external source, add the following lines to `application.yml/receiver-sharing-server/default`:
```yaml
gRPCPort: ${SW_RECEIVER_GRPC_PORT:"changeMe"}
gRPCSslEnabled: true
gRPCSslKeyPath: /path/to/server.pem
gRPCSslCertChainPath: /path/to/server.crt
```
Since `recevier-sharing-server` only receives data from an external source, it doesn't need a CA at all. But you have to configure the CA for the clients, such as [Java agent](http://github.com/apache/skywalking-java), [Satellite](http://github.com/apache/skywalking-satellite). If you port to Java agent, refer to [the Java agent repo](http://github.com/apache/skywalking-java) to configure java agent and enable TLS.
**NOTE**: change the `SW_RECEIVER_GRPC_PORT` as non-zore to enable `receiver-sharing-server`. And the port is open for the clients.
### Enable mTLS mode on gRPC receiver
Since 8.8.0, SkyWalking supports enable mutual TLS authentication for transporting between clients and OAP servers. To enable `mTLS` mode for gRPC channel requires [Sharing gRPC Server](./backend-receivers.md/#grpchttp-server-for-receiver) enabled, as the following configuration.
```yaml
receiver-sharing-server:
selector: ${SW_RECEIVER_SHARING_SERVER:default}
default:
# For gRPC server
gRPCHost: ${SW_RECEIVER_GRPC_HOST:0.0.0.0}
gRPCPort: ${SW_RECEIVER_GRPC_PORT:"changeMe"}
maxConcurrentCallsPerConnection: ${SW_RECEIVER_GRPC_MAX_CONCURRENT_CALL:0}
maxMessageSize: ${SW_RECEIVER_GRPC_MAX_MESSAGE_SIZE:0}
gRPCThreadPoolQueueSize: ${SW_RECEIVER_GRPC_POOL_QUEUE_SIZE:0}
gRPCThreadPoolSize: ${SW_RECEIVER_GRPC_THREAD_POOL_SIZE:0}
gRPCSslEnabled: ${SW_RECEIVER_GRPC_SSL_ENABLED:true}
gRPCSslKeyPath: ${SW_RECEIVER_GRPC_SSL_KEY_PATH:"/path/to/server.pem"}
gRPCSslCertChainPath: ${SW_RECEIVER_GRPC_SSL_CERT_CHAIN_PATH:"/path/to/server.crt"}
gRPCSslTrustedCAsPath: ${SW_RECEIVER_GRPC_SSL_TRUSTED_CAS_PATH:"/path/to/ca.crt"}
authentication: ${SW_AUTHENTICATION:""}
```
You can still use this [script](../../../../tools/TLS/tls_key_generate.sh) to generate CA certificate and the key files of server-side(for OAP Server) and client-side(for Agent/Satellite).
You have to notice the keys, including server and client-side, are from the same CA certificate.
# gRPC SSL transportation support for OAP server
For OAP communication, we are currently using gRPC, a multi-platform RPC framework that uses protocol buffers for
message serialization. The nice part about gRPC is that it promotes the use of SSL/TLS to authenticate and encrypt
exchanges. Now OAP supports enabling SSL transportation for gRPC receivers.
To enable this feature, follow the steps below.
## Creating SSL/TLS Certificates
The first step is to generate certificates and key files for encrypting communication. This is
fairly straightforward: use `openssl` from the command line.
Use this [script](../../../../tools/TLS/tls_key_generate.sh) if you are not familiar with how to generate key files.
We need the following files:
- `server.pem`: A private RSA key to sign and authenticate the public key. It's either a PKCS#8(PEM) or PKCS#1(DER).
- `server.crt`: Self-signed X.509 public keys for distribution.
- `ca.crt`: A certificate authority public key for a client to validate the server's certificate.
## Config OAP server
You can enable gRPC SSL by adding the following lines to `application.yml/core/default`.
```json
gRPCSslEnabled: true
gRPCSslKeyPath: /path/to/server.pem
gRPCSslCertChainPath: /path/to/server.crt
gRPCSslTrustedCAPath: /path/to/ca.crt
```
`gRPCSslKeyPath` and `gRPCSslCertChainPath` are loaded by the OAP server to encrypt communication. `gRPCSslTrustedCAPath`
helps the gRPC client to verify server certificates in cluster mode.
When new files are in place, they can be loaded dynamically, and you won't have to restart an OAP instance.
If you enable `sharding-server` to ingest data from an external source, add the following lines to `application.yml/receiver-sharing-server/default`:
```json
gRPCSslEnabled: true
gRPCSslKeyPath: /path/to/server.pem
gRPCSslCertChainPath: /path/to/server.crt
```
Since `sharding-server` only receives data from an external source, it doesn't need a CA at all.
If you port to Java agent, refer to [the Java agent repo](http://github.com/apache/skywalking-java) to config java agent and enable TLS.
......@@ -91,6 +91,8 @@ catalog:
path: "/en/setup/backend/ttl"
- name: "Dynamical Logging"
path: "/en/setup/backend/dynamical-logging"
- name: "Security(SSL/TLS/mTLS)"
path: "/en/setup/backend/grpc-security"
- name: "Deploy In Kubernetes"
path: "/en/setup/backend/backend-k8s"
- name: "Tracing"
......
......@@ -312,6 +312,7 @@ receiver-sharing-server:
gRPCSslEnabled: ${SW_RECEIVER_GRPC_SSL_ENABLED:false}
gRPCSslKeyPath: ${SW_RECEIVER_GRPC_SSL_KEY_PATH:""}
gRPCSslCertChainPath: ${SW_RECEIVER_GRPC_SSL_CERT_CHAIN_PATH:""}
gRPCSslTrustedCAsPath: ${SW_RECEIVER_GRPC_SSL_TRUSTED_CAS_PATH:""}
authentication: ${SW_AUTHENTICATION:""}
receiver-register:
selector: ${SW_RECEIVER_REGISTER:default}
......
......@@ -193,7 +193,8 @@ public class CoreModuleProvider extends ModuleProvider {
if (moduleConfig.isGRPCSslEnabled()) {
grpcServer = new GRPCServer(moduleConfig.getGRPCHost(), moduleConfig.getGRPCPort(),
moduleConfig.getGRPCSslCertChainPath(),
moduleConfig.getGRPCSslKeyPath()
moduleConfig.getGRPCSslKeyPath(),
null
);
} else {
grpcServer = new GRPCServer(moduleConfig.getGRPCHost(), moduleConfig.getGRPCPort());
......
......@@ -49,6 +49,7 @@ public class GRPCServer implements Server {
private NettyServerBuilder nettyServerBuilder;
private String certChainFile;
private String privateKeyFile;
private String trustedCAsFile;
private DynamicSslContext sslContext;
private int threadPoolSize = Runtime.getRuntime().availableProcessors() * 4;
private int threadPoolQueueSize = 10000;
......@@ -82,10 +83,11 @@ public class GRPCServer implements Server {
* @param certChainFile `server.crt` file
* @param privateKeyFile `server.pem` file
*/
public GRPCServer(String host, int port, String certChainFile, String privateKeyFile) {
public GRPCServer(String host, int port, String certChainFile, String privateKeyFile, String trustedCAsFile) {
this(host, port);
this.certChainFile = certChainFile;
this.privateKeyFile = privateKeyFile;
this.trustedCAsFile = trustedCAsFile;
}
@Override
......@@ -101,7 +103,7 @@ public class GRPCServer implements Server {
@Override
public void initialize() {
InetSocketAddress address = new InetSocketAddress(host, port);
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(threadPoolQueueSize);
ArrayBlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(threadPoolQueueSize);
ExecutorService executor = new ThreadPoolExecutor(
threadPoolSize, threadPoolSize, 60, TimeUnit.SECONDS, blockingQueue,
new CustomThreadFactory("grpcServerPool"), new CustomRejectedExecutionHandler()
......@@ -111,7 +113,7 @@ public class GRPCServer implements Server {
.maxInboundMessageSize(maxMessageSize)
.executor(executor);
if (!Strings.isNullOrEmpty(privateKeyFile) && !Strings.isNullOrEmpty(certChainFile)) {
sslContext = DynamicSslContext.forServer(privateKeyFile, certChainFile);
sslContext = DynamicSslContext.forServer(privateKeyFile, certChainFile, trustedCAsFile);
nettyServerBuilder.sslContext(sslContext);
}
log.info("Server started, host {} listening on {}", host, port);
......
......@@ -19,13 +19,15 @@
package org.apache.skywalking.oap.server.library.server.grpc.ssl;
import io.grpc.netty.GrpcSslContexts;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLException;
import org.apache.skywalking.apm.util.StringUtil;
import org.apache.skywalking.oap.server.library.server.ssl.AbstractSslContext;
import org.apache.skywalking.oap.server.library.server.ssl.PrivateKeyUtil;
......@@ -34,16 +36,18 @@ import org.apache.skywalking.oap.server.library.server.ssl.PrivateKeyUtil;
*/
public class DynamicSslContext extends AbstractSslContext {
public static DynamicSslContext forServer(final String privateKeyFile, final String certChainFile) {
return new DynamicSslContext(privateKeyFile, certChainFile);
public static DynamicSslContext forServer(final String privateKeyFile,
final String certChainFile,
final String trustedCAsFile) {
return new DynamicSslContext(privateKeyFile, certChainFile, trustedCAsFile);
}
public static DynamicSslContext forClient(final String caFile) {
return new DynamicSslContext(caFile);
}
protected DynamicSslContext(String privateKeyFile, String certChainFile) {
super(privateKeyFile, certChainFile);
protected DynamicSslContext(String privateKeyFile, String certChainFile, String trustedCAsFile) {
super(privateKeyFile, certChainFile, trustedCAsFile);
}
protected DynamicSslContext(String caFile) {
......@@ -59,18 +63,24 @@ public class DynamicSslContext extends AbstractSslContext {
}
}
@Override
protected void updateContext(final String privateKeyFile, final String certChainFile) {
try {
setCtx(GrpcSslContexts
.configure(SslContextBuilder
.forServer(
new FileInputStream(Paths.get(certChainFile).toFile()),
PrivateKeyUtil.loadDecryptionKey(privateKeyFile)),
SslProvider.OPENSSL)
.build());
} catch (GeneralSecurityException | IOException e) {
protected void updateContext(final String privateKeyFile, final String certChainFile, final String trustedCAsFile) {
try (InputStream cert = new FileInputStream(Paths.get(certChainFile).toFile());
InputStream key = PrivateKeyUtil.loadDecryptionKey(privateKeyFile)) {
SslContextBuilder builder = GrpcSslContexts.configure(
SslContextBuilder.forServer(cert, key),
SslProvider.OPENSSL
);
if (StringUtil.isNotEmpty(trustedCAsFile)) {
builder.trustManager(Paths.get(trustedCAsFile).toFile())
.clientAuth(ClientAuth.REQUIRE);
}
setCtx(builder.build());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
......@@ -34,13 +34,14 @@ public abstract class AbstractSslContext extends SslContext {
@Setter(AccessLevel.PROTECTED)
private volatile SslContext ctx;
protected AbstractSslContext(final String privateKeyFile, final String certChainFile) {
updateContext(privateKeyFile, certChainFile);
protected AbstractSslContext(final String privateKeyFile, final String certChainFile, final String trustedCAsFile) {
updateContext(privateKeyFile, certChainFile, trustedCAsFile);
monitor = new MultipleFilesChangeMonitor(
10,
readableContents -> updateContext(privateKeyFile, certChainFile),
readableContents -> updateContext(privateKeyFile, certChainFile, trustedCAsFile),
certChainFile,
privateKeyFile);
privateKeyFile,
trustedCAsFile);
}
protected AbstractSslContext(final String caFile) {
......@@ -53,7 +54,7 @@ public abstract class AbstractSslContext extends SslContext {
protected abstract void updateContext(String caFile);
protected abstract void updateContext(final String privateKeyFile, final String certChainFile);
protected abstract void updateContext(final String privateKeyFile, final String certChainFile, final String trustedCAsFile);
public void start() {
monitor.start();
......
......@@ -21,8 +21,8 @@ package org.apache.skywalking.oap.server.library.server.ssl;
import io.netty.handler.ssl.SslContextBuilder;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLException;
public class HttpDynamicSslContext extends AbstractSslContext {
......@@ -36,7 +36,7 @@ public class HttpDynamicSslContext extends AbstractSslContext {
}
protected HttpDynamicSslContext(String privateKeyFile, String certChainFile) {
super(privateKeyFile, certChainFile);
super(privateKeyFile, certChainFile, null);
}
protected HttpDynamicSslContext(String caFile) {
......@@ -53,13 +53,12 @@ public class HttpDynamicSslContext extends AbstractSslContext {
}
@Override
protected void updateContext(final String privateKeyFile, final String certChainFile) {
try {
setCtx(SslContextBuilder
.forServer(
new FileInputStream(Paths.get(certChainFile).toFile()),
PrivateKeyUtil.loadDecryptionKey(privateKeyFile)).build());
} catch (GeneralSecurityException | IOException e) {
protected void updateContext(final String privateKeyFile, final String certChainFile, final String trustedCAsFile) {
try (InputStream cert = new FileInputStream(Paths.get(certChainFile).toFile());
InputStream key = PrivateKeyUtil.loadDecryptionKey(privateKeyFile)) {
setCtx(SslContextBuilder.forServer(cert, key).build());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
......
......@@ -24,7 +24,6 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Base64;
/**
......@@ -39,7 +38,7 @@ public class PrivateKeyUtil {
/**
* Load a RSA decryption key from a file (PEM or DER).
*/
public static InputStream loadDecryptionKey(String keyFilePath) throws GeneralSecurityException, IOException {
public static InputStream loadDecryptionKey(String keyFilePath) throws IOException {
byte[] keyDataBytes = Files.readAllBytes(Paths.get(keyFilePath));
String keyDataString = new String(keyDataBytes, StandardCharsets.UTF_8);
......@@ -58,7 +57,7 @@ public class PrivateKeyUtil {
* Create a InputStream instance from raw PKCS#1 bytes. Raw Java API can't recognize ASN.1 format, so we should
* convert it into a pkcs#8 format Java can understand.
*/
private static InputStream readPkcs1PrivateKey(byte[] pkcs1Bytes) throws GeneralSecurityException {
private static InputStream readPkcs1PrivateKey(byte[] pkcs1Bytes) {
int pkcs1Length = pkcs1Bytes.length;
int totalLength = pkcs1Length + 22;
byte[] pkcs8Header = new byte[] {
......
......@@ -50,6 +50,7 @@ public class SharingServerConfig extends ModuleConfig {
private boolean gRPCSslEnabled = false;
private String gRPCSslKeyPath;
private String gRPCSslCertChainPath;
private String gRPCSslTrustedCAsPath;
/**
* The maximum size in bytes allowed for request headers.
......
......@@ -103,7 +103,8 @@ public class SharingServerModuleProvider extends ModuleProvider {
Strings.isBlank(config.getGRPCHost()) ? "0.0.0.0" : config.getGRPCHost(),
config.getGRPCPort(),
config.getGRPCSslCertChainPath(),
config.getGRPCSslKeyPath()
config.getGRPCSslKeyPath(),
config.getGRPCSslTrustedCAsPath()
);
} else {
grpcServer = new GRPCServer(
......
-----BEGIN CERTIFICATE-----
MIIEmDCCAoACCQCHw9DXRSuLGDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANv
YXAwHhcNMjEwODI2MDIzOTM0WhcNMjIwODI2MDIzOTM0WjAOMQwwCgYDVQQDDANv
YXAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDBrmOmHjb3OrbXXbF8
09uuV8f8ITI1H1AceoD6Ak1yovanCxvvCvcKgZ4hLtPlUldg8fBqo3SivyzYbd4N
082K8yYeoGHBKSaW5ckO126IHAzMHLCzw5qDgqTHHoovWQVv5VKCJC2y4xpeeakY
ifk2DWPmou/rkKzZyflWMn7b0+bi81IQJgDz01UF19OAXXx4jp53yCKPwp+Que9R
AIrlYXuBHkpwbDDs7vtCMSa/j+Klw8eAcZAm1NO3Y8VpG+uMxTFfVLr0nGvKMEKO
0P3q1mp2NML/sNP89RHZsXM/eYU1UE0Da1aQ/o+fivXs3CRStxwFMwCMg/+uXccT
Raufz0OhEwRJa0zbn8XBvcvLJ7kmXio3df23fSPT/i9whyQujfXE7cAraOEj8KjI
NUDTi6dASlUeoNoKsklB4P3UIUCwCvZu9PqU9v7Oz8tOsxNpGvY2oBA+tVwZ1U/p
ON992LmGZz2J9xpDjojCddUS5tRZGD0itMWlJiLaRd7VSPLRuyUGLqbEjf/7GffO
Tb/5R7DhcKSFdrNXisz/kjU4qSwYFJ9WzUbbraV3MNiJiRa0eHGbLffHKVIvVcPI
scaP04nTTbX7l4qUPmX/YcTNEZV0HDC3s0Qxk0y6lXxu3rDue1yx+N+S0TwCSq8D
hpIDLWi/fYLbAoYnqv5jJxbnLQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQB84/85
KA2S2iXHhN1bkEx0cIjkg2t16Ht+b602na4bmZIpxUVzUv6dqiwCx5wDyM96T3OX
xRDO8uVQEUMJ+76dm53UFZ9uhtGBvSFc8YqnL+EoXD1IQ8IQ7PVVf4dyzKyxhtK2
XHr6RPtDnLhHy6mxQ5eaqBTaZ1dLoQlAIPyzh2mX2px+jGu9DKQxrZQ+0aMklidW
xSDLLGr7cK+tQln4P0DC61uLS/VGQ+15a6XpqF3FhhspJrQPse+WSC015KCdSgnZ
dIDxqtPS+hhyw5o4C39KDHD0x04VVIEhMsX3Z2eykK0KXnnsPcXWdFj/pR55Kcr3
qPfxGRLrEeYvoUUzr2APiHTPzWsUDwugfV9KXyGYuxIqqOpPr5nWqGfo9ZcvbH7K
Mqc32DFvFdNnUqMYQHu9oigjfCvscdNTrWHlGYD3HJmWZur4IBReb8uG5qPN+VLH
1xs7EcW6NN6A1pndHEosAFcVIh5kx4gRCsZMsEdGExn4gkTknfBosWo0tROV00UU
UYFqLZfUmJyPDVYZf1fjiLd+yC2qJERNxWWy03JG5UZDLc03ULPlqys+MCT8rRdD
b7p2WGOQBM9whNC5BC6SydFPphwc3yhR1jmMSz+QB18Easo7RV6fmhE462wj1EoH
+IDtJX6JRGguky29ibdolQUiRK3rVowGpIjjgw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIElTCCAn0CAQEwDQYJKoZIhvcNAQEFBQAwDjEMMAoGA1UEAwwDb2FwMB4XDTIx
MDgyNjAyMzkzN1oXDTIyMDgyNjAyMzkzN1owEzERMA8GA1UEAwwIcHJvdmlkZXIw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDnXZ54vJNK/xAwrunMJf2l
DgdsKfdAkW70LpzAFVq/lXjZXM0FxG4Kl/pR0cTf69MdhNxEQffyv42bjAEDIWQ/
FhtPa0mApLAd0gAGZ920ANS6XRw9QV4UujxOzTyivxOHU4LQ8s+xo6X3jZQ6rzdM
KeFYu4uEDZj7J0aN4d68HDhAiQFtmDxJ4AOyMpQ+6VzuC8RslcKXeO1mhzHbbYwN
e1cMaLMzJpcgw7mpTF3iGkiSO5dEbTmEdmEdTDikJtoQvB7YISMeKeFe1BE19ccF
n06mPh7fgBEJsBZmkPflpksjFG9VTbdC0qiz4NIA/t1Sjig65p6uEOkV1KwyTzxp
53P962prYw/yYtCftgPtrQeW9u7xJ81MkR4WLi9ut6UKyHSsgZwRtK4nInt+avDc
3CPHGuZfSg7Cf8Ll6dUc7UGVJoFCY3zZjeZ+JcxD52hPfOXBmsG1QF1LUBO0+Dwl
snwJVIeRTRtVgMp64iKzvjpqAU/ydZNr7JbBYMw9kLSi/Lv8UDfXyB2h0fmWLXOB
eWSfoNZTUlIS+D03Q6UwbH45VBiYz0+PWN7ywa1uB3igQrbUdiweYgaErjUtKthB
xg4ZlN3xkccon1MsM9YuX1jlj2IeCWFiLFDwU17V0V263ETRN9NgUZF/JpoJEmb0
PE4aKRq/M8CRMvPn5Ga4HQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQALy+gfWHtf
+6jP7OJRCpYuNrJFsvowp6WmFqc9P0XE10Ux9LL6NuDJ0TQE6361qqIE1oy263bV
2zTKg3+MwRbVsxDvK7smxBxdeDGQILFXWzfJIMcJTjd8sEUQ05fN/nJx8obYHcz1
YDntOrFH3azTL5SbrUMuaxhWIWf4lQXXN8EzE3IP0x0cgGk5ULaIim8sTh7t2+k9
+Sck6z7PB7u8gCMLBtTfyxgwS5MFT5zJAQmOqg/0bNd9lVqWTqd1D94jjsu1mnMc
nhRHqSO2WsIg6Z8YUofYQ0xelDpqXFkyN5MeiVU0iDuC93sjjG52bjFR0YKcKKYW
8fe4bIpt7XKKZsjyZqCuQGDvwjpBh7a2feElqlLGqDjKyhIEkENZv9+8i3L0Sor0
mrAkFf4THnGbxmrJyrp/89cc44scv0QHz6EKHTk1idfLc+HhL3a6vlqx0CxpupXi
K5Lz2UCs6DIoyqYI0vciaa9xVsxhIbfXasXPp1Jflz2k4NRn0LuS8WSQds+Sall2
A0GwKZjc7DkgKzsQaxk3BEi44XH8WaXDuFBQY5AEzQEGA/kcwKsKg0pggGt9Usrk
mmB5QDUDC0NeQKcCE45POcrUuNbfvMKoLjj+SlkOmcJUBjcDlroNCBSPDuRRZswS
eBPbdGuiYYZj52ImTm/ytg7ksehW+fGolg==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
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
7fL3g6gq14QLJJhYzF1FkGFQZw7iH2gvBg==
-----END PRIVATE KEY-----
# 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.
version: '2.1'
services:
oap:
extends:
file: ../../base-compose.yml
service: oap
volumes:
- ./server:/skywalking/certs
expose:
- 11811
environment:
SW_RECEIVER_GRPC_PORT: 11811
SW_RECEIVER_GRPC_SSL_ENABLED: "true"
SW_RECEIVER_GRPC_SSL_KEY_PATH: /skywalking/certs/server.pem
SW_RECEIVER_GRPC_SSL_CERT_CHAIN_PATH: /skywalking/certs/server.crt
SW_RECEIVER_GRPC_SSL_TRUSTED_CAS_PATH: /skywalking/certs/ca.crt
SW_PROMETHEUS_FETCHER: "default"
SW_TELEMETRY: prometheus
ui:
extends:
file: ../../base-compose.yml
service: ui
depends_on:
oap:
condition: service_healthy
provider:
extends:
file: ../../base-compose.yml
service: provider
volumes:
- ./client:/skywalking/agent/certs
environment:
SW_AGENT_COLLECTOR_BACKEND_SERVICES: oap:11811
SW_AGENT_SSL_KEY_PATH: /certs/client.pem
SW_AGENT_SSL_CERT_CHAIN_PATH: /certs/client.crt
SW_AGENT_SSL_TRUSTED_CA_PATH: /certs/ca.crt
depends_on:
oap:
condition: service_healthy
networks:
e2e:
-----BEGIN CERTIFICATE-----
MIIEmDCCAoACCQCHw9DXRSuLGDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANv
YXAwHhcNMjEwODI2MDIzOTM0WhcNMjIwODI2MDIzOTM0WjAOMQwwCgYDVQQDDANv
YXAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDBrmOmHjb3OrbXXbF8
09uuV8f8ITI1H1AceoD6Ak1yovanCxvvCvcKgZ4hLtPlUldg8fBqo3SivyzYbd4N
082K8yYeoGHBKSaW5ckO126IHAzMHLCzw5qDgqTHHoovWQVv5VKCJC2y4xpeeakY
ifk2DWPmou/rkKzZyflWMn7b0+bi81IQJgDz01UF19OAXXx4jp53yCKPwp+Que9R
AIrlYXuBHkpwbDDs7vtCMSa/j+Klw8eAcZAm1NO3Y8VpG+uMxTFfVLr0nGvKMEKO
0P3q1mp2NML/sNP89RHZsXM/eYU1UE0Da1aQ/o+fivXs3CRStxwFMwCMg/+uXccT
Raufz0OhEwRJa0zbn8XBvcvLJ7kmXio3df23fSPT/i9whyQujfXE7cAraOEj8KjI
NUDTi6dASlUeoNoKsklB4P3UIUCwCvZu9PqU9v7Oz8tOsxNpGvY2oBA+tVwZ1U/p
ON992LmGZz2J9xpDjojCddUS5tRZGD0itMWlJiLaRd7VSPLRuyUGLqbEjf/7GffO
Tb/5R7DhcKSFdrNXisz/kjU4qSwYFJ9WzUbbraV3MNiJiRa0eHGbLffHKVIvVcPI
scaP04nTTbX7l4qUPmX/YcTNEZV0HDC3s0Qxk0y6lXxu3rDue1yx+N+S0TwCSq8D
hpIDLWi/fYLbAoYnqv5jJxbnLQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQB84/85
KA2S2iXHhN1bkEx0cIjkg2t16Ht+b602na4bmZIpxUVzUv6dqiwCx5wDyM96T3OX
xRDO8uVQEUMJ+76dm53UFZ9uhtGBvSFc8YqnL+EoXD1IQ8IQ7PVVf4dyzKyxhtK2
XHr6RPtDnLhHy6mxQ5eaqBTaZ1dLoQlAIPyzh2mX2px+jGu9DKQxrZQ+0aMklidW
xSDLLGr7cK+tQln4P0DC61uLS/VGQ+15a6XpqF3FhhspJrQPse+WSC015KCdSgnZ
dIDxqtPS+hhyw5o4C39KDHD0x04VVIEhMsX3Z2eykK0KXnnsPcXWdFj/pR55Kcr3
qPfxGRLrEeYvoUUzr2APiHTPzWsUDwugfV9KXyGYuxIqqOpPr5nWqGfo9ZcvbH7K
Mqc32DFvFdNnUqMYQHu9oigjfCvscdNTrWHlGYD3HJmWZur4IBReb8uG5qPN+VLH
1xs7EcW6NN6A1pndHEosAFcVIh5kx4gRCsZMsEdGExn4gkTknfBosWo0tROV00UU
UYFqLZfUmJyPDVYZf1fjiLd+yC2qJERNxWWy03JG5UZDLc03ULPlqys+MCT8rRdD
b7p2WGOQBM9whNC5BC6SydFPphwc3yhR1jmMSz+QB18Easo7RV6fmhE462wj1EoH
+IDtJX6JRGguky29ibdolQUiRK3rVowGpIjjgw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEkDCCAngCAQEwDQYJKoZIhvcNAQEFBQAwDjEMMAoGA1UEAwwDb2FwMB4XDTIx
MDgyNjAyMzkzNVoXDTIyMDgyNjAyMzkzNVowDjEMMAoGA1UEAwwDb2FwMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0VJUfo/vFmE5N8QYnOAsq3UUgoaH
U+OkwGD+VNQf23sajzfc6/IAoR0Qm6zUqgf1d3BjUrhk4+QRZPCzKzgEvJtFcPwm
VGOp2ojhy92OUMSPL8S6UARHZ12VBfZyvC8UK6AZhvIpKRoE4quzZbIoD8FB7zfw
ABlJvzUHBQp6kHy7UTlNPLBlMbDk7n5EZp53fCzl6hpRuZ5v3EPtq0u76Ub7gGoH
40J3omPMwR0g8ACEK7ijyXbIOKkQiKPcHwaC2dQRaUwShdhSX3fzsq6A/f4Knzhk
m0YyRcr3AD3z0Ud/VBUgSOHIylQq0XATbc5qjsKr+06lKqk2vq565Jh5SdnwZD+N
2CyPa8dld6Ogc3fq7Pl48Sil2Jt5yxV2rPNTYoLeRJKh2ExtATkgJGIEG8fUwP6M
42lyKJOm/R4penHMNHr+Mun+fscY/2DcTAGNYinXlBGEX0fRccQljGYKTy2J3fP8
XzRQgYzT5q1RNC8eKlH1IDRlWbVpisNOGcPOxf8n7MSCu6PjlKbmSKnS9+MeYeqZ
wMObi4vWv7j84r6lMHW2AYtr+iXVkgBlB6mDS72lgpViZeLnfh+KEoMQpZFGSc70
CuS5W+RD/4dJxIAlCiO8zvv0lJl/C67ZNbpfD+nG2csGW0iG0v/amfK0mPaw3idu
7LNqK240yGioOJcCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAYoSC5eUue47ytaqm
504i+iSQUiVHr3C9sRbVMhr0JtnemafWOKAuJUPEf27nxKEL3RJsZ6Csj/PkZboZ
+p+Zg7F+n8EOTGhEADG5Q7AIiayioqFNEgn+ff1LlPEILFYysdAfTCRBkwVOksLO
YMBeb/MXccf62FQrTrDcrAskmC0kENQDeQNjtQjCaV8DDTI68QX5lOK8FNSFIlYh
DwCu1JZSUYvKE0UcZzAQI8BZrlLtacYolwz6cfuB4NvofS1Su5nSB+ugbYkzxNyS
zBSOL7HSn6xhcCayyFsqRBZaIf81EaUg2wWr4E5pfyy7jWFMPiG2m/3z8lPGDa38
cyCdnHz8khmzxMV1qJn4e40YwnWtwZ8xH3S4UNI35RVS4I7IWwEbvnwZ5evYszsS
cxoKZg7vfWuSSqGgZjHqgCI44BsLo9gbT7KtZSnlQKrXd4FlXA3Jjw02qpNr+R8a
Kld25+GtQgsfbAooC9HkTAVsTmAl+pFv/Rm6vVPRYTQJOZE3Y5/hh8E+33odIthd
uw0upy21BSwHukL+Bu8yQSw0kdzy3s/aSuqwyxVsrjZ7xlprmp14Tb2S9NlpnWiO
s2LykpvyO0h9vKa4iQMZidwysm3sb4hzb7NsUnwk+pWev7AE/iQ3DwsV2qYUsfNp
N2pQDjCFe9atqCglt+mkdZfucss=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
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
iE7e5UrqISr1f6Sd1Rb4l3y30ky0Mg==
-----END PRIVATE KEY-----
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册