Configure and instantiate a Noise-over-WebSocket tunnel
This commit is contained in:
parent
e096c608ee
commit
c5c5f642e8
|
@ -98,4 +98,6 @@ linkDevice.secret: AAAAAAAAAAA=
|
|||
|
||||
tlsKeyStore.password: unset
|
||||
|
||||
noiseTunnel.tlsKeyStorePassword: ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
noiseTunnel.noiseStaticPrivateKey: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||
noiseTunnel.recognizedProxySecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
||||
|
|
|
@ -464,6 +464,11 @@ callingTurnManualTable:
|
|||
|
||||
noiseTunnel:
|
||||
port: 8443
|
||||
tlsKeyStoreFile: /path/to/file.p12
|
||||
tlsKeyStoreEntryAlias: example.com
|
||||
tlsKeyStorePassword: secret://noiseTunnel.tlsKeyStorePassword
|
||||
noiseStaticPrivateKey: secret://noiseTunnel.noiseStaticPrivateKey
|
||||
noiseRootPublicKeySignature: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
recognizedProxySecret: secret://noiseTunnel.recognizedProxySecret
|
||||
|
||||
externalRequestFilter:
|
||||
|
|
|
@ -28,7 +28,11 @@ import io.micrometer.core.instrument.Metrics;
|
|||
import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor;
|
||||
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
|
@ -46,6 +50,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterRegistration;
|
||||
|
@ -144,6 +149,8 @@ import org.whispersystems.textsecuregcm.grpc.RequestAttributesInterceptor;
|
|||
import org.whispersystems.textsecuregcm.grpc.net.ClientConnectionManager;
|
||||
import org.whispersystems.textsecuregcm.grpc.net.ManagedDefaultEventLoopGroup;
|
||||
import org.whispersystems.textsecuregcm.grpc.net.ManagedLocalGrpcServer;
|
||||
import org.whispersystems.textsecuregcm.grpc.net.ManagedNioEventLoopGroup;
|
||||
import org.whispersystems.textsecuregcm.grpc.net.NoiseWebSocketTunnelServer;
|
||||
import org.whispersystems.textsecuregcm.jetty.JettyHttpConfigurationCustomizer;
|
||||
import org.whispersystems.textsecuregcm.limits.CardinalityEstimator;
|
||||
import org.whispersystems.textsecuregcm.limits.PushChallengeManager;
|
||||
|
@ -829,9 +836,56 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
}
|
||||
};
|
||||
|
||||
@Nullable final X509Certificate noiseWebSocketTlsCertificate;
|
||||
@Nullable final PrivateKey noiseWebSocketTlsPrivateKey;
|
||||
|
||||
if (config.getNoiseWebSocketTunnelConfiguration().tlsKeyStoreFile() != null &&
|
||||
config.getNoiseWebSocketTunnelConfiguration().tlsKeyStoreEntryAlias() != null &&
|
||||
config.getNoiseWebSocketTunnelConfiguration().tlsKeyStorePassword() != null) {
|
||||
|
||||
try (final FileInputStream websocketNoiseTunnelTlsKeyStoreInputStream = new FileInputStream(config.getNoiseWebSocketTunnelConfiguration().tlsKeyStoreFile())) {
|
||||
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
keyStore.load(websocketNoiseTunnelTlsKeyStoreInputStream, config.getNoiseWebSocketTunnelConfiguration().tlsKeyStorePassword().value().toCharArray());
|
||||
|
||||
final KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(config.getNoiseWebSocketTunnelConfiguration().tlsKeyStoreEntryAlias(),
|
||||
new KeyStore.PasswordProtection(config.getNoiseWebSocketTunnelConfiguration().tlsKeyStorePassword().value().toCharArray()));
|
||||
|
||||
noiseWebSocketTlsCertificate = (X509Certificate) privateKeyEntry.getCertificate();
|
||||
noiseWebSocketTlsPrivateKey = privateKeyEntry.getPrivateKey();
|
||||
}
|
||||
} else {
|
||||
noiseWebSocketTlsCertificate = null;
|
||||
noiseWebSocketTlsPrivateKey = null;
|
||||
}
|
||||
|
||||
final ExecutorService noiseWebSocketDelegatedTaskExecutor = environment.lifecycle()
|
||||
.executorService(name(getClass(), "noiseWebsocketDelegatedTask-%d"))
|
||||
.minThreads(8)
|
||||
.maxThreads(8)
|
||||
.allowCoreThreadTimeOut(false)
|
||||
.build();
|
||||
|
||||
final ManagedNioEventLoopGroup noiseWebSocketEventLoopGroup = new ManagedNioEventLoopGroup();
|
||||
|
||||
final NoiseWebSocketTunnelServer noiseWebSocketTunnelServer = new NoiseWebSocketTunnelServer(
|
||||
config.getNoiseWebSocketTunnelConfiguration().port(),
|
||||
new X509Certificate[] { noiseWebSocketTlsCertificate },
|
||||
noiseWebSocketTlsPrivateKey,
|
||||
noiseWebSocketEventLoopGroup,
|
||||
noiseWebSocketDelegatedTaskExecutor,
|
||||
clientConnectionManager,
|
||||
clientPublicKeysManager,
|
||||
config.getNoiseWebSocketTunnelConfiguration().noiseStaticKeyPair(),
|
||||
config.getNoiseWebSocketTunnelConfiguration().noiseRootPublicKeySignature(),
|
||||
authenticatedGrpcServerAddress,
|
||||
anonymousGrpcServerAddress,
|
||||
config.getNoiseWebSocketTunnelConfiguration().recognizedProxySecret().value());
|
||||
|
||||
environment.lifecycle().manage(localEventLoopGroup);
|
||||
environment.lifecycle().manage(anonymousGrpcServer);
|
||||
environment.lifecycle().manage(authenticatedGrpcServer);
|
||||
environment.lifecycle().manage(noiseWebSocketEventLoopGroup);
|
||||
environment.lifecycle().manage(noiseWebSocketTunnelServer);
|
||||
|
||||
final List<Filter> filters = new ArrayList<>();
|
||||
filters.add(remoteDeprecationFilter);
|
||||
|
|
|
@ -1,8 +1,26 @@
|
|||
package org.whispersystems.textsecuregcm.configuration;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Positive;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.signal.libsignal.protocol.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
|
||||
import org.whispersystems.textsecuregcm.configuration.secrets.SecretString;
|
||||
|
||||
public record NoiseWebSocketTunnelConfiguration(@Positive int port, @NotNull SecretString recognizedProxySecret) {
|
||||
public record NoiseWebSocketTunnelConfiguration(@Positive int port,
|
||||
@Nullable String tlsKeyStoreFile,
|
||||
@Nullable String tlsKeyStoreEntryAlias,
|
||||
@Nullable SecretString tlsKeyStorePassword,
|
||||
@NotNull SecretBytes noiseStaticPrivateKey,
|
||||
@NotNull byte[] noiseRootPublicKeySignature,
|
||||
@NotNull SecretString recognizedProxySecret) {
|
||||
|
||||
public ECKeyPair noiseStaticKeyPair() throws InvalidKeyException {
|
||||
final ECPrivateKey privateKey = Curve.decodePrivatePoint(noiseStaticPrivateKey().value());
|
||||
|
||||
return new ECKeyPair(privateKey.publicKey(), privateKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,4 +131,5 @@ linkDevice.secret: AAAAAAAAAAA=
|
|||
|
||||
tlsKeyStore.password: unset
|
||||
|
||||
noiseTunnel.noiseStaticPrivateKey: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||
noiseTunnel.recognizedProxySecret: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789AAAAAAA
|
||||
|
|
|
@ -443,6 +443,8 @@ callingTurnManualTable:
|
|||
|
||||
noiseTunnel:
|
||||
port: 8443
|
||||
noiseStaticPrivateKey: secret://noiseTunnel.noiseStaticPrivateKey
|
||||
noiseRootPublicKeySignature: ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz
|
||||
recognizedProxySecret: secret://noiseTunnel.recognizedProxySecret
|
||||
|
||||
externalRequestFilter:
|
||||
|
@ -452,3 +454,4 @@ externalRequestFilter:
|
|||
- /example
|
||||
permittedInternalRanges:
|
||||
- 127.0.0.0/8
|
||||
|
||||
|
|
Loading…
Reference in New Issue