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

Adds toggle for recursive subfolder inventory #14

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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,10 @@ This integration was built on the .NET Core 3.1 target framework and are compati

1. It is not necessary to use the Vault root token when creating a Certificate Store for HashicorpVault. We recommend creating a token with policies that reflect the minimum permissions necessary to perform the intended operations.

1. For the Key-Value secrets engine, the certificates are stored as an entry with 3 fields.
1. For the Key-Value secrets engine, the certificates are stored as an entry with 2 fields.

- `PUBLIC_KEY` - The certificate public key
- `PRIVATE_KEY` - The certificate private key
- `KEY_SECRET` - The certificate private key password

## Extension Configuration

Expand Down Expand Up @@ -121,6 +120,7 @@ This integration was built on the .NET Core 3.1 target framework and are compati
- **MountPoint** - type: *string*
- **VaultServerUrl** - type: *string*, *required*
- **VaultToken** - type: *secret*, *required*
- **SubfolderInventory** - type: *bool* (By default, this is set to false. Not a required field)

![](images/store_type_fields.png)

Expand All @@ -144,6 +144,7 @@ In Keyfactor Command create a new Certificate Store that resembles the one below
- If left blank, will default to "kv-v2".
- **Vault Token** - This is the access token that will be used by the orchestrator for requests to Vault.
- **Vault Server Url** - the full url and port of the Vault server instance
- **Subfolder Inventory** - Set to 'True' if it is a requirement to inventory secrets at the subfolder/component level. The default, 'False' will inventory secrets stored at the root of the "Store Path", but will not look at secrets in subfolders.

### For the Keyfactor and PKI plugins

Expand Down Expand Up @@ -237,5 +238,4 @@ At this point you should be able to enroll a certificate and store it in Vault u

## Notes / Future Enhancements

- For the Key-Value stores we operate on a single version of the Key Value secret (no versioning capabilities through the Orchesterator Extension / Keyfactor).

- For the Key-Value stores we operate on a single version of the Key Value secret (no versioning capabilities through the Orchesterator Extension / Keyfactor).
93 changes: 70 additions & 23 deletions hashicorp-vault-orchestrator/HcvKeyValueClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ public class HcvKeyValueClient : IHashiClient

private string _storePath { get; set; }
private string _mountPoint { get; set; }
private bool _subfolderInventory { get; set; }

//private VaultClientSettings clientSettings { get; set; }

public HcvKeyValueClient(string vaultToken, string serverUrl, string mountPoint, string storePath)
public HcvKeyValueClient(string vaultToken, string serverUrl, string mountPoint, string storePath,bool SubfolderInventory = false)
{
// Initialize one of the several auth methods.
IAuthMethodInfo authMethod = new TokenAuthMethodInfo(vaultToken);
Expand All @@ -48,18 +49,47 @@ public HcvKeyValueClient(string vaultToken, string serverUrl, string mountPoint,
_mountPoint = mountPoint;
_storePath = !string.IsNullOrEmpty(storePath) ? "/" + storePath : storePath;
_vaultClient = new VaultClient(clientSettings);
_subfolderInventory = SubfolderInventory;
}
public async Task<List<string>> ListComponentPathsAsync(string storagePath)
{
VaultClient.V1.Auth.ResetVaultToken();
List<string> componentPaths = new List<string> {};
try
{
Secret<ListInfo> listInfo = (await VaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(storagePath, _mountPoint));

foreach (var path in listInfo.Data.Keys)
{
if (!path.EndsWith("/"))
{
continue;
}

string fullPath = $"{storagePath}{path}";
componentPaths.Add(fullPath);

List<string> subPaths = await ListComponentPathsAsync(fullPath);
componentPaths.AddRange(subPaths);
}
}
catch (Exception ex)
{
logger.LogWarning($"Error while listing component paths: {ex}");
}
return componentPaths;
}
public async Task<CurrentInventoryItem> GetCertificate(string key)
{
VaultClient.V1.Auth.ResetVaultToken();

Dictionary<string, object> certData;
Secret<SecretData> res;

var fullPath = _storePath + key;
var relativePath = fullPath.Substring(_storePath.Length);
try
{
var fullPath = _storePath + key;


try
{
Expand All @@ -74,7 +104,7 @@ public async Task<CurrentInventoryItem> GetCertificate(string key)
}
catch (Exception ex)
{
logger.LogWarning("Error getting certificate (deleted?)", ex);
logger.LogWarning($"Error getting certificate (deleted?) {fullPath}", ex);
return null;
}

Expand All @@ -99,7 +129,7 @@ public async Task<CurrentInventoryItem> GetCertificate(string key)

return new CurrentInventoryItem()
{
Alias = key,
Alias = relativePath,
PrivateKeyEntry = hasPrivateKey,
ItemStatus = OrchestratorInventoryItemStatus.Unknown,
UseChainLevel = true,
Expand Down Expand Up @@ -247,31 +277,48 @@ public async Task<IEnumerable<CurrentInventoryItem>> GetCertificates()
{
VaultClient.V1.Auth.ResetVaultToken();
_storePath = _storePath.TrimStart('/');
List<string> subPaths = new List<string>();
//Grabs the list of subpaths to get certificates from, if SubFolder Inventory is turned on.
//Otherwise just define the single path _storePath
if (_subfolderInventory == true)
{
subPaths = (await ListComponentPathsAsync(_storePath));
subPaths.Add(_storePath);
}
else
{
subPaths.Add(_storePath);
}
var certs = new List<CurrentInventoryItem>();
var certNames = new List<string>();
try
logger.LogDebug($"SubInventoryEnabled: {_subfolderInventory}");
foreach (var path in subPaths)
{
if (string.IsNullOrEmpty(_mountPoint))
var relative_path = path.Substring(_storePath.Length);
try
{
certNames = (await VaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(_storePath)).Data.Keys.ToList();

if (string.IsNullOrEmpty(_mountPoint))
{
certNames = (await VaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(path)).Data.Keys.ToList();
}
else
{
certNames = (await VaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(path, mountPoint: _mountPoint)).Data.Keys.ToList();
}

certNames.ForEach(k =>
{
var cert = GetCertificate($"{relative_path}{k}").Result;
if (cert != null) certs.Add(cert);
});
}
else
catch (Exception ex)
{
certNames = (await VaultClient.V1.Secrets.KeyValue.V2.ReadSecretPathsAsync(_storePath, mountPoint: _mountPoint)).Data.Keys.ToList();
logger.LogError(ex.Message);
throw ex;
}

certNames.ForEach(k =>
{
var cert = GetCertificate(k).Result;
if (cert != null) certs.Add(cert);
});
}
catch (Exception ex)
{
logger.LogError(ex.Message);
throw ex;
}

return certs;
}
private static Func<string, string> Pemify = base64Cert =>
Expand All @@ -285,4 +332,4 @@ public async Task<IEnumerable<CurrentInventoryItem>> GetCertificates()
return header + FormatBase64(base64Cert) + footer;
};
}
}
}
14 changes: 12 additions & 2 deletions hashicorp-vault-orchestrator/Jobs/JobBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public abstract class JobBase
public string SecretsEngine { get; set; } // "PKI", "Keyfactor", "Key Value"

public string VaultServerUrl { get; set; }

public bool SubfolderInventory { get; set; }

public string MountPoint { get; set; } // the mount point of the KV secrets engine. defaults to KV

Expand Down Expand Up @@ -69,12 +71,20 @@ private void InitProps(dynamic props, string capability)
VaultServerUrl = props["VaultServerUrl"];
SecretsEngine = props["SecretsEngine"];
MountPoint = props["MountPoint"] ?? null;
if (props["SubfolderInventory"] == null)
{
SubfolderInventory = false;
}
else
{
SubfolderInventory = props["SubfolderInventory"];
}

var isPki = capability.Contains("HCVPKI");

if (!isPki)
{
VaultClient = new HcvKeyValueClient(VaultToken, VaultServerUrl, MountPoint, StorePath);
VaultClient = new HcvKeyValueClient(VaultToken, VaultServerUrl, MountPoint, StorePath, SubfolderInventory);
}
else
{
Expand All @@ -83,4 +93,4 @@ private void InitProps(dynamic props, string capability)

}
}
}
}
Loading