Skip to content

Conversation

brettsam
Copy link
Member

@brettsam brettsam commented Jul 22, 2025

fixes #11166

Container Apps currently uses blob storage for it's secrets, which are not manageable easily. We're transitioning secrets to be managed via Container Apps itself. In this case, the secrets will be in a drive in the container the same way as our K8S secrets are.

The main difference here is that these are read-only. We have no mechanism for auto-generating these in Container Apps, so they need to be created separately.

Pull request checklist

IMPORTANT: Currently, changes must be backported to the in-proc branch to be included in Core Tools and non-Flex deployments.

  • Backporting to the in-proc branch is not required
    • Otherwise: Link to backporting PR
  • My changes do not require documentation changes
    • Otherwise: Documentation issue linked to PR
  • My changes should not be added to the release notes for the next release
    • Otherwise: I've added my notes to release_notes.md
  • My changes do not need to be backported to a previous version
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • My changes do not require diagnostic events changes
    • Otherwise: I have added/updated all related diagnostic events and their documentation (Documentation issue linked to PR)
  • I have added all required tests (Unit tests, E2E tests)

@brettsam brettsam requested a review from a team as a code owner July 22, 2025 20:28
@fabiocav fabiocav requested a review from Copilot July 22, 2025 23:22
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request adds support for Container Apps secrets by implementing a read-only secrets repository that reads secrets from mounted files in the container filesystem, transitioning from blob storage to Container Apps native secret management.

  • Introduces ContainerAppsSecretsRepository as a new read-only secrets repository implementation
  • Modifies SecretManager to handle nullable master keys for scenarios where secrets don't exist
  • Updates the secret manager provider to support the new "containerapps" secret storage type

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
ContainerAppsSecretsRepository.cs New read-only repository implementation that reads secrets from /run/secrets/functions-keys directory
DefaultSecretManagerProvider.cs Adds support for "containerapps" secret storage type and instantiation of the new repository
SecretManager.cs Updates to handle nullable master keys and removes unused import
ContainerAppsSecretsRepositoryTests.cs Comprehensive test suite covering secret reading, empty scenarios, and SecretManager integration

Comment on lines 141 to +142
// before caching any secrets, validate them
string masterKeyValue = hostSecrets.MasterKey.Value;
string masterKeyValue = hostSecrets.MasterKey?.Value;
Copy link
Preview

Copilot AI Jul 22, 2025

Choose a reason for hiding this comment

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

The change to make masterKeyValue nullable without updating the ValidateHostSecrets method call could cause issues. The ValidateHostSecrets method may not be designed to handle null masterKeyValue parameter.

Copilot uses AI. Check for mistakes.

}
}

// Always return a HostSecrets object, even if empty. This will prevent the SecretManager from thinking
Copy link
Member

Choose a reason for hiding this comment

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

So who is responsible for ensuring that require keys like the master key are present? What happens if the key isn't present but /admin APIs are called by control plane?

@brettsam brettsam force-pushed the brettsam/env-secrets branch 2 times, most recently from 3dd38cf to 3bf0a43 Compare July 29, 2025 15:08
@brettsam brettsam force-pushed the brettsam/env-secrets branch from 3bf0a43 to bba7bad Compare August 11, 2025 15:55
}

public Task WriteAsync(ScriptSecretsType type, string functionName, ScriptSecrets secrets)
=> throw new NotImplementedException();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be NotSupportedException with a message saying this is a read-only repository?

{
if (type == ScriptSecretsType.Function && string.IsNullOrEmpty(functionName))
{
throw new ArgumentNullException(nameof(functionName), $"{nameof(functionName)} cannot be null or empty with {nameof(type)} = {nameof(ScriptSecretsType.Function)}");
Copy link
Contributor

Choose a reason for hiding this comment

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

very minor nit: technically you throw ArgumentNullException when null and ArgumentException when empty.

There is a helper for that: ArgumentException.ThrowIfNullOrEmpty

if (type == ScriptSecretsType.Function)
{
    ArgumentException.ThrowIfNullOrEmpty(functionName);
}


private async Task<ScriptSecrets> ReadHostSecretsAsync()
{
var secrets = await GetFromFilesAsync(ContainerAppsSecretsDir);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: prefer actual types over var

{
var secrets = await GetFromFilesAsync(ContainerAppsSecretsDir);

HostSecrets hostSecrets = new HostSecrets()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can be made more concise

Suggested change
HostSecrets hostSecrets = new HostSecrets()
HostSecrets hostSecrets = new()


private async Task<IDictionary<string, string>> GetFromFilesAsync(string path)
{
string[] files = await FileUtility.GetFilesAsync(path, "*");
Copy link
Contributor

Choose a reason for hiding this comment

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

For testability, can you inject a IFileSystem? Not sure if we have that type in the DI container. But would be helpful to add it if not there.

This way you can mock the file system and not need to perform any actual I/O in tests.

sb.AppendLine($" {file}");
}

_logger.LogDebug(sb.ToString());
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: not sure if we have a performance concern here, but you could do a _logger.IsEnabled(LogLevel.Debug) check and no-op all logging if it is false. This will avoid the string builder allocation.

/// no-op - allow stale secrets to remain.
/// </summary>
public async Task PurgeOldSecretsAsync(IList<string> currentFunctions, ILogger logger)
=> await Task.Yield();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove async and return Task.CompletedTask.

/// Runtime is not responsible for encryption so this code will never be executed.
/// </summary>
public Task WriteSnapshotAsync(ScriptSecretsType type, string functionName, ScriptSecrets secrets)
=> throw new NotSupportedException();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a message explaining this is a read-only repository?

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.

Aspire: Add read-only secret repository for ACA
3 participants