Вызов стороннего контейнера с помощью TestHost/TestServer ядра .net через SSL: Обойти проверку SSL с помощью метода Testservers CreateClient()

Я пытаюсь добавить keycloak в качестве testcontainer к моим интеграционным тестам .net core (5) с использованием библиотеки dotnet-testcontainers .

Моя проблема в том, что я борюсь с поддержкой HTTPS, имеющей контейнер, использующий самозаверяющие сертификаты и TestServer-Class для моих интеграционных тестов.

Чтобы быть точным, я использую класс Microsofts TestServer для создания реальных запросов API с конфигурацией в памяти для использования keycloak-testcontainer с открытым портом 8443 и его самозаверяющим сертификатом.

Проблема в том, что я не могу добавить HttpClientHandler в TestServers HttpClient(созданный через serverCreateClient()), чтобы разрешить недоверенные сертификаты в этом обработчике. Я создал конкретный пример здесь, в ветке apitests-https. Неудачный тест можно найти здесь , в SucceedsWhenGetRequestWithTokenReturnsListOfArticlesМетод испытания. Я добавил несколько комментариев к классу и Startup.cs DemoApi - Project, который показывает, что я пробовал.

В результате внутреннее промежуточное ПО Jwt TestServers использует HttpClient по умолчанию и генерирует следующее исключение 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 ---

Я уже пробовал несколько вещей, которые прокомментированы в коде, чтобы заставить его работать.

  • DemoApi/Startup.cs: Пытался добавить мою собственную среду "Тестирования" со следующим кодом:

    ServicePointManager.Expect100Continue = true;ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls| SecurityProtocolType.Tls11| SecurityProtocolType.Tls12;ServicePointManager.ServerCertificateValidationCallback +=(отправитель, сертификат, цепочка, sslPolicyErrors) => true;

используя это через UseEnvironment("Testing")при создании TestServer-Instance в Api Test. Отладка показывает, что код вызывается, но все же возникает исключение.

  • DemoApiTestsApi/BaseFixture.cs или DemoApiTests/Infrastructure/Persistence/KeycloakTest.cs: См. Здесь рабочую реализацию моего собственного HttpClient с обработчиком для получения токена в целом (это работает в ветке) - GetTestToken это метод в BaseFixture.

Итак, честно говоря, у меня немного нет идей, как заставить это работать с TestServer или иным образом. По сути, мне нужен обработчик, который я использую в BaseFixture.GetTestToken()/ KeycloakTest.cs также использоваться в моем экземпляре TestServer, но не может применять его в CreateClient()который не принимает параметры. Любая помощь приветствуется, может быть, это решение или подсказка к другому способу решения этой проблемы. TestServer не обязательно исправлять, если есть другой способ решить эту проблему.

1 ответ

Решение

Хорошо, я разобрался. Имхо, это не лучшее решение, но пока у меня нет другого, это должно сработать. Все, что мне нужно было сделать, это:

  1. Добавить IWebHostEnvironment в качестве поля в Startup.cs и ввести в Startup() конструктор.

Тогда запуск выглядит так:

              private IWebHostEnvironment CurrentEnv { get; }

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

С этого момента все остальное было довольно легко. В моем ConfigureServices() метод, теперь я могу проверить через CurrentEnv.IsEnvironment("Testing") если я нахожусь в интеграционном тесте, и для этого я мог бы обойти проверку ssl, установив jwt BackChannelHandler вручную.

Код:

      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
                    };
                }
            });

Я должен признать, что это кажется хакерским, и в лучшем случае мне бы хотелось не трогать какой-либо код, связанный с приложением, чтобы этот тест работал. Лучшим решением было бы получить сертификат из моего testcontainer keycloak и автоматически добавить его в доверенные сертификаты экземпляра TestServers Application. Если бы кто-нибудь мог дать другой ответ / намек, как с этим справиться, я был бы рад это увидеть. Но пока это то, что работает лучше всего.

Другие вопросы по тегам