Skip to content

Commit ce30a6e

Browse files
committed
MSI support for Linux consumption
MSI Token service is implemented as a sidecar which gets specialized when the main container (Functions host) is specialized.If MSI is enabled for a site, the specialization payload will include MSI_ENDPOINT and MSI_SECRET which function apps can use to communicate with Token Service.
1 parent 93ea69f commit ce30a6e

File tree

13 files changed

+347
-5
lines changed

13 files changed

+347
-5
lines changed

src/WebJobs.Script.WebHost/Controllers/InstanceController.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public async Task<IActionResult> Assign([FromBody] EncryptedHostAssignmentContex
4343
return StatusCode(StatusCodes.Status400BadRequest, error);
4444
}
4545

46+
// Wait for Sidecar specialization to complete before returning ok.
47+
// This shouldn't take too long so ok to do this sequentially.
48+
error = await _instanceManager.SpecializeMSISidecar(assignmentContext);
49+
if (error != null)
50+
{
51+
return StatusCode(StatusCodes.Status500InternalServerError, error);
52+
}
53+
4654
var result = _instanceManager.StartAssignment(assignmentContext);
4755

4856
return result

src/WebJobs.Script.WebHost/Management/IInstanceManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ public interface IInstanceManager
1414
Task<string> ValidateContext(HostAssignmentContext assignmentContext);
1515

1616
bool StartAssignment(HostAssignmentContext assignmentContext);
17+
18+
Task<string> SpecializeMSISidecar(HostAssignmentContext assignmentContext);
1719
}
1820
}

src/WebJobs.Script.WebHost/Management/InstanceManager.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.Azure.WebJobs.Script.WebHost.Models;
1717
using Microsoft.Extensions.Logging;
1818
using Microsoft.Extensions.Options;
19+
using Newtonsoft.Json;
1920

2021
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management
2122
{
@@ -42,6 +43,44 @@ public InstanceManager(IOptionsFactory<ScriptApplicationHostOptions> optionsFact
4243
_optionsFactory = optionsFactory ?? throw new ArgumentNullException(nameof(optionsFactory));
4344
}
4445

46+
public async Task<string> SpecializeMSISidecar(HostAssignmentContext context)
47+
{
48+
string endpoint;
49+
var msiEnabled = context.IsMSIEnabled(out endpoint);
50+
51+
_logger.LogInformation($"MSI enabled status: {msiEnabled}");
52+
53+
if (msiEnabled)
54+
{
55+
using (_metricsLogger.LatencyEvent(MetricEventNames.LinuxContainerSpecializationMSIInit))
56+
{
57+
var uri = new Uri(endpoint);
58+
var address = $"http://{uri.Host}:{uri.Port}{ScriptConstants.LinuxMSISpecializationStem}";
59+
60+
_logger.LogDebug($"Specializing sidecar at {address}");
61+
62+
var requestMessage = new HttpRequestMessage(HttpMethod.Post, address)
63+
{
64+
Content = new StringContent(JsonConvert.SerializeObject(context.MSIContext),
65+
Encoding.UTF8, "application/json")
66+
};
67+
68+
var response = await _client.SendAsync(requestMessage);
69+
70+
_logger.LogInformation($"Specialize MSI sidecar returned {response.StatusCode}");
71+
72+
if (!response.IsSuccessStatusCode)
73+
{
74+
var message = $"Specialize MSI sidecar call failed. StatusCode={response.StatusCode}";
75+
_logger.LogError(message);
76+
return message;
77+
}
78+
}
79+
}
80+
81+
return null;
82+
}
83+
4584
public bool StartAssignment(HostAssignmentContext context)
4685
{
4786
if (!_webHostEnvironment.InStandbyMode)

src/WebJobs.Script.WebHost/Models/HostAssignmentContext.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public class HostAssignmentContext
2121
[JsonProperty("lastModifiedTime")]
2222
public DateTime LastModifiedTime { get; set; }
2323

24+
[JsonProperty("MSISpecializationPayload")]
25+
public MSIContext MSIContext { get; set; }
26+
2427
public string ZipUrl
2528
{
2629
get
@@ -44,6 +47,21 @@ public string ZipUrl
4447
}
4548
}
4649

50+
public bool IsMSIEnabled(out string endpoint)
51+
{
52+
endpoint = null;
53+
if (Environment.TryGetValue(EnvironmentSettingNames.MsiEndpoint, out endpoint))
54+
{
55+
string secret;
56+
if (Environment.TryGetValue(EnvironmentSettingNames.MsiSecret, out secret))
57+
{
58+
return !string.IsNullOrEmpty(endpoint) && !string.IsNullOrEmpty(secret);
59+
}
60+
}
61+
62+
return false;
63+
}
64+
4765
public bool Equals(HostAssignmentContext other)
4866
{
4967
if (other == null)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Models
7+
{
8+
public class MSIContext
9+
{
10+
public string MSISecret { get; set; }
11+
12+
public IEnumerable<ManagedServiceIdentity> Identities { get; set; }
13+
}
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Models
5+
{
6+
public class ManagedServiceIdentity
7+
{
8+
public ManagedServiceIdentityType Type { get; set; }
9+
10+
public string ClientId { get; set; }
11+
12+
public string TenantId { get; set; }
13+
14+
public string Thumbprint { get; set; }
15+
16+
public string SecretUrl { get; set; }
17+
18+
public string ResourceId { get; set; }
19+
20+
public string Certificate { get; set; }
21+
}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Runtime.Serialization;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Models
8+
{
9+
[Flags]
10+
public enum ManagedServiceIdentityType
11+
{
12+
[EnumMember]
13+
None = 0,
14+
[EnumMember]
15+
SystemAssigned = 1,
16+
[EnumMember]
17+
UserAssigned = 2
18+
}
19+
}

src/WebJobs.Script/Diagnostics/MetricEventNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ public static class MetricEventNames
4444
public const string LinuxContainerSpecializationZipWrite = "linux.container.specialization.zip.write";
4545
public const string LinuxContainerSpecializationZipHead = "linux.container.specialization.zip.head";
4646
public const string LinuxContainerSpecializationFuseMount = "linux.container.specialization.zip.mount";
47+
public const string LinuxContainerSpecializationMSIInit = "linux.container.specialization.msi.init";
4748
}
4849
}

src/WebJobs.Script/Environment/EnvironmentSettingNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public static class EnvironmentSettingNames
3636
public const string AzureWebJobsSecretStorageKeyVaultConnectionString = "AzureWebJobsSecretStorageKeyVaultConnectionString";
3737
public const string AzureWebsiteArmCacheEnabled = "WEBSITE_FUNCTIONS_ARMCACHE_ENABLED";
3838
public const string MountEnabled = "WEBSITE_MOUNT_ENABLED";
39+
public const string MsiEndpoint = "MSI_ENDPOINT";
40+
public const string MsiSecret = "MSI_SECRET";
3941

4042
/// <summary>
4143
/// Environment variable dynamically set by the platform when it is safe to

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public static class ScriptConstants
115115
public const string LinuxLogEventStreamName = "MS_FUNCTION_LOGS";
116116
public const string LinuxMetricEventStreamName = "MS_FUNCTION_METRICS";
117117
public const string LinuxFunctionDetailsEventStreamName = "MS_FUNCTION_DETAILS";
118+
public const string LinuxMSISpecializationStem = "/api/specialize?api-version=2017-09-01";
118119

119120
public const string DurableTaskPropertyName = "durableTask";
120121
public const string DurableTaskHubName = "HubName";

0 commit comments

Comments
 (0)