Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Exposed the internal endpoints for azurite #1004

Closed
wants to merge 13 commits into from
Closed

chore: Exposed the internal endpoints for azurite #1004

wants to merge 13 commits into from

Conversation

samtrion
Copy link

What does this PR do?

The service specific endpoints are provided, for free use.

Why is it important?

For better use within tests.

Related issues

None

@netlify
Copy link

netlify bot commented Sep 18, 2023

Deploy Preview for testcontainers-dotnet ready!

Name Link
🔨 Latest commit db51bb0
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-dotnet/deploys/652a5d85aa7cd200083d0bb2
😎 Deploy Preview https://deploy-preview-1004--testcontainers-dotnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Collaborator

@HofmeisterAn HofmeisterAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR. Could you please quickly elaborate why this change is necessary? IIRC, the individual service clients work well with the connection string that includes endpoints for all services.

@samtrion
Copy link
Author

samtrion commented Sep 18, 2023

The BlobServiceClient or BlobContainerClient constructors (NuGet Package Azure.Storage.Blobs) has several overloads. Depending on the Authentication / Credential Method used, either the ConnectionString or the ServiceUrl is used.

https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobserviceclient.-ctor?view=azure-dotnet
https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobcontainerclient.-ctor?view=azure-dotnet

Copy link
Collaborator

@HofmeisterAn HofmeisterAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with the changes per se, but I am wondering if the endpoint lacks the necessary auth information. The documentation related to the connectionString constructor argument says:

A connection string includes the authentication information required for your application to access data in an Azure Storage account at runtime.

I am uncertain about how to pass the auth information to the constructors that utilize, for example, the blobContainerUri argument. Perhaps we should consider expanding the documentation for these constructors to provide a clear explanation of how to use the endpoint getter?

@samtrion
Copy link
Author

AzuriteBuilder now supports the possibility to store own account credentials. Furthermore, it is now possible to communicate with the container via https.

❗ It may still be necessary to call mkcert -install on the build agents before running the tests.

@samtrion
Copy link
Author

❗ It may still be necessary to call mkcert -install on the build agents before running the tests.

I tried to adjust the pipeline, but there seems to be more dependencies to the mkcert local root CA than initially thought. I will try this further.

@HofmeisterAn
Copy link
Collaborator

❗ It may still be necessary to call mkcert -install on the build agents before running the tests.

I tried to adjust the pipeline, but there seems to be more dependencies to the mkcert local root CA than initially thought. I will try this further.

If it makes things easier, we can add the PEM files to the test repository for now and get the pull request to a proper state. We can sort it out later. Why don't you use openssl? We can generate the certificate during the CI run and store it where others can access it as well. Azureite won't be the last dependency that adds TLS support.

@samtrion
Copy link
Author

Ok, reworked the PR. Created a certificate with OpenSSL in the tests directory and added it to the repository so we can reuse it.

openssl req -newkey rsa:2048 -x509 -nodes -keyout server.key -new -out server.cert -sha256 -days 99999 -addext "subjectAltName=IP:127.0.0.1" -subj "/C=CO/ST=ST/L=LO/O=OR/OU=OU/CN=CN"

Then uploaded the certificate locally to the certificate store and trusted it.

certutil -addstore -enterprise -f Root .\server.cert

As well as extended the pipeline to also trust the certificate on ubuntu based systems/runner. Unfortunately this does not seem to work as documented ... Here I would need support . 😞

Copy link
Collaborator

@HofmeisterAn HofmeisterAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As well as extended the pipeline to also trust the certificate on ubuntu based systems/runner. Unfortunately this does not seem to work as documented ... Here I would need support . 😞

I did not review the CI step. I do not want to install the certificates in the CI environment because I want to avoid requiring developers to install the certificates on their machines as well to run the tests. I prefer configuring the HttpClientHandler and setting the Azurite client options accordingly. We can inherit from HttpClientHandler and implement one that configures the certificates, like (simplified version):

var serverCertificate = new X509Certificate2(Certificate.PEMPath);

var httpClientHandler = new HttpClientHandler();
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
httpClientHandler.ClientCertificates.Add(serverCertificate);
httpClientHandler.ServerCertificateCustomValidationCallback = (_, certificate, chain, _) =>
    {
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
        var isVerified = chain.Build(certificate);
        var isSignedByExpectedRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate.RawData.SequenceEqual(certificate.RawData);
        return isVerified && isSignedByExpectedRoot;
    };

var options = new BlobClientOptions();
options.Transport = new HttpClientTransport(httpClientHandler);

Additionally, I would like to encourage you to split the changes into multiple PRs. This PR includes different changes that are not dependent on each other, making the review more difficult. Specifically, implementing HTTPS necessitates more changes than the initial PR. Please reduce the changes to the bare minimum for each individual feature.

public AzuriteBuilder WithPemCertificate(string certificatePath, string certificateKeyPath)
{
return Merge(DockerResourceConfiguration, new AzuriteConfiguration(certificatePath: certificatePath, certificateKeyPath: certificateKeyPath))
.WithBindMount(Path.GetDirectoryName(GetType().Assembly.Location), "/internal")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not mount a volume and, instead, utilize the WithResourceMapping(string, string) API or one of its overloaded methods to copy the required files to the container before it starts. Mounting a volume is considered an anti-pattern, it does not work properly in all Docker environments.

.WithResourceMapping(File.ReadAllBytes(clientCertificateFilePath), ClientCertificateFilePath)
.WithResourceMapping(File.ReadAllBytes(clientCertificateFilePath), ClientCertificateKeyFilePath)
.WithCommand("--cert", ClientCertificateFilePath)
.WithCommand("--key", **ClientCertificateKeyFilePath)

Comment on lines +93 to +94
// Default command, if not specified the command will be completely overwritten in case of a certificate.
.WithCommand("azurite", "--blobHost", "0.0.0.0", "--queueHost", "0.0.0.0", "--tableHost", "0.0.0.0");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider the _enabledServices field here. This may require extending th AzuriteService class. We may not initiate every service in the future.

Comment on lines +115 to +118
private static string CleanUpPaths(string value)
{
return value.StartsWith("/internal/", StringComparison.OrdinalIgnoreCase) ? value : $"/internal/{value}";
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utilizing WithResourceMapping(string, string) makes this method unnecessary.

Comment on lines +70 to +72
public string CertificatePath { get; }

public string CertificateKeyPath { get; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we need to store the cert and key.

@HofmeisterAn
Copy link
Collaborator

This pull request has been inactive for some time. I will close it for now. If you wish to continue working on it, please feel free to reopen it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants