Skip to content

Commit 6ebe1e0

Browse files
committed
Host keys race fix
1 parent 0dc5ffd commit 6ebe1e0

File tree

1 file changed

+48
-35
lines changed

1 file changed

+48
-35
lines changed

src/WebJobs.Script.WebHost/Security/SecretManager.cs

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.IO;
99
using System.Linq;
1010
using System.Security.Cryptography;
11+
using System.Threading;
1112
using System.Threading.Tasks;
1213
using Microsoft.Azure.WebJobs.Host;
1314
using Microsoft.Azure.WebJobs.Script.Config;
@@ -24,6 +25,7 @@ public class SecretManager : IDisposable, ISecretManager
2425
private readonly ILogger _logger;
2526
private readonly ISecretsRepository _repository;
2627
private HostSecretsInfo _hostSecrets;
28+
private SemaphoreSlim _hostSecretsLock = new SemaphoreSlim(1, 1);
2729

2830
// for testing
2931
public SecretManager()
@@ -62,53 +64,64 @@ protected virtual void Dispose(bool disposing)
6264
if (disposing)
6365
{
6466
(_repository as IDisposable)?.Dispose();
67+
_hostSecretsLock.Dispose();
6568
}
6669
}
6770

6871
public async virtual Task<HostSecretsInfo> GetHostSecretsAsync()
6972
{
7073
if (_hostSecrets == null)
7174
{
72-
HostSecrets hostSecrets = await LoadSecretsAsync<HostSecrets>();
73-
74-
if (hostSecrets == null)
75-
{
76-
// host secrets do not yet exist so generate them
77-
_traceWriter.Verbose(Resources.TraceHostSecretGeneration);
78-
_logger?.LogDebug(Resources.TraceHostSecretGeneration);
79-
hostSecrets = GenerateHostSecrets();
80-
await PersistSecretsAsync(hostSecrets);
81-
}
82-
75+
HostSecrets hostSecrets;
76+
// Allow only one thread to modify the secrets
77+
await _hostSecretsLock.WaitAsync();
8378
try
8479
{
85-
// Host secrets will be in the original persisted state at this point (e.g. encrypted),
86-
// so we read the secrets running them through the appropriate readers
87-
hostSecrets = ReadHostSecrets(hostSecrets);
88-
}
89-
catch (CryptographicException)
90-
{
91-
_traceWriter.Verbose(Resources.TraceNonDecryptedHostSecretRefresh);
92-
_logger?.LogDebug(Resources.TraceNonDecryptedHostSecretRefresh);
93-
await PersistSecretsAsync(hostSecrets, null, true);
94-
await RefreshSecretsAsync(hostSecrets);
95-
}
80+
hostSecrets = await LoadSecretsAsync<HostSecrets>();
9681

97-
// If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
98-
// the state and persist the secrets
99-
if (hostSecrets.HasStaleKeys)
100-
{
101-
_traceWriter.Verbose(Resources.TraceStaleHostSecretRefresh);
102-
_logger?.LogDebug(Resources.TraceStaleHostSecretRefresh);
103-
await RefreshSecretsAsync(hostSecrets);
104-
}
82+
if (hostSecrets == null)
83+
{
84+
// host secrets do not yet exist so generate them
85+
_traceWriter.Verbose(Resources.TraceHostSecretGeneration);
86+
_logger?.LogDebug(Resources.TraceHostSecretGeneration);
87+
hostSecrets = GenerateHostSecrets();
88+
await PersistSecretsAsync(hostSecrets);
89+
}
90+
91+
try
92+
{
93+
// Host secrets will be in the original persisted state at this point (e.g. encrypted),
94+
// so we read the secrets running them through the appropriate readers
95+
hostSecrets = ReadHostSecrets(hostSecrets);
96+
}
97+
catch (CryptographicException)
98+
{
99+
_traceWriter.Verbose(Resources.TraceNonDecryptedHostSecretRefresh);
100+
_logger?.LogDebug(Resources.TraceNonDecryptedHostSecretRefresh);
101+
await PersistSecretsAsync(hostSecrets, null, true);
102+
await RefreshSecretsAsync(hostSecrets);
103+
}
104+
105+
// If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
106+
// the state and persist the secrets
107+
if (hostSecrets.HasStaleKeys)
108+
{
109+
_traceWriter.Verbose(Resources.TraceStaleHostSecretRefresh);
110+
_logger?.LogDebug(Resources.TraceStaleHostSecretRefresh);
111+
await RefreshSecretsAsync(hostSecrets);
112+
}
105113

106-
_hostSecrets = new HostSecretsInfo
114+
_hostSecrets = new HostSecretsInfo
115+
{
116+
MasterKey = hostSecrets.MasterKey.Value,
117+
FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value),
118+
SystemKeys = hostSecrets.SystemKeys.ToDictionary(s => s.Name, s => s.Value)
119+
};
120+
}
121+
finally
107122
{
108-
MasterKey = hostSecrets.MasterKey.Value,
109-
FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value),
110-
SystemKeys = hostSecrets.SystemKeys.ToDictionary(s => s.Name, s => s.Value)
111-
};
123+
_hostSecretsLock.Release();
124+
}
112125
}
113126

114127
return _hostSecrets;

0 commit comments

Comments
 (0)