diff --git a/.github/workflows/keyfactor-bootstrap-workflow-v3.yml b/.github/workflows/keyfactor-bootstrap-workflow-v3.yml deleted file mode 100644 index 64919a4..0000000 --- a/.github/workflows/keyfactor-bootstrap-workflow-v3.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Keyfactor Bootstrap Workflow - -on: - workflow_dispatch: - pull_request: - types: [opened, closed, synchronize, edited, reopened] - push: - create: - branches: - - 'release-*.*' - -jobs: - call-starter-workflow: - uses: keyfactor/actions/.github/workflows/starter.yml@v3 - secrets: - token: ${{ secrets.V2BUILDTOKEN}} - APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}} - gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} - gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} - scan_token: ${{ secrets.SAST_TOKEN }} diff --git a/.github/workflows/keyfactor-bootstrap-workflow.yml b/.github/workflows/keyfactor-bootstrap-workflow.yml index 6d8de53..bd5f384 100644 --- a/.github/workflows/keyfactor-bootstrap-workflow.yml +++ b/.github/workflows/keyfactor-bootstrap-workflow.yml @@ -11,9 +11,17 @@ on: jobs: call-starter-workflow: - uses: keyfactor/actions/.github/workflows/starter.yml@v2 + uses: keyfactor/actions/.github/workflows/starter.yml@v4 + with: + command_token_url: ${{ vars.COMMAND_TOKEN_URL }} + command_hostname: ${{ vars.COMMAND_HOSTNAME }} + command_base_api_path: ${{ vars.COMMAND_API_PATH }} secrets: token: ${{ secrets.V2BUILDTOKEN}} - APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}} gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} + scan_token: ${{ secrets.SAST_TOKEN }} + entra_username: ${{ secrets.DOCTOOL_ENTRA_USERNAME }} + entra_password: ${{ secrets.DOCTOOL_ENTRA_PASSWD }} + command_client_id: ${{ secrets.COMMAND_CLIENT_ID }} + command_client_secret: ${{ secrets.COMMAND_CLIENT_SECRET }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29..f4d155a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## 1.0.1 +* added retrieval of roles associated with enrolled certificates via metadata for Vault Enterprise users + +## 1.0.0 +* initial release \ No newline at end of file diff --git a/hashicorp-vault-cagateway/APIProxy/CertResponse.cs b/hashicorp-vault-cagateway/APIProxy/CertResponse.cs index 305ec3f..1546191 100644 --- a/hashicorp-vault-cagateway/APIProxy/CertResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/CertResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/ErrorResponse.cs b/hashicorp-vault-cagateway/APIProxy/ErrorResponse.cs index 8da94ad..340ec90 100644 --- a/hashicorp-vault-cagateway/APIProxy/ErrorResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/ErrorResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/KeyedList.cs b/hashicorp-vault-cagateway/APIProxy/KeyedList.cs index 9fe9ecc..ba94423 100644 --- a/hashicorp-vault-cagateway/APIProxy/KeyedList.cs +++ b/hashicorp-vault-cagateway/APIProxy/KeyedList.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/MetadataResponse.cs b/hashicorp-vault-cagateway/APIProxy/MetadataResponse.cs new file mode 100644 index 0000000..42481b2 --- /dev/null +++ b/hashicorp-vault-cagateway/APIProxy/MetadataResponse.cs @@ -0,0 +1,30 @@ +// Copyright 2025 Keyfactor +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions +// and limitations under the License. + +using System; +using System.Text.Json.Serialization; + +namespace Keyfactor.Extensions.CAPlugin.HashicorpVault.APIProxy +{ + public class MetadataResponse + { + [JsonPropertyName("issuer_id")] + public string IssuerId { get; set; } + + [JsonPropertyName("expiration")] + public DateTime? Expiration { get; set; } + + [JsonPropertyName("cert_metadata")] + public string CertMetadata { get; set; } + + [JsonPropertyName("role")] + public string Role { get; set; } + + [JsonPropertyName("serial_number")] + public string SerialNumber { get; set; } + } +} diff --git a/hashicorp-vault-cagateway/APIProxy/RevokeRequest.cs b/hashicorp-vault-cagateway/APIProxy/RevokeRequest.cs index 7a4421a..f974359 100644 --- a/hashicorp-vault-cagateway/APIProxy/RevokeRequest.cs +++ b/hashicorp-vault-cagateway/APIProxy/RevokeRequest.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/RevokeResponse.cs b/hashicorp-vault-cagateway/APIProxy/RevokeResponse.cs index 6a1e144..aecfe80 100644 --- a/hashicorp-vault-cagateway/APIProxy/RevokeResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/RevokeResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/SealStatusResponse.cs b/hashicorp-vault-cagateway/APIProxy/SealStatusResponse.cs index e6cb1c3..9d86d04 100644 --- a/hashicorp-vault-cagateway/APIProxy/SealStatusResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/SealStatusResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/SignRequest.cs b/hashicorp-vault-cagateway/APIProxy/SignRequest.cs index f0360f2..1e65db2 100644 --- a/hashicorp-vault-cagateway/APIProxy/SignRequest.cs +++ b/hashicorp-vault-cagateway/APIProxy/SignRequest.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/SignResponse.cs b/hashicorp-vault-cagateway/APIProxy/SignResponse.cs index 2aa29d7..882fbc9 100644 --- a/hashicorp-vault-cagateway/APIProxy/SignResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/SignResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/TokenLookupResponse.cs b/hashicorp-vault-cagateway/APIProxy/TokenLookupResponse.cs index 0b7d141..e72f5e8 100644 --- a/hashicorp-vault-cagateway/APIProxy/TokenLookupResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/TokenLookupResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs b/hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs index 7725dd9..44035ae 100644 --- a/hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs +++ b/hashicorp-vault-cagateway/APIProxy/WrappedResponse.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs b/hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs index 3ab133d..8edb068 100644 --- a/hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs +++ b/hashicorp-vault-cagateway/Client/HashicorpVaultClient.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -246,6 +246,41 @@ public async Task> GetRoleNamesAsync() finally { logger.MethodExit(); } } + /// + /// Retreives the metadata for the certificate + /// + /// + /// + public async Task GetCertMetadata(string certSerial) + { + logger.MethodEntry(); + + try + { + var res = await _vaultHttp.GetAsync>($"cert-metadata/{certSerial}"); + var md = res?.Data; + if (md != null) + { + logger.LogTrace($"got response from cert-metadata"); + logger.LogTrace($"serial number: {md.SerialNumber}"); + logger.LogTrace($"issuer id: {md.IssuerId}"); + logger.LogTrace($"expiration: {md.Expiration}"); + logger.LogTrace($"metadata: {md.CertMetadata}"); + logger.LogTrace($"role: {md.Role}"); + } + else { + logger.LogTrace($"no metadata associated with cert {certSerial} could be found."); + } + return md; + } + catch (Exception ex) + { + logger.LogError($"an error occurred when attempting to retreive the certificate metadata: {ex.Message}"); + throw; + } + finally { logger.MethodExit(); } + } + private void SetClientValuesFromConfigs(HashicorpVaultCAConfig caConfig, HashicorpVaultCATemplateConfig templateConfig) { logger.MethodEntry(); diff --git a/hashicorp-vault-cagateway/Client/VaultHttp.cs b/hashicorp-vault-cagateway/Client/VaultHttp.cs index c1c5f99..fda1eb2 100644 --- a/hashicorp-vault-cagateway/Client/VaultHttp.cs +++ b/hashicorp-vault-cagateway/Client/VaultHttp.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -77,6 +77,7 @@ public async Task GetAsync(string path, Dictionary paramet if (parameters != null) { request.AddJsonBody(parameters); } var response = await _restClient.ExecuteGetAsync(request); + logger.LogTrace($"raw response: {response.Content}"); response.ThrowIfError(); diff --git a/hashicorp-vault-cagateway/Constants.cs b/hashicorp-vault-cagateway/Constants.cs index d82b13b..3a38d15 100644 --- a/hashicorp-vault-cagateway/Constants.cs +++ b/hashicorp-vault-cagateway/Constants.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/hashicorp-vault-cagateway/HashicorpVaultCAConfig.cs b/hashicorp-vault-cagateway/HashicorpVaultCAConfig.cs index 0413b0c..5972502 100644 --- a/hashicorp-vault-cagateway/HashicorpVaultCAConfig.cs +++ b/hashicorp-vault-cagateway/HashicorpVaultCAConfig.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -23,8 +23,8 @@ public class HashicorpVaultCAConfig [JsonPropertyName(Constants.CAConfig.NAMESPACE)] public string Namespace { get; set; } - [JsonPropertyName(Constants.CAConfig.CLIENTCERT)] - public AuthCert ClientCertificate { get; set; } + //[JsonPropertyName(Constants.CAConfig.CLIENTCERT)] + //public AuthCert ClientCertificate { get; set; } [JsonPropertyName(Constants.CAConfig.ENABLED)] public bool Enabled { get; set; } diff --git a/hashicorp-vault-cagateway/HashicorpVaultCAConnector.cs b/hashicorp-vault-cagateway/HashicorpVaultCAConnector.cs index e020e9e..597cf95 100644 --- a/hashicorp-vault-cagateway/HashicorpVaultCAConnector.cs +++ b/hashicorp-vault-cagateway/HashicorpVaultCAConnector.cs @@ -1,4 +1,4 @@ -// Copyright 2024 Keyfactor +// Copyright 2025 Keyfactor // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -263,7 +263,7 @@ public async Task Synchronize(BlockingCollection blockin } logger.LogTrace($"converting {certSerial} to database trackingId"); - var trackingId = certSerial.Replace(":", "-"); // we store with '-'; hashi stores with ':' + var trackingId = certSerial.Replace(":", "-"); // we store with '-'; hashi stores with ':' // then, check for an existing local entry try @@ -280,14 +280,33 @@ public async Task Synchronize(BlockingCollection blockin { logger.LogTrace($"adding cert with serial {trackingId} to the database. fullsync is {fullSync}, and the certificate {(dbStatus == -1 ? "does not yet exist" : "already exists")} in the database."); + logger.LogTrace("attempting to retreive the role name (productId) from the certificate metadata, if available"); + + var metaData = new MetadataResponse(); + + try + { + metaData = await _client.GetCertMetadata(certSerial); + } + catch (Exception) + { + logger.LogTrace("an error occurred when attempting to retreive the metadata, continuing.."); + } + var newCert = new AnyCAPluginCertificate { CARequestID = trackingId, Certificate = certFromVault.Certificate, Status = certFromVault.RevocationTime != null ? (int)EndEntityStatus.REVOKED : (int)EndEntityStatus.GENERATED, - RevocationDate = certFromVault.RevocationTime, + RevocationDate = certFromVault.RevocationTime, }; + // if we were able to get the role name from metadata, we include it + if (!string.IsNullOrEmpty(metaData?.Role)) + { + newCert.ProductID = metaData.Role; + } + try { logger.LogTrace($"writing the result."); @@ -327,8 +346,9 @@ public async Task Synchronize(BlockingCollection blockin /// /// The information used to connect to the CA. public async Task ValidateCAConnectionInfo(Dictionary connectionInfo) - { + { logger.MethodEntry(); + logger.LogTrace(message: $"Validating CA connection info: {JsonSerializer.Serialize(connectionInfo)}"); // first, we check to see if the CA Gateway is enabled in the configuration if (!(bool)connectionInfo[Constants.CAConfig.ENABLED]) @@ -352,7 +372,10 @@ public async Task ValidateCAConnectionInfo(Dictionary connection // make sure an authentication mechanism is defined (either certificate or token) var token = connectionInfo[Constants.CAConfig.TOKEN] as string; - var cert = connectionInfo[Constants.CAConfig.CLIENTCERT] as string; + + //var cert = connectionInfo[Constants.CAConfig.CLIENTCERT] as string; + + var cert = string.Empty; // temporary until client cert auth into vault is implemented if (string.IsNullOrEmpty(token) && string.IsNullOrEmpty(cert)) { @@ -422,6 +445,9 @@ public async Task ValidateCAConnectionInfo(Dictionary connection public Task ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary connectionInfo) { logger.MethodEntry(); + + logger.LogTrace($"validating product info: {JsonSerializer.Serialize(productInfo)}"); + List errors = new List(); HashicorpVaultCATemplateConfig templateConfig = null; @@ -429,7 +455,7 @@ public Task ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary(JsonSerializer.Serialize(productInfo)); + templateConfig = JsonSerializer.Deserialize(JsonSerializer.Serialize(productInfo.ProductParameters)); caConfig = JsonSerializer.Deserialize(JsonSerializer.Serialize(connectionInfo)); logger.LogTrace("successfully deserialized the product and CA config values."); } @@ -439,12 +465,7 @@ public Task ValidateProductInfo(EnrollmentProductInfo productInfo, Dictionary