Unverified Commit 428bff1d authored by Arpit Gandhi's avatar Arpit Gandhi Committed by GitHub

#60704: Pass cert for TLS localhost connection (#106635)

*Pass locally generated certificate via command line*

*Fixes: #60704*

Added ARGS: 

- web-tls-cert-path
- web-tls-cert-key-path

Passing the path of local certificate and the key to cert will allow flutter tool to create a secure debugging session on chrome

**Pre-launch Checklist**

✅   I read the [Contributor Guide](https://github.com/flutter/flutter/wiki/Tree-hygiene#overview) and followed the process outlined there for submitting PRs.
✅   I read the [Tree Hygiene](https://github.com/flutter/flutter/wiki/Tree-hygiene) wiki page, which explains my responsibilities.
✅   I read and followed the [Flutter Style Guide](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo), including [Features we expect every widget to implement](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement).
✅   I signed the [CLA](https://cla.developers.google.com/).
✅   I listed at least one issue that this PR fixes in the description above.
✅   I updated/added relevant documentation (doc comments with ///).
✅   I added new tests to check the change I am making.
✅   All existing and new tests are passing.
parent 9be8f4fd
......@@ -93,6 +93,7 @@ export 'dart:io'
// ProcessSignal NO! Use [ProcessSignal] below.
ProcessStartMode,
// RandomAccessFile NO! Use `file_system.dart`
SecurityContext,
ServerSocket,
SignalException,
Socket,
......
......@@ -248,6 +248,8 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
dartEntrypointArgs: stringsArg('dart-entrypoint-args'),
hostname: featureFlags.isWebEnabled ? stringArg('web-hostname') : '',
port: featureFlags.isWebEnabled ? stringArg('web-port') : '',
tlsCertPath: featureFlags.isWebEnabled ? stringArg('web-tls-cert-path') : null,
tlsCertKeyPath: featureFlags.isWebEnabled ? stringArg('web-tls-cert-key-path') : null,
webUseSseForDebugProxy: featureFlags.isWebEnabled && stringArg('web-server-debug-protocol') == 'sse',
webUseSseForDebugBackend: featureFlags.isWebEnabled && stringArg('web-server-debug-backend-protocol') == 'sse',
webUseSseForInjectedClient: featureFlags.isWebEnabled && stringArg('web-server-debug-injected-client-protocol') == 'sse',
......@@ -293,6 +295,8 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
verboseSystemLogs: boolArg('verbose-system-logs'),
hostname: featureFlags.isWebEnabled ? stringArg('web-hostname') : '',
port: featureFlags.isWebEnabled ? stringArg('web-port') : '',
tlsCertPath: featureFlags.isWebEnabled ? stringArg('web-tls-cert-path') : null,
tlsCertKeyPath: featureFlags.isWebEnabled ? stringArg('web-tls-cert-key-path') : null,
webUseSseForDebugProxy: featureFlags.isWebEnabled && stringArg('web-server-debug-protocol') == 'sse',
webUseSseForDebugBackend: featureFlags.isWebEnabled && stringArg('web-server-debug-backend-protocol') == 'sse',
webUseSseForInjectedClient: featureFlags.isWebEnabled && stringArg('web-server-debug-injected-client-protocol') == 'sse',
......
......@@ -951,6 +951,8 @@ class DebuggingOptions {
this.devToolsServerAddress,
this.hostname,
this.port,
this.tlsCertPath,
this.tlsCertKeyPath,
this.webEnableExposeUrl,
this.webUseSseForDebugProxy = true,
this.webUseSseForDebugBackend = true,
......@@ -979,6 +981,8 @@ class DebuggingOptions {
this.dartEntrypointArgs = const <String>[],
this.port,
this.hostname,
this.tlsCertPath,
this.tlsCertKeyPath,
this.webEnableExposeUrl,
this.webUseSseForDebugProxy = true,
this.webUseSseForDebugBackend = true,
......@@ -1055,6 +1059,8 @@ class DebuggingOptions {
required this.devToolsServerAddress,
required this.port,
required this.hostname,
required this.tlsCertPath,
required this.tlsCertKeyPath,
required this.webEnableExposeUrl,
required this.webUseSseForDebugProxy,
required this.webUseSseForDebugBackend,
......@@ -1108,6 +1114,8 @@ class DebuggingOptions {
final Uri? devToolsServerAddress;
final String? port;
final String? hostname;
final String? tlsCertPath;
final String? tlsCertKeyPath;
final bool? webEnableExposeUrl;
final bool webUseSseForDebugProxy;
final bool webUseSseForDebugBackend;
......@@ -1243,6 +1251,8 @@ class DebuggingOptions {
'devToolsServerAddress': devToolsServerAddress.toString(),
'port': port,
'hostname': hostname,
'tlsCertPath': tlsCertPath,
'tlsCertKeyPath': tlsCertKeyPath,
'webEnableExposeUrl': webEnableExposeUrl,
'webUseSseForDebugProxy': webUseSseForDebugProxy,
'webUseSseForDebugBackend': webUseSseForDebugBackend,
......@@ -1296,6 +1306,8 @@ class DebuggingOptions {
devToolsServerAddress: json['devToolsServerAddress'] != null ? Uri.parse(json['devToolsServerAddress']! as String) : null,
port: json['port'] as String?,
hostname: json['hostname'] as String?,
tlsCertPath: json['tlsCertPath'] as String?,
tlsCertKeyPath: json['tlsCertKeyPath'] as String?,
webEnableExposeUrl: json['webEnableExposeUrl'] as bool?,
webUseSseForDebugProxy: json['webUseSseForDebugProxy']! as bool,
webUseSseForDebugBackend: json['webUseSseForDebugBackend']! as bool,
......
......@@ -164,6 +164,8 @@ class WebAssetServer implements AssetReader {
ChromiumLauncher? chromiumLauncher,
String hostname,
int port,
String? tlsCertPath,
String? tlsCertKeyPath,
UrlTunneller? urlTunneller,
bool useSseForDebugProxy,
bool useSseForDebugBackend,
......@@ -188,7 +190,14 @@ class WebAssetServer implements AssetReader {
const int kMaxRetries = 4;
for (int i = 0; i <= kMaxRetries; i++) {
try {
if (tlsCertPath != null && tlsCertKeyPath != null) {
final SecurityContext serverContext = SecurityContext()
..useCertificateChain(tlsCertPath)
..usePrivateKey(tlsCertKeyPath);
httpServer = await HttpServer.bindSecure(address, port, serverContext);
} else {
httpServer = await HttpServer.bind(address, port);
}
break;
} on SocketException catch (e, s) {
if (i >= kMaxRetries) {
......@@ -635,6 +644,8 @@ class WebDevFS implements DevFS {
WebDevFS({
required this.hostname,
required int port,
required this.tlsCertPath,
required this.tlsCertKeyPath,
required this.packagesFilePath,
required this.urlTunneller,
required this.useSseForDebugProxy,
......@@ -671,6 +682,8 @@ class WebDevFS implements DevFS {
final bool nativeNullAssertions;
final int _port;
final NullSafetyMode nullSafetyMode;
final String? tlsCertPath;
final String? tlsCertKeyPath;
late WebAssetServer webAssetServer;
......@@ -757,6 +770,8 @@ class WebDevFS implements DevFS {
chromiumLauncher,
hostname,
_port,
tlsCertPath,
tlsCertKeyPath,
urlTunneller,
useSseForDebugProxy,
useSseForDebugBackend,
......@@ -777,10 +792,13 @@ class WebDevFS implements DevFS {
} else if (buildInfo.dartDefines.contains('FLUTTER_WEB_USE_SKIA=true')) {
webAssetServer.webRenderer = WebRendererMode.canvaskit;
}
String url = '$hostname:$selectedPort';
if (hostname == 'any') {
_baseUri = Uri.http('localhost:$selectedPort', webAssetServer.basePath);
} else {
_baseUri = Uri.http('$hostname:$selectedPort', webAssetServer.basePath);
url ='localhost:$selectedPort';
}
_baseUri = Uri.http(url, webAssetServer.basePath);
if (tlsCertPath != null && tlsCertKeyPath!= null) {
_baseUri = Uri.https(url, webAssetServer.basePath);
}
return _baseUri!;
}
......
......@@ -293,6 +293,8 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
device!.devFS = WebDevFS(
hostname: debuggingOptions.hostname ?? 'localhost',
port: await getPort(),
tlsCertPath: debuggingOptions.tlsCertPath,
tlsCertKeyPath: debuggingOptions.tlsCertKeyPath,
packagesFilePath: packagesFilePath,
urlTunneller: _urlTunneller,
useSseForDebugProxy: debuggingOptions.webUseSseForDebugProxy,
......@@ -309,7 +311,10 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
nativeNullAssertions: debuggingOptions.nativeNullAssertions,
);
final Uri url = await device!.devFS!.create();
Uri url = await device!.devFS!.create();
if (debuggingOptions.tlsCertKeyPath != null && debuggingOptions.tlsCertPath != null) {
url = url.replace(scheme: 'https');
}
if (debuggingOptions.buildInfo.isDebug) {
await runSourceGenerators();
final UpdateFSReport report = await _updateDevFS(fullRestart: true);
......
......@@ -261,6 +261,16 @@ abstract class FlutterCommand extends Command<void> {
'will select a random open port on the host.',
hide: !verboseHelp,
);
argParser.addOption(
'web-tls-cert-path',
help: 'The certificate that host will use to serve using TLS connection. '
'If not provided, the tool will use default http scheme.',
);
argParser.addOption(
'web-tls-cert-key-path',
help: 'The certificate key that host will use to authenticate cert. '
'If not provided, the tool will use default http scheme.',
);
argParser.addOption('web-server-debug-protocol',
allowed: <String>['sse', 'ws'],
defaultsTo: 'ws',
......
-----BEGIN CERTIFICATE-----
MIICjTCCAfYCCQDR1evIEbvoVjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
VVMxEjAQBgNVBAgMCVNvbWV3aGVyZTERMA8GA1UEBwwIU29tZUNpdHkxDTALBgNV
BAoMBENvcnAxETAPBgNVBAsMCFNvZnR3YXJlMRIwEAYDVQQDDAlsb2NhbGhvc3Qx
HjAcBgkqhkiG9w0BCQEWD2FkbWluQGxvY2FsaG9zdDAeFw0xMjA2MDgxOTE0Mzha
Fw0xMjA3MDgxOTE0MzhaMIGKMQswCQYDVQQGEwJVUzESMBAGA1UECAwJU29tZXdo
ZXJlMREwDwYDVQQHDAhTb21lQ2l0eTENMAsGA1UECgwEQ29ycDERMA8GA1UECwwI
U29mdHdhcmUxEjAQBgNVBAMMCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPYWRt
aW5AbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/Vj4UCdQI
N0IMCHDWwDo3QyH9I8sBm/OwIiiJbQ0RpyfWCn4ilzZwu98okwUCu5PwlFQZd67a
DooxhFS2FSw4iRZCUGJlgV7BG1JX9q0xqVy33V6rxFFYQYHw6r7FHPaw2FuRCOoi
uDqE+ua6lbV2YP/eXiRnq5hT5vWfEX5rYwIDAQABMA0GCSqGSIb3DQEBBQUAA4GB
ADQFczkmr+91I3id7HH1voh9YKVqA9nh1yYFCkonsDPXBEJIZEyYa9HaWVkcMMgo
F7bJbWKyaYXW818XPXuOmSBI7dmaMtqITEJWsxdcMVKYCOMtjTLwquPki6xXZxNb
y/zubrV+P4XN0Wi0hoDU/0/RNQOuAF1w7UOQsUmn1ihj
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC/Vj4UCdQIN0IMCHDWwDo3QyH9I8sBm/OwIiiJbQ0RpyfWCn4i
lzZwu98okwUCu5PwlFQZd67aDooxhFS2FSw4iRZCUGJlgV7BG1JX9q0xqVy33V6r
xFFYQYHw6r7FHPaw2FuRCOoiuDqE+ua6lbV2YP/eXiRnq5hT5vWfEX5rYwIDAQAB
AoGAaL5oq4WZ2omNkZLJWvbOp9QLdk2y44WhSOnaMSlOvzw3tZf25y7KcbqXdtnN
I2rWmRxKUcrQILVW97aOvUMn+jOCN6+IY1kiT6Un4H1Ow+rVj3CDvCjBCZhfkExK
osTzpwbiRyHueWOLOB3RZUNXC+5OfTBVoYgQp85INykwUHECQQD10XsOKDtkuCh4
yNf68lXC7TUSPAwAjX+I4xJ1UWMO5DWBpuyuA2GSlYHWZg4ae0xm9zUijb6A5WDW
aVTvi9S5AkEAx0MSU2qD837lXAjkcyBS9WqtoJebC263uUaiQ8WZQhDE+R8aeXj+
e80hY8FOc6WogC2VbQgYO52t82KWLDKq+wJAS+2Xl+jfZ53mimBnLhE6YkpIsUgw
4N7T/OE+q1QnR8s/p7t6sclDkzZw81t0kcNx9v/2vqSPqlqvjardXFyRqQJBAIAW
jWExx0BvAeD3lmKrFKjNum7RBcmDknZ3ATevfaUKQpQhelM7g9rxMdV+HYAZrQc4
RiWgXnN0GK2rYf1nVKECQQCo5UHoukW89+UX/SbamoMeUZJxL3bQAqv71B59C6cy
NQuDZlHOqDajyxaX2y8tJWgk3ciGXlIqByHQFXb2Rhuw
-----END RSA PRIVATE KEY-----
......@@ -662,6 +662,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......@@ -777,6 +779,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......@@ -888,6 +892,8 @@ void main() {
// if this is any other value, we will do a real ip lookup
hostname: 'any',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null,
useSseForDebugProxy: true,
......@@ -951,6 +957,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'any',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......@@ -987,6 +995,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......@@ -1031,6 +1041,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......@@ -1065,12 +1077,64 @@ void main() {
await webDevFS.destroy();
}));
test('Can start web server with tls connection', () => testbed.run(() async {
final String dataPath = globals.fs.path.join(
getFlutterRoot(),
'packages',
'flutter_tools',
'test',
'data',
'asset_test',
);
final String dummyCertPath =
globals.fs.path.join(dataPath, 'tls_cert', 'dummy-cert.pem');
final String dummyCertKeyPath =
globals.fs.path.join(dataPath, 'tls_cert', 'dummy-key.pem');
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: dummyCertPath,
tlsCertKeyPath: dummyCertKeyPath,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
useSseForDebugBackend: true,
useSseForInjectedClient: true,
nullAssertions: true,
nativeNullAssertions: true,
buildInfo: BuildInfo.debug,
enableDwds: false,
enableDds: false,
entrypoint: Uri.base,
testMode: true,
expressionCompiler: null, // ignore: avoid_redundant_argument_values
extraHeaders: const <String, String>{},
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
nullSafetyMode: NullSafetyMode.unsound,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
final Uri uri = await webDevFS.create();
// Ensure the connection established is secure
expect(uri.scheme, 'https');
await webDevFS.destroy();
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
}));
test('allows frame embedding', () async {
final WebAssetServer webAssetServer = await WebAssetServer.start(
null,
'localhost',
0,
null,
null,
null,
true,
true,
true,
......@@ -1099,6 +1163,8 @@ void main() {
'localhost',
0,
null,
null,
null,
true,
true,
true,
......@@ -1174,6 +1240,8 @@ void main() {
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
tlsCertPath: null,
tlsCertKeyPath: null,
packagesFilePath: '.packages',
urlTunneller: null, // ignore: avoid_redundant_argument_values
useSseForDebugProxy: true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment