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

Hiding KeyVault secrets on Environment page #155

Merged
merged 2 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions Kudu.Core/Helpers/KeyVaultReferenceHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Kudu.Core.Helpers
{
public static class KeyVaultReferenceHelper
{
private const string AppSettingPrefix = "APPSETTING_";
private const string KeyVaultReferenceInfoEnvVar = "WEBSITE_KEYVAULT_REFERENCES";

/// Example dictionary:
/// {
/// "secret1":
/// {
/// { "rawReference", "@Microsoft.KeyVault(SecretUri=)" },
/// { "status", "ValueNotFound" }
/// }
/// }
/// </summary>
private static Dictionary<string, Dictionary<string, string>> KeyVaultReferencesInformation = GetKeyVaultReferencesInformation();

public static int NumKeyVaultReferences
{
get
{
return KeyVaultReferencesInformation.Count();
}
}

/// <summary>
/// Simple filter to hide secrets from KeyVault references.
/// </summary>
/// <param name="environmentVariables">All variables for the site</param>
/// <param name="hideKeyVaultSecrets">Whether to hide KeyVault secrets</param>
/// <returns>Filtered environment variables</returns>
public static IDictionary<object, object> KeyVaultReferencesFilter(IDictionary variables, bool hideKeyVaultSecrets)
{
IDictionary<object, object> filteredEnvironmentVariables = new Dictionary<object, object>();
foreach (var entry in variables.Keys)
{
filteredEnvironmentVariables.Add(entry, HideKeyVaultSecret(entry, variables[entry], hideKeyVaultSecrets));
}
return filteredEnvironmentVariables;
}

/// <summary>
/// Deserializes KeyVault References information in the form of a Dictionary<string, Dictionary<string, string>>
/// </summary>
/// <param name="serializedInformationBlob">Serialized dictionary containing KeyVault reference information</param>
public static Dictionary<string, Dictionary<string, string>> GetKeyVaultReferencesInformation()
{
try
{
var serializedInformationBlob = System.Environment.GetEnvironmentVariable(KeyVaultReferenceInfoEnvVar);
if (serializedInformationBlob != null)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(serializedInformationBlob);
if (result != null)
{
return result;
}
}
}
catch (Exception)
{
}

return new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
}

/// <summary>
/// Hides a KeyVault secret if enabled, or just returns the original value if disabled
/// </summary>
/// <param name="key">Environment variable key</param>
/// <param name="value">Environment variable original value</param>
/// <param name="hideValue">Whether to hide</param>
/// <returns>Hidden value (or original)</returns>
public static object HideKeyVaultSecret(object key, object value, bool hideValue)
{
var keyString = (string)key;
if (hideValue && KeyVaultReferencesInformation.ContainsKey(keyString))
{
try
{
return "[Hidden - " + KeyVaultReferencesInformation[keyString]["status"] + ": " + KeyVaultReferencesInformation[keyString]["rawReference"] + "]";
}
catch (Exception)
{
return "[Hidden KeyVault secret]";
}
}

return value;
}
}
}
71 changes: 56 additions & 15 deletions Kudu.Services.Web/Pages/Env.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@

<div class="container">
<h3>Index</h3>

@{
bool isHidden = String.IsNullOrEmpty(httpContextAccessor.HttpContext.Request.Query["hideSecrets"]) || ("true").Equals(httpContextAccessor.HttpContext.Request.Query["hideSecrets"]); // Hide by default
}
@if (Kudu.Core.Helpers.KeyVaultReferenceHelper.NumKeyVaultReferences > 0)
{
<div class="checkbox span5">
<label class="checkbox" style="padding:0px">
@Html.CheckBox("hideKeyVaultSecrets", isHidden, new { id = "hideKeyVaultSecrets", style = "padding:0px", onchange = "keyVaultCheckbox()" }) Hide Values of KeyVault References
</label>
</div>
}
<ul>
<li><a href="#sysInfo">System Info</a></li>
<li><a href="#appSettings">App Settings</a></li>
Expand Down Expand Up @@ -50,29 +62,34 @@
</ul>

<h3 id="appSettings">AppSettings</h3>
@{
var appSettingsInEnvironment = new Dictionary<string, string>(_settingsManager.GetValues());
var appSettingVariablesDict = Kudu.Core.Helpers.KeyVaultReferenceHelper.KeyVaultReferencesFilter(appSettingsInEnvironment, isHidden);
}
<ul class="fixed-width">
@using System.Configuration;
@using System.Linq
@using Core.Infrastructure
@using Kudu.Contracts.Settings
@using System.Linq
@using Core.Infrastructure
@using Kudu.Contracts.Settings
@using Microsoft.AspNetCore.Http
@foreach (string name in System.Configuration.ConfigurationManager.AppSettings)
{
<li>
@name = @System.Configuration.ConfigurationManager.AppSettings[name]
</li>
}

@foreach (KeyValuePair<string, string> kv in _settingsManager.GetValues())
{

if (kv.Value != null)
{
<li>@kv.Key = @kv.Value</li>
}

}
</ul>

@foreach (KeyValuePair<object, object> kv in appSettingVariablesDict)
{

if (kv.Value != null)
{
<li>@kv.Key = @kv.Value</li>

}

}
</ul>

<h3 id="connectionStrings">Connection Strings</h3>
<ul>
Expand All @@ -89,8 +106,12 @@
</ul>

<h3 id="envVariables">Environment variables</h3>
@{
IDictionary environmentVariables = Environment.GetEnvironmentVariables();
var environmentVariablesDict = Kudu.Core.Helpers.KeyVaultReferenceHelper.KeyVaultReferencesFilter(environmentVariables, isHidden);
}
<ul class="fixed-width">
@foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables().OfType<DictionaryEntry>().OrderBy(e => e.Key))
@foreach (KeyValuePair<object, object> entry in environmentVariablesDict.OfType<KeyValuePair<object, object>>().OrderBy(e => e.Key))
{
<li>@entry.Key = @entry.Value</li>
}
Expand Down Expand Up @@ -122,3 +143,23 @@
</ul>
*@
</div>

<script type="text/javascript">
// Flip whether to hide KeyVault secrets based on current request; Reload page with new parameter
function keyVaultCheckbox() {
var isHidden = '@isHidden' == 'True';
var flipHidden = (!isHidden).toString();
var paramName = "hideSecrets";

// Replace hideSecrets param in URL
var str = location.search;
if (new RegExp("[&?]" + paramName + "([=&].+)?$").test(str)) {
str = str.replace(new RegExp("(?:[&?])" + paramName + "[^&]*", "g"), "")
}
str += "&";
str += paramName + "=" + flipHidden;
str = "?" + str.slice(1);

location.assign(location.origin + location.pathname + str + location.hash);
}
</script>
83 changes: 62 additions & 21 deletions Kudu.Services.Web/Pages/NewUI/Env.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@

<div class="container">
<h3>Index</h3>

@{
bool isHidden = String.IsNullOrEmpty(httpContextAccessor.HttpContext.Request.Query["hideSecrets"]) || ("true").Equals(httpContextAccessor.HttpContext.Request.Query["hideSecrets"]); // Hide by default
}
@if (Kudu.Core.Helpers.KeyVaultReferenceHelper.NumKeyVaultReferences > 0)
{
<div class="checkbox span5">
<label class="checkbox" style="padding:0px">
@Html.CheckBox("hideKeyVaultSecrets", isHidden, new { id = "hideKeyVaultSecrets", style = "padding:0px", onchange = "keyVaultCheckbox()" }) Hide Values of KeyVault References
</label>
</div>
}
<ul>
<li><a href="#sysInfo">System Info</a></li>
<li><a href="#appSettings">App Settings</a></li>
Expand Down Expand Up @@ -50,29 +62,34 @@
</ul>

<h3 id="appSettings">AppSettings</h3>
@{
var appSettingsInEnvironment = new Dictionary<string, string>(_settingsManager.GetValues());
var appSettingVariablesDict = Kudu.Core.Helpers.KeyVaultReferenceHelper.KeyVaultReferencesFilter(appSettingsInEnvironment, isHidden);
}
<ul class="fixed-width">
@using System.Configuration;
@using System.Linq
@using Core.Infrastructure
@using Kudu.Contracts.Settings
@using System.Linq
@using Core.Infrastructure
@using Kudu.Contracts.Settings
@using Microsoft.AspNetCore.Http
@foreach (string name in System.Configuration.ConfigurationManager.AppSettings)
{
<li>
@name = @System.Configuration.ConfigurationManager.AppSettings[name]
</li>
}
@foreach (KeyValuePair<string, string> kv in _settingsManager.GetValues())

@foreach (KeyValuePair<object, object> kv in appSettingVariablesDict)
{

if (kv.Value != null)
{
<li>@kv.Key = @kv.Value</li>
}


if (kv.Value != null)
{
<li>@kv.Key = @kv.Value</li>

}

}
</ul>
</ul>

<h3 id="connectionStrings">Connection Strings</h3>
<ul>
Expand All @@ -89,8 +106,12 @@
</ul>

<h3 id="envVariables">Environment variables</h3>
@{
IDictionary environmentVariables = Environment.GetEnvironmentVariables();
var environmentVariablesDict = Kudu.Core.Helpers.KeyVaultReferenceHelper.KeyVaultReferencesFilter(environmentVariables, isHidden);
}
<ul class="fixed-width">
@foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables().OfType<DictionaryEntry>().OrderBy(e => e.Key))
@foreach (KeyValuePair<object, object> entry in environmentVariablesDict.OfType<KeyValuePair<object, object>>().OrderBy(e => e.Key))
{
<li>@entry.Key = @entry.Value</li>
}
Expand All @@ -113,12 +134,32 @@
</ul>

@*
<h3 id="serverVar">Server variables</h3>
<ul class="fixed-width">
@foreach (string name in HttpContext.Connection.OfType<string>().OrderBy(s => s))
{
<li>@name=@HttpContext.Connection[name]</li>
}
</ul>
*@
<h3 id="serverVar">Server variables</h3>
<ul class="fixed-width">
@foreach (string name in HttpContext.Connection.OfType<string>().OrderBy(s => s))
{
<li>@name=@HttpContext.Connection[name]</li>
}
</ul>
*@
</div>

<script type="text/javascript">
// Flip whether to hide KeyVault secrets based on current request; Reload page with new parameter
function keyVaultCheckbox() {
var isHidden = '@isHidden' == 'True';
var flipHidden = (!isHidden).toString();
var paramName = "hideSecrets";

// Replace hideSecrets param in URL
var str = location.search;
if (new RegExp("[&?]" + paramName + "([=&].+)?$").test(str)) {
str = str.replace(new RegExp("(?:[&?])" + paramName + "[^&]*", "g"), "")
}
str += "&";
str += paramName + "=" + flipHidden;
str = "?" + str.slice(1);

location.assign(location.origin + location.pathname + str + location.hash);
}
</script>