Skip to content

Commit 1191a2a

Browse files
tomkerkhovealanwestcijothomas
authored
feat: Fail fast when insecure channel is not configured for .NET Core 3.1 with gRPC (#2691)
* feat: Fail fast when unsecure channel is not configured for .NET Core 3.1 Signed-off-by: Tom Kerkhove <[email protected]> * Update changelog Signed-off-by: Tom Kerkhove <[email protected]> * NotSupportedException -> InvalidOperationException Signed-off-by: Tom Kerkhove <[email protected]> * Code quality Signed-off-by: Tom Kerkhove <[email protected]> * Use static class, instead of base class Signed-off-by: Tom Kerkhove <[email protected]> * Revert change Signed-off-by: Tom Kerkhove <[email protected]> * Add missing file header Signed-off-by: Tom Kerkhove <[email protected]> * Change build directive Signed-off-by: Tom Kerkhove <[email protected]> * Code linting Signed-off-by: Tom Kerkhove <[email protected]> * Remove redundant check Signed-off-by: Tom Kerkhove <[email protected]> * Update changelog Signed-off-by: Tom Kerkhove <[email protected]> * Fix build errors after rebase Signed-off-by: Tom Kerkhove <[email protected]> * Use previous build directives Signed-off-by: Tom Kerkhove <[email protected]> * Update changelog with latest RC Signed-off-by: Tom Kerkhove <[email protected]> * Check for version, instead of build directive Signed-off-by: Tom Kerkhove <[email protected]> * Align tests Signed-off-by: Tom Kerkhove <[email protected]> * Change error message Co-authored-by: Alan West <[email protected]> * Change comment Co-authored-by: Alan West <[email protected]> * Change test name Co-authored-by: Alan West <[email protected]> * Change test name Co-authored-by: Alan West <[email protected]> * fix: Build integration test image with SDK of specified .NET version Signed-off-by: Tom Kerkhove <[email protected]> * revert: Build in 6.0 due to language version Signed-off-by: Tom Kerkhove <[email protected]> * Use version check instead of build directive Signed-off-by: Tom Kerkhove <[email protected]> * Fix formatting Signed-off-by: Tom Kerkhove <[email protected]> * Fix check on version Signed-off-by: Tom Kerkhove <[email protected]> * Remove flaky test * Incorporate @pellared's feedback Signed-off-by: Tom Kerkhove <[email protected]> * Change code style Signed-off-by: Tom Kerkhove <[email protected]> * Code review remarks Signed-off-by: Tom Kerkhove <[email protected]> * Update changelog Co-authored-by: Cijo Thomas <[email protected]> * Update changelog Co-authored-by: Cijo Thomas <[email protected]> Co-authored-by: Alan West <[email protected]> Co-authored-by: Cijo Thomas <[email protected]>
1 parent fd2aa0b commit 1191a2a

File tree

9 files changed

+246
-3
lines changed

9 files changed

+246
-3
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
* Added validation that insecure channel is configured correctly when using
6+
.NET Core 3.x for gRPC-based exporting.
7+
([#2691](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2691))
8+
59
## 1.2.0-rc1
610

711
Released 2021-Nov-29

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
using System.Threading;
1818
using System.Threading.Tasks;
1919
using Grpc.Core;
20+
using OpenTelemetry.Internal;
2021
#if NETSTANDARD2_1 || NET5_0_OR_GREATER
2122
using Grpc.Net.Client;
2223
#endif
23-
using OpenTelemetry.Internal;
2424

2525
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient
2626
{
@@ -33,6 +33,8 @@ protected BaseOtlpGrpcExportClient(OtlpExporterOptions options)
3333
Guard.Null(options, nameof(options));
3434
Guard.InvalidTimeout(options.TimeoutMilliseconds, nameof(options.TimeoutMilliseconds));
3535

36+
ExporterClientValidation.EnsureUnencryptedSupportIsEnabled(options);
37+
3638
this.Options = options;
3739
this.Headers = options.GetMetadataFromHeaders();
3840
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// <copyright file="ExporterClientValidation.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using System;
18+
19+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient
20+
{
21+
internal static class ExporterClientValidation
22+
{
23+
internal static void EnsureUnencryptedSupportIsEnabled(OtlpExporterOptions options)
24+
{
25+
var version = System.Environment.Version;
26+
27+
// This verification is only required for .NET Core 3.x
28+
if (version.Major != 3)
29+
{
30+
return;
31+
}
32+
33+
if (options.Endpoint.Scheme.Equals("http", StringComparison.InvariantCultureIgnoreCase))
34+
{
35+
if (AppContext.TryGetSwitch(
36+
"System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", out var unencryptedIsSupported) == false
37+
|| unencryptedIsSupported == false)
38+
{
39+
throw new InvalidOperationException(
40+
"Calling insecure gRPC services on .NET Core 3.x requires enabling the 'System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport' switch. See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client");
41+
}
42+
}
43+
}
44+
}
45+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// <copyright file="ExporterClientValidationTests.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using System;
18+
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
19+
using Xunit;
20+
21+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
22+
{
23+
public class ExporterClientValidationTests : Http2UnencryptedSupportTests
24+
{
25+
private const string HttpEndpoint = "http://localhost:4173";
26+
private const string HttpsEndpoint = "https://localhost:4173";
27+
28+
[Fact]
29+
public void ExporterClientValidation_FlagIsEnabledForHttpEndpoint()
30+
{
31+
var options = new OtlpExporterOptions
32+
{
33+
Endpoint = new Uri(HttpEndpoint),
34+
};
35+
36+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
37+
38+
var exception = Record.Exception(() => ExporterClientValidation.EnsureUnencryptedSupportIsEnabled(options));
39+
Assert.Null(exception);
40+
}
41+
42+
[Fact]
43+
public void ExporterClientValidation_FlagIsNotEnabledForHttpEndpoint()
44+
{
45+
var options = new OtlpExporterOptions
46+
{
47+
Endpoint = new Uri(HttpEndpoint),
48+
};
49+
50+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", false);
51+
52+
var exception = Record.Exception(() => ExporterClientValidation.EnsureUnencryptedSupportIsEnabled(options));
53+
54+
if (Environment.Version.Major == 3)
55+
{
56+
Assert.NotNull(exception);
57+
Assert.IsType<InvalidOperationException>(exception);
58+
}
59+
else
60+
{
61+
Assert.Null(exception);
62+
}
63+
}
64+
65+
[Fact]
66+
public void ExporterClientValidation_FlagIsNotEnabledForHttpsEndpoint()
67+
{
68+
var options = new OtlpExporterOptions
69+
{
70+
Endpoint = new Uri(HttpsEndpoint),
71+
};
72+
73+
var exception = Record.Exception(() => ExporterClientValidation.EnsureUnencryptedSupportIsEnabled(options));
74+
Assert.Null(exception);
75+
}
76+
}
77+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// <copyright file="Http2UnencryptedSupportTests.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using System;
18+
19+
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
20+
{
21+
public class Http2UnencryptedSupportTests : IDisposable
22+
{
23+
private readonly bool initialFlagStatus;
24+
25+
public Http2UnencryptedSupportTests()
26+
{
27+
this.initialFlagStatus = this.DetermineInitialFlagStatus();
28+
}
29+
30+
public void Dispose()
31+
{
32+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", this.initialFlagStatus);
33+
}
34+
35+
private bool DetermineInitialFlagStatus()
36+
{
37+
if (AppContext.TryGetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", out var flag))
38+
{
39+
return flag;
40+
}
41+
42+
return false;
43+
}
44+
}
45+
}

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,32 @@ public void ExportResultIsSuccess(OtlpExportProtocol protocol, string endpoint)
6868
Assert.Single(delegatingExporter.ExportResults);
6969
Assert.Equal(ExportResult.Success, delegatingExporter.ExportResults[0]);
7070
}
71+
72+
[Trait("CategoryName", "CollectorIntegrationTests")]
73+
[SkipUnlessEnvVarFoundFact(CollectorHostnameEnvVarName)]
74+
public void ConstructingGrpcExporterFailsWhenHttp2UnencryptedSupportIsDisabledForNetcoreapp31()
75+
{
76+
// Adding the OtlpExporter creates a GrpcChannel.
77+
// This switch must be set before creating a GrpcChannel/HttpClient when calling an insecure gRPC service.
78+
// We want to fail fast so we are disabling it
79+
// See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client
80+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", false);
81+
82+
var exporterOptions = new OtlpExporterOptions
83+
{
84+
Endpoint = new Uri($"http://{CollectorHostname}:4317"),
85+
};
86+
87+
var exception = Record.Exception(() => new OtlpTraceExporter(exporterOptions));
88+
89+
if (Environment.Version.Major == 3)
90+
{
91+
Assert.NotNull(exception);
92+
}
93+
else
94+
{
95+
Assert.Null(exception);
96+
}
97+
}
7198
}
7299
}

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\DelegatingTestExporter.cs" Link="Includes\DelegatingTestExporter.cs" />
3030
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\EventSourceTestHelper.cs" Link="Includes\EventSourceTestHelper.cs" />
3131
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\SkipUnlessEnvVarFoundTheoryAttribute.cs" Link="Includes\SkipUnlessEnvVarFoundTheoryAttribute.cs" />
32+
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\SkipUnlessEnvVarFoundFactAttribute.cs" Link="Includes\SkipUnlessEnvVarFoundFactAttribute.cs" />
3233
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestActivityProcessor.cs" Link="Includes\TestActivityProcessor.cs" />
3334
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestEventListener.cs" Link="Includes\TestEventListener.cs" />
3435
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\Utils.cs" Link="Includes\Utils.cs" />

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
2424
{
25-
public class OtlpExporterOptionsExtensionsTests
25+
public class OtlpExporterOptionsExtensionsTests : Http2UnencryptedSupportTests
2626
{
2727
[Theory]
2828
[InlineData("key=value", new string[] { "key" }, new string[] { "value" })]
@@ -86,6 +86,14 @@ public void GetHeaders_NoOptionHeaders_ReturnsEmptyHeadres(string optionHeaders)
8686
[InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpTraceExportClient))]
8787
public void GetTraceExportClient_SupportedProtocol_ReturnsCorrectExportClient(OtlpExportProtocol protocol, Type expectedExportClientType)
8888
{
89+
if (protocol == OtlpExportProtocol.Grpc && Environment.Version.Major == 3)
90+
{
91+
// Adding the OtlpExporter creates a GrpcChannel.
92+
// This switch must be set before creating a GrpcChannel when calling an insecure HTTP/2 endpoint.
93+
// See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client
94+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
95+
}
96+
8997
var options = new OtlpExporterOptions
9098
{
9199
Protocol = protocol,
@@ -96,6 +104,32 @@ public void GetTraceExportClient_SupportedProtocol_ReturnsCorrectExportClient(Ot
96104
Assert.Equal(expectedExportClientType, exportClient.GetType());
97105
}
98106

107+
[Fact]
108+
public void GetTraceExportClient_GetClientForGrpcWithoutUnencryptedFlag_ThrowsException()
109+
{
110+
// Adding the OtlpExporter creates a GrpcChannel.
111+
// This switch must be set before creating a GrpcChannel when calling an insecure HTTP/2 endpoint.
112+
// See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client
113+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", false);
114+
115+
var options = new OtlpExporterOptions
116+
{
117+
Protocol = OtlpExportProtocol.Grpc,
118+
};
119+
120+
var exception = Record.Exception(() => options.GetTraceExportClient());
121+
122+
if (Environment.Version.Major == 3)
123+
{
124+
Assert.NotNull(exception);
125+
Assert.IsType<InvalidOperationException>(exception);
126+
}
127+
else
128+
{
129+
Assert.Null(exception);
130+
}
131+
}
132+
99133
[Fact]
100134
public void GetTraceExportClient_UnsupportedProtocol_Throws()
101135
{

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
3737
{
38-
public class OtlpTraceExporterTests
38+
public class OtlpTraceExporterTests : Http2UnencryptedSupportTests
3939
{
4040
static OtlpTraceExporterTests()
4141
{
@@ -372,6 +372,14 @@ public void ToOtlpSpanPeerServiceTest()
372372
[Fact]
373373
public void UseOpenTelemetryProtocolActivityExporterWithCustomActivityProcessor()
374374
{
375+
if (Environment.Version.Major == 3)
376+
{
377+
// Adding the OtlpExporter creates a GrpcChannel.
378+
// This switch must be set before creating a GrpcChannel when calling an insecure HTTP/2 endpoint.
379+
// See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client
380+
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
381+
}
382+
375383
const string ActivitySourceName = "otlp.test";
376384
TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
377385

0 commit comments

Comments
 (0)