Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
545 views
in Technique[技术] by (71.8m points)

https - Calling thirdparty container with .net core TestHost/TestServer via SSL: Bypass SSL Validation using Testservers CreateClient() method

I am trying to add keycloak as a testcontainer to my .net core (5) integration tests using the dotnet-testcontainers library .

My Problem is, I am struggling with HTTPS-Support having a container using self-signed certificates and TestServer-Class for my integration tests.

To be precise, I am using Microsofts TestServer class to create real API requests with an in-memory config for using a keycloak-testcontainer with exposed port 8443 and its self-signed certificate.

The Problem is: I can’t add a HttpClientHandler to TestServers HttpClient(created via serverCreateClient()) to allow non-trusted certs within this handler. I have created a concrete example here on branch apitests-https. The failing test can be found here, its in the SucceedsWhenGetRequestWithTokenReturnsListOfArticlestest method. I added some Comments to the class and the Startup.cs of DemoApi - Project that shows what i've tried.

As a result, the TestServers internal Jwt Middleware uses the default HttpClient and throws the following AuthenticationException:

 ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'System.String'.
 ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch, RemoteCertificateChainErrors
   at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---

I already tried multiple things to make it work which are commented in the code.

  • DemoApi/Startup.cs: Tried to add my own "Testing" Environment with following Code:

    ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

using it via UseEnvironment("Testing") when creating the TestServer-Instance in the Api Test. Debugging shows the code is called, but still the exception occurs.

  • DemoApiTestsApi/BaseFixture.cs or DemoApiTests/Infrastructure/Persistence/KeycloakTest.cs: See here for a working Implementation of my own HttpClient with handler to obtain the token in general (this works in the branch) - GetTestToken is the method in BaseFixture.

So, honestly I am a bit out of ideas on how to make this work with TestServer or otherwise. Essentially, I need the handler I use in BaseFixture.GetTestToken()/KeycloakTest.cs to be also used within my TestServer instance, but can't apply it in the CreateClient() which does not accept parameters. Any help is highly appreciated, may it be a solution or a hint to another way to solve this. TestServer is not necessarily fixed when there's another way to work this out.

question from:https://stackoverflow.com/questions/66062832/calling-thirdparty-container-with-net-core-testhost-testserver-via-ssl-bypass

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, I worked it out. Imho it is not the best solution, but as long as I don't have another one, this should work. All I had to do was:

  1. Add IWebHostEnvironment as a field to Startup.cs and inject in in Startup() constructor.

Startup then looks like this:

        private IWebHostEnvironment CurrentEnv { get; }

        public Startup(IWebHostEnvironment env, IConfiguration configuration)
        {
            Configuration = configuration;
            CurrentEnv = env;
        }

From here on, the rest was pretty easy. In my ConfigureServices() method, I can now check via CurrentEnv.IsEnvironment("Testing") if I am in an integration test, and for these I could bypass ssl validation by setting the jwt BackChannelHandler manually.

Code:

services.ConfigureJwtAuthentication(options =>
            {
                options.Audience = Configuration["Jwt:Audience"];
                options.Authority = Configuration["Jwt:Issuer"];
                options.TokenValidationParameters.ValidIssuer = options.Authority;

                if (CurrentEnv.IsEnvironment("Testing"))
                {
                    options.BackchannelHttpHandler = new HttpClientHandler()
                    {
                        ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
                    };
                }
            });

I have to admit, this feels hacky and at best I would love to not need to touch any application related code to make this test work. Best solution would be to get the certificate from my keycloak testcontainer and add it automatically to the trusted certs of the TestServers Application instance. If anyone could give another answer / a hint on how to get along with this, I'd be happy to see it. But for now this is what works best.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...