Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
035f66d
Update generated docs
Aug 6, 2025
0d9c696
ab#74559
Aug 6, 2025
f16fe34
Merge branch 'ab#74559' of https://github.com/Keyfactor/remote-file-o…
Aug 6, 2025
68cd3fc
ab#74559
Aug 11, 2025
50ca99f
Update generated docs
Aug 11, 2025
73878da
ab#74559
Aug 11, 2025
3920d58
Merge branch 'ab#74559' of https://github.com/Keyfactor/remote-file-o…
Aug 11, 2025
e6047a8
Update generated docs
Aug 11, 2025
45c84b8
ab#74559
Aug 11, 2025
f5bd04c
Merge branch 'ab#74559' of https://github.com/Keyfactor/remote-file-o…
Aug 11, 2025
ae0124f
Update generated docs
Aug 11, 2025
252118e
ab#74559
Aug 11, 2025
11a9b4a
Merge branch 'ab#74559' of https://github.com/Keyfactor/remote-file-o…
Aug 11, 2025
b6fed1a
Update generated docs
Aug 11, 2025
14f731e
ab#74559
Aug 11, 2025
8444eaf
Merge branch 'ab#74559' of https://github.com/Keyfactor/remote-file-o…
Aug 11, 2025
2745dd5
Merge pull request #87 from Keyfactor/ab#74559
spbsoluble Aug 14, 2025
7930e17
Merge pull request #88 from Keyfactor/release-2.11
spbsoluble Aug 14, 2025
f867239
ab#75723
Sep 8, 2025
40cdcfb
Update generated docs
Sep 8, 2025
624095c
ab#75723
Sep 8, 2025
4e149b1
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 8, 2025
def5106
Update generated docs
Sep 8, 2025
7249433
ab#75723
Sep 8, 2025
b232de1
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 8, 2025
597fcbe
ab#75273
Sep 8, 2025
fa165a1
Update generated docs
Sep 8, 2025
98567e4
ab#75723
Sep 8, 2025
1e10d80
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 8, 2025
234d46b
Update generated docs
Sep 8, 2025
d4b3dd6
ab#75723
Sep 9, 2025
6d7455d
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 9, 2025
6ba9e5f
ab#75723
Sep 9, 2025
fd738cc
Update generated docs
Sep 9, 2025
6163d96
ab#75723
Sep 9, 2025
06d5161
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 9, 2025
c2af492
Update generated docs
Sep 9, 2025
53410ec
ab#75723
Sep 10, 2025
f0dc3dc
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 10, 2025
e761681
fix(ci): Update bootstrap workflow to `v4`
spbsoluble Sep 10, 2025
83f28dc
Update generated docs
Sep 10, 2025
c93df78
ab#75723
Sep 11, 2025
9d4788d
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 11, 2025
4d5b4c5
Update generated docs
Sep 11, 2025
700bbcb
Merge branch 'ab#76170' into ab#75723
leefine02 Sep 19, 2025
a160410
Update generated docs
Sep 19, 2025
cf47ceb
ab#75723
Sep 25, 2025
d463a68
Merge branch 'ab#75723' of https://github.com/Keyfactor/remote-file-o…
Sep 25, 2025
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
27 changes: 0 additions & 27 deletions .github/workflows/keyfactor-merge-store-types.yml

This file was deleted.

19 changes: 13 additions & 6 deletions .github/workflows/keyfactor-starter-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ on:

jobs:
call-starter-workflow:
uses: keyfactor/actions/.github/workflows/starter.yml@3.1.2
uses: keyfactor/actions/.github/workflows/starter.yml@v4
with:
command_token_url: ${{ vars.COMMAND_TOKEN_URL }} # Only required for doctool generated screenshots
command_hostname: ${{ vars.COMMAND_HOSTNAME }} # Only required for doctool generated screenshots
command_base_api_path: ${{ vars.COMMAND_API_PATH }} # Only required for doctool generated screenshots
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 }}
token: ${{ secrets.V2BUILDTOKEN}} # REQUIRED
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} # Only required for golang builds
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} # Only required for golang builds
scan_token: ${{ secrets.SAST_TOKEN }} # REQUIRED
entra_username: ${{ secrets.DOCTOOL_ENTRA_USERNAME }} # Only required for doctool generated screenshots
entra_password: ${{ secrets.DOCTOOL_ENTRA_PASSWD }} # Only required for doctool generated screenshots
command_client_id: ${{ secrets.COMMAND_CLIENT_ID }} # Only required for doctool generated screenshots
command_client_secret: ${{ secrets.COMMAND_CLIENT_SECRET }} # Only required for doctool generated screenshots
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
v2.12.0
- Added config.json setting and its override store level custom field - AllowShellCommands. If "N" (default "Y"), SFTP will be used to create stores and move files on Linux-based certificate store servers. No Linux shell commands will be used in the integration.

v2.11.5
- Bug Fix: Rare race condition loading config settings when multiple RemoteFile jobs are running simultaneously on the same orchestrator
- Documentation update to better list out what Linux commands get executed under what situations in Requirements & Prerequisites section

v2.11.4
- Bug Fix: Handle condition where a certificate store definition that contains an invalid value for `FileTransferProtocol`
would return empty inventory. If no value is set or an invalid value is set, the default value of `Both` will be used
Expand Down
89 changes: 70 additions & 19 deletions README.md

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions RemoteFile.UnitTests/ApplicationSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ public class ApplicationSettingsTests
[Fact]
public void FileTransferProtocol_WhenPopulatedWithValidValue_ReturnsValue()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "fixtures", "config", "valid", "config.json");
ApplicationSettings.Initialize(path);
Assert.Equal(ApplicationSettings.FileTransferProtocolEnum.SCP, ApplicationSettings.FileTransferProtocol);
}

[Fact]
public void FileTransferProtocol_WhenAllThreePopulated_DefaultsToBoth()
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "fixtures", "config", "file_transfer_protocol_all_three", "config.json");
ApplicationSettings.Initialize(path);
Assert.Equal(ApplicationSettings.FileTransferProtocolEnum.Both, ApplicationSettings.FileTransferProtocol);
}
}
6 changes: 4 additions & 2 deletions RemoteFile/ApplicationSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Newtonsoft.Json;
using Microsoft.Extensions.Logging;
using Keyfactor.Logging;
using System.Reflection;


namespace Keyfactor.Extensions.Orchestrator.RemoteFile
Expand Down Expand Up @@ -41,6 +42,7 @@
public static string DefaultSudoImpersonatedUser { get { return configuration.ContainsKey("DefaultSudoImpersonatedUser") ? configuration["DefaultSudoImpersonatedUser"] : DEFAULT_SUDO_IMPERSONATION_SETTING; } }
public static bool CreateCSROnDevice { get { return configuration.ContainsKey("CreateCSROnDevice") ? configuration["CreateCSROnDevice"]?.ToUpper() == "Y" : false; } }
public static string TempFilePathForODKG { get { return configuration.ContainsKey("TempFilePathForODKG") ? configuration["TempFilePathForODKG"] : string.Empty; } }
public static bool UseShellCommands { get { return configuration.ContainsKey("UseShellCommands") ? configuration["UseShellCommands"]?.ToUpper() == "Y" : true; } }
public static int SSHPort
{
get
Expand Down Expand Up @@ -71,7 +73,7 @@
protocolNames += protocolName + ", ";
}
protocolNames = protocolNames.Substring(0, protocolNames.Length - 2);
string? protocolValue = configuration["FileTransferProtocol"];

Check warning on line 76 in RemoteFile/ApplicationSettings.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 76 in RemoteFile/ApplicationSettings.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 76 in RemoteFile/ApplicationSettings.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 76 in RemoteFile/ApplicationSettings.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

if (!PropertyUtilities.TryEnumParse(protocolValue, out bool isFlagCombination, out FileTransferProtocolEnum protocol))
throw new RemoteFileException($"Invalid optional config.json FileTransferProtocol option of {protocolValue}. If present, must be one of these values: {protocolNames}.");
Expand All @@ -88,13 +90,13 @@
}
}

public static void Initialize(string configLocation)
static ApplicationSettings()
{
ILogger logger = LogHandler.GetClassLogger<ApplicationSettings>();
logger.MethodEntry(LogLevel.Debug);

configuration = new Dictionary<string, string>();
configLocation = $"{Path.GetDirectoryName(configLocation)}{Path.DirectorySeparatorChar}config.json";
string configLocation = $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}config.json";
string configContents = string.Empty;

if (!File.Exists(configLocation))
Expand Down
4 changes: 1 addition & 3 deletions RemoteFile/Discovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ public JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDiscoveryUpd
string userName = PAMUtilities.ResolvePAMField(_resolver, logger, "Server User Name", config.ServerUsername);
string userPassword = PAMUtilities.ResolvePAMField(_resolver, logger, "Server Password", config.ServerPassword);

ApplicationSettings.Initialize(this.GetType().Assembly.Location);

certificateStore = new RemoteCertificateStore(config.ClientMachine, userName, userPassword, directoriesToSearch[0].Substring(0, 1) == "/" ? RemoteCertificateStore.ServerTypeEnum.Linux : RemoteCertificateStore.ServerTypeEnum.Windows, ApplicationSettings.SSHPort);
certificateStore.Initialize(ApplicationSettings.DefaultSudoImpersonatedUser);
certificateStore.Initialize(ApplicationSettings.DefaultSudoImpersonatedUser, true);

if (directoriesToSearch.Length == 0)
throw new RemoteFileException("Blank or missing search directories for Discovery.");
Expand Down
4 changes: 1 addition & 3 deletions RemoteFile/InventoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd

try
{
ApplicationSettings.Initialize(this.GetType().Assembly.Location);

SetJobProperties(config, config.CertificateStoreDetails, logger);

certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, UserName, UserPassword, config.CertificateStoreDetails.StorePath, StorePassword, FileTransferProtocol, SSHPort, IncludePortInSPN);
certificateStore.Initialize(SudoImpersonatedUser);
certificateStore.Initialize(SudoImpersonatedUser, UseShellCommands);
certificateStore.LoadCertificateStore(certificateStoreSerializer, true);

List<X509Certificate2Collection> collections = certificateStore.GetCertificateChains();
Expand Down
4 changes: 1 addition & 3 deletions RemoteFile/ManagementBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ public JobResult ProcessJob(ManagementJobConfiguration config)

try
{
ApplicationSettings.Initialize(this.GetType().Assembly.Location);

SetJobProperties(config, config.CertificateStoreDetails, logger);

certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, UserName, UserPassword, config.CertificateStoreDetails.StorePath, StorePassword, FileTransferProtocol, SSHPort, IncludePortInSPN);
certificateStore.Initialize(SudoImpersonatedUser);
certificateStore.Initialize(SudoImpersonatedUser, UseShellCommands);

PathFile storePathFile = RemoteCertificateStore.SplitStorePathFile(config.CertificateStoreDetails.StorePath);

Expand Down
4 changes: 1 addition & 3 deletions RemoteFile/ReenrollmentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ public JobResult ProcessJobToDo(ReenrollmentJobConfiguration config, SubmitReenr

try
{
ApplicationSettings.Initialize(this.GetType().Assembly.Location);

SetJobProperties(config, config.CertificateStoreDetails, logger);

string alias = "abcd";
Expand All @@ -68,7 +66,7 @@ public JobResult ProcessJobToDo(ReenrollmentJobConfiguration config, SubmitReenr
ApplicationSettings.FileTransferProtocolEnum fileTransferProtocol = ApplicationSettings.FileTransferProtocol;

certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, UserName, UserPassword, config.CertificateStoreDetails.StorePath, StorePassword, fileTransferProtocol, SSHPort, IncludePortInSPN);
certificateStore.Initialize(SudoImpersonatedUser);
certificateStore.Initialize(SudoImpersonatedUser, UseShellCommands);

PathFile storePathFile = RemoteCertificateStore.SplitStorePathFile(config.CertificateStoreDetails.StorePath);

Expand Down
4 changes: 2 additions & 2 deletions RemoteFile/RemoteCertificateStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,14 +453,14 @@ internal string GenerateCSROnDevice(string subjectText, SupportedKeyTypeEnum key
return csr;
}

internal void Initialize(string sudoImpersonatedUser)
internal void Initialize(string sudoImpersonatedUser, bool useShellCommands)
{
logger.MethodEntry(LogLevel.Debug);

bool treatAsLocal = Server.ToLower().EndsWith(LOCAL_MACHINE_SUFFIX);

if (ServerType == ServerTypeEnum.Linux || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
RemoteHandler = treatAsLocal ? new LinuxLocalHandler() as IRemoteHandler : new SSHHandler(Server, ServerId, ServerPassword, ServerType == ServerTypeEnum.Linux, FileTransferProtocol, SSHPort, sudoImpersonatedUser) as IRemoteHandler;
RemoteHandler = treatAsLocal ? new LinuxLocalHandler() as IRemoteHandler : new SSHHandler(Server, ServerId, ServerPassword, ServerType == ServerTypeEnum.Linux, FileTransferProtocol, SSHPort, sudoImpersonatedUser, useShellCommands) as IRemoteHandler;
else
RemoteHandler = new WinRMHandler(Server, ServerId, ServerPassword, treatAsLocal, IncludePortInSPN);

Expand Down
7 changes: 6 additions & 1 deletion RemoteFile/RemoteFileJobTypeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public abstract class RemoteFileJobTypeBase
internal bool IncludePortInSPN { get; set; }
internal ApplicationSettings.FileTransferProtocolEnum FileTransferProtocol { get; set; }
internal bool CreateCSROnDevice { get; set; }
internal bool UseShellCommands { get; set; }
internal string KeyType { get; set; }
internal int KeySize { get; set; }
internal string SubjectText { get; set; }
Expand Down Expand Up @@ -57,7 +58,7 @@ internal void SetJobProperties(JobConfiguration config, CertificateStore certifi
ApplicationSettings.DefaultSudoImpersonatedUser :
properties.SudoImpersonatedUser.Value;

SSHPort = properties.SSHPort == null || string.IsNullOrEmpty(properties.SSHPort.Value) || !int.TryParse(properties.SSHPort.Value, out int notUsed) ?
SSHPort = properties.SSHPort == null || string.IsNullOrEmpty(properties.SSHPort.Value) || !int.TryParse(properties.SSHPort.Value, out int _) ?
ApplicationSettings.SSHPort :
properties.SSHPort;

Expand All @@ -73,6 +74,10 @@ internal void SetJobProperties(JobConfiguration config, CertificateStore certifi
ApplicationSettings.CreateCSROnDevice :
Convert.ToBoolean(properties.CreateCSROnDevice.Value);

UseShellCommands = properties.UseShellCommands == null || string.IsNullOrEmpty(properties.UseShellCommands.Value) ?
ApplicationSettings.UseShellCommands :
properties.UseShellCommands;

FileTransferProtocol = ApplicationSettings.FileTransferProtocol;
if (properties.FileTransferProtocol != null && !string.IsNullOrEmpty(properties.FileTransferProtocol.Value))
{
Expand Down
78 changes: 49 additions & 29 deletions RemoteFile/RemoteHandlers/SSHHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,20 @@ class SSHHandler : BaseRemoteHandler
private string SudoImpersonatedUser { get; set; }
private ApplicationSettings.FileTransferProtocolEnum FileTransferProtocol { get; set; }
private bool IsStoreServerLinux { get; set; }
private bool UseShellCommands { get; set; }
private string UserId { get; set; }
private string Password { get; set; }
private SshClient sshClient;

internal SSHHandler(string server, string serverLogin, string serverPassword, bool isStoreServerLinux, ApplicationSettings.FileTransferProtocolEnum fileTransferProtocol, int sshPort, string sudoImpersonatedUser)
internal SSHHandler(string server, string serverLogin, string serverPassword, bool isStoreServerLinux, ApplicationSettings.FileTransferProtocolEnum fileTransferProtocol, int sshPort, string sudoImpersonatedUser, bool useShellCommands)
{
_logger.MethodEntry(LogLevel.Debug);

Server = server;
SudoImpersonatedUser = sudoImpersonatedUser;
FileTransferProtocol = fileTransferProtocol;
IsStoreServerLinux = isStoreServerLinux;
UseShellCommands = useShellCommands;
UserId = serverLogin;
Password = serverPassword;

Expand Down Expand Up @@ -80,7 +82,8 @@ internal SSHHandler(string server, string serverLogin, string serverPassword, bo
sshClient.Connect();

//method call below necessary to check edge condition where password for user id has expired. SCP (and possibly SFTP) download hangs in that scenario
CheckConnection();
if (useShellCommands)
CheckConnection();
}
catch (Exception ex)
{
Expand Down Expand Up @@ -368,13 +371,20 @@ public override void CreateEmptyStoreFile(string path, string linuxFilePermissio
if (IsStoreServerLinux)
{
string pathOnly = string.Empty;
SplitStorePathFile(path, out pathOnly, out _);
string fileName = string.Empty;
SplitStorePathFile(path, out pathOnly, out fileName);

linuxFilePermissions = string.IsNullOrEmpty(linuxFilePermissions) ? GetFolderPermissions(pathOnly) : linuxFilePermissions;
linuxFileOwner = string.IsNullOrEmpty(linuxFileOwner) ? GetFolderOwner(pathOnly) : linuxFileOwner;
if (UseShellCommands)
{
linuxFilePermissions = string.IsNullOrEmpty(linuxFilePermissions) ? GetFolderPermissions(pathOnly) : linuxFilePermissions;
linuxFileOwner = string.IsNullOrEmpty(linuxFileOwner) ? GetFolderOwner(pathOnly) : linuxFileOwner;

AreLinuxPermissionsValid(linuxFilePermissions);

AreLinuxPermissionsValid(linuxFilePermissions);
RunCommand($"install -m {linuxFilePermissions} -o {linuxFileOwner} {linuxFileGroup} /dev/null {path}", null, ApplicationSettings.UseSudo, null);
RunCommand($"install -m {linuxFilePermissions} -o {linuxFileOwner} {linuxFileGroup} /dev/null {path}", null, ApplicationSettings.UseSudo, null);
}
else
UploadCertificateFile(pathOnly, fileName, Array.Empty<byte>());
}
else
RunCommand($@"Out-File -FilePath ""{path}""", null, false, null);
Expand All @@ -386,28 +396,38 @@ public override bool DoesFileExist(string path)
{
_logger.MethodEntry(LogLevel.Debug);
_logger.LogDebug($"DoesFileExist: {path}");

string rtn = RunCommand($"ls {path} >> /dev/null 2>&1 && echo True || echo False", null, ApplicationSettings.UseSudo, null);
return Convert.ToBoolean(rtn);

//using (SftpClient client = new SftpClient(Connection))
//{
// try
// {
// client.Connect();
// string existsPath = FormatFTPPath(path, !IsStoreServerLinux);
// bool exists = client.Exists(existsPath);
// _logger.LogDebug(existsPath);

// _logger.MethodExit(LogLevel.Debug);

// return exists;
// }
// finally
// {
// client.Disconnect();
// }
//}

bool exists = false;

if (UseShellCommands)
{
exists = Convert.ToBoolean(RunCommand($"ls {path} >> /dev/null 2>&1 && echo True || echo False", null, ApplicationSettings.UseSudo, null));
}
else
{
using (SftpClient client = new SftpClient(Connection))
{
try
{
client.Connect();
string existsPath = FormatFTPPath(path, !IsStoreServerLinux);
exists = client.Exists(existsPath);
_logger.LogDebug(existsPath);
}
catch (Exception ex)
{
_logger.LogError(RemoteFileException.FlattenExceptionMessages(ex, $"Error checking existence of file {path} using SFTP"));
throw;
}
finally
{
_logger.MethodExit(LogLevel.Debug);
client.Disconnect();
}
}
}

return exists;
}

public override void RemoveCertificateFile(string path, string fileName)
Expand Down
Loading
Loading