Skip to content

Commit

Permalink
Update AzureOpenAIConfig validation to cater for reverse proxy endpoi…
Browse files Browse the repository at this point in the history
…nt (#875)

## Motivation and Context (Why the change? What's the scenario?)

The intent of the change in this PR is to allow Azure OpenAI requests to
be made over HTTP to a reverse proxy that is running within the same
Docker Compose orchestration for local development.

We have reverse proxy in place for Azure OpenAI that routes requests to
different service instances based on available quota. When deployed to
Azure all services can use HTTPS and Kernel Memory can use the reverse
proxy for Azure OpenAI requests.

Visual Studio with Docker Compose orchestration is used for local
development. The reverse proxy project is started and exposes both HTTP
and HTTPS ports. A local dev certificate is used automatically by the
tooling and the reverse proxy can be called over HTTPS from the host
machine using `localhost`.

## High level description (Approach, Design)

Update the validation in the `AzureOpenAIConfig` class:

- Allow to define a custom list of SSL cert thumbprints to be trusted

---------

Co-authored-by: Devis Lucato <[email protected]>
Co-authored-by: Devis Lucato <[email protected]>
  • Loading branch information
3 people authored Nov 19, 2024
1 parent 6173e9b commit 4b6b0e8
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 3 deletions.
7 changes: 7 additions & 0 deletions extensions/AzureOpenAI/AzureOpenAI/AzureOpenAIConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using Azure.Core;

#pragma warning disable IDE0130 // reduce number of "using" statements
Expand Down Expand Up @@ -83,6 +84,12 @@ public enum APITypes
/// </summary>
public int MaxRetries { get; set; } = 10;

/// <summary>
/// Thumbprints of certificates that should be trusted for HTTPS requests when SSL policy errors are detected.
/// This should only be used for local development when using a proxy to call the OpenAI endpoints.
/// </summary>
public HashSet<string> TrustedCertificateThumbprints { get; set; } = [];

/// <summary>
/// Set credentials manually from code
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -32,6 +35,13 @@ internal static AzureOpenAIClient Build(
// See https://github.com/Azure/azure-sdk-for-net/issues/46109
options.AddPolicy(new SingleAuthorizationHeaderPolicy(), PipelinePosition.PerTry);

if (httpClient is null && config.TrustedCertificateThumbprints.Count > 0)
{
#pragma warning disable CA2000 // False Positive: https://github.com/dotnet/roslyn-analyzers/issues/4636
httpClient = BuildHttpClientWithCustomCertificateValidation(config);
#pragma warning restore CA2000
}

if (httpClient is not null)
{
options.Transport = new HttpClientPipelineTransport(httpClient);
Expand All @@ -57,6 +67,47 @@ internal static AzureOpenAIClient Build(
throw new ConfigurationException($"Azure OpenAI: authentication type '{config.Auth:G}' is not supported");
}
}

private static HttpClient BuildHttpClientWithCustomCertificateValidation(AzureOpenAIConfig config)
{
#pragma warning disable CA2000 // False Positive: https://github.com/dotnet/roslyn-analyzers/issues/4636
var handler = new HttpClientHandler();
#pragma warning restore CA2000

handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback =
(_, cert, _, policyErrors) =>
{
// Pass if there are no policy errors.
if (policyErrors == SslPolicyErrors.None) { return true; }
// Attempt to get the thumbprint of the remote certificate.
string? remoteCert;
try
{
remoteCert = cert?.GetCertHashString();
}
catch (CryptographicException)
{
// Fail if crypto lib is not working
return false;
}
catch (ArgumentException)
{
// Fail if thumbprint format is invalid
return false;
}
// Fail if no thumbprint available
if (remoteCert == null) { return false; }
// Success if the remote cert thumbprint matches any of the trusted ones.
return config.TrustedCertificateThumbprints.Any(
trustedCert => string.Equals(remoteCert, trustedCert, StringComparison.OrdinalIgnoreCase));
};

return new HttpClient(handler);
}
}

// Use only for local debugging - Usage:
Expand Down
10 changes: 8 additions & 2 deletions service/Service/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,10 @@
// See https://learn.microsoft.com/azure/ai-services/openai/reference#embeddings
"MaxEmbeddingBatchSize": 1,
// How many times to retry in case of throttling.
"MaxRetries": 10
"MaxRetries": 10,
// Thumbprints of certificates that should be trusted for HTTPS requests when SSL policy errors are detected.
// This should only be used for local development when using a proxy to call the OpenAI endpoints.
"TrustedCertificateThumbprints": []
},
"AzureOpenAIText": {
// "ApiKey" or "AzureIdentity"
Expand All @@ -355,7 +358,10 @@
// "ChatCompletion" or "TextCompletion"
"APIType": "ChatCompletion",
// How many times to retry in case of throttling.
"MaxRetries": 10
"MaxRetries": 10,
// Thumbprints of certificates that should be trusted for HTTPS requests when SSL policy errors are detected.
// This should only be used for local development when using a proxy to call the OpenAI endpoints.
"TrustedCertificateThumbprints": []
},
"AzureQueues": {
// "ConnectionString" or "AzureIdentity". For other options see <AzureQueueConfig>.
Expand Down

0 comments on commit 4b6b0e8

Please sign in to comment.