Skip to content

Instantly share code, notes, and snippets.

@DamianSuess
Last active May 20, 2024 16:05
Show Gist options
  • Save DamianSuess/53436b050759759be044b450094d749f to your computer and use it in GitHub Desktop.
Save DamianSuess/53436b050759759be044b450094d749f to your computer and use it in GitHub Desktop.
C# Linux BASH

About C# BASH Sample

The example code here references Docker and IoT Edge as an example. The user/pass is Base64 encoded as part of the Bash class in this example.

By no means should you keep the admin user/pass hard-coded in the production class, this is an example piece.

What's The Difference?

The Bash class has both RunAs(..) and Command(..), and each allows you to run using sudo. The difference is, the RunAs(...) method does not require your app to be started with elevated privledges. Where as, Command(..) requires you to start the app with elevated privledges in order to execute commands using sudo.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
using System.Threading.Tasks;
namespace SuessLabs.Tools;
public class SampleUsage
{
/// <summary>Sample command to execute with elevated user access.</summary>
public const string DockerStopContainerCommand = "docker container stop MyContainer";
/// <summary>Execute using SUDO. Stop a Docker container.</summary>
/// <returns>True on success.</returns>
public static async Task<bool> DockerStopContainerAsync()
{
var result = true;
try
{
var process = Bash.RunAs(DockerStopContainerCommand.Split(' '));
await process?.WaitForExitAsync();
}
catch
{
result = false;
}
return result;
}
/// <summary>Execute basic BASH command as the current user</summary>
public static string ReadLogFromConsole(string containerName)
{
// Sample command: string.format("ifconfig {0} {1} netmask {2} up", networkType, ipAddress, subnet);
var log = Bash.Command($"iotedge logs {containerName}");
return log ?? string.Empty;
}
/// <summary>Execute basic BASH command as the current user</summary>
public static string ReadLogFromDockerFilestring containerName()
{
var log = Bash.Command("docker inspect --format=\"{{.LogPath}}\" " + containerName, true);
return log ?? string.Empty;
}
}
public class Bash
{
/// <summary>Sample default admin password to use (BASE 64 encoded).</summary>
private const string DefaultPass = "TXlQYXNzdzByZCE";
/// <summary>Sample default admin user name to use (BASE 64 encoded).</summary>
private const string DefaultUser = "TXlVc2VyTmFtZQ";
/// <summary>Get Effective User Id (Linux only).</summary>
/// <returns>Returns 0 when running as root.</returns>
/// <remarks>Reference, <![CDATA[Mono.Unix.Native.Syscall.geteuid() == 0;]]>, <![CDATA[Mono.Unix.UserEnvironment.EffectiveUserId]]>.</remarks>
[DllImport("libc", SetLastError = true, EntryPoint = "geteuid")]
public static extern uint GetEuid();
/// <summary>Gets a value indicating whether the application was launched with admin privileges or not.</summary>
public static bool IsAdministrator =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
new WindowsPrincipal(WindowsIdentity.GetCurrent())
.IsInRole(WindowsBuiltInRole.Administrator) :
GetEuid() == 0;
/// <summary>
/// Execute BASH command. For SUDO commands, the app must be started with elevated privileges.
/// </summary>
/// <param name="command">Command to execute.</param>
/// <param name="isSudo">Execute with SUDO.</param>
/// <returns>Return results.</returns>
public static string? Command(string command, bool isSudo = false)
{
var output = string.Empty;
var escapedArgs = command.Replace("\"", "\\\"");
if (isSudo)
escapedArgs = $"sudo {escapedArgs}";
var bashCommand = $@"-c ""{escapedArgs}""";
try
{
var psi = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = bashCommand,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
if (isSudo)
{
psi.UserName = TextDecode(DefaultUser);
psi.PasswordInClearText = TextDecode(DefaultPass);
}
using var process = Process.Start(psi);
output = process?.StandardOutput.ReadToEnd();
process?.WaitForExit();
}
catch (Exception ex)
{
var title = "Error Loading Logs";
var message = $"An error occurred pulling logs.\nCommand: {bashCommand}\n\n{ex}";
Console.WriteLine($"{title} - {message}");
Debug.WriteLine($"{title} - {message}");
}
return output;
}
/// <summary>Execute command as specified user, or leave blank for default account.</summary>
/// <param name="command">Command to execute.</param>
/// <param name="user">User name.</param>
/// <param name="password">Password.</param>
/// <returns>Process.</returns>
/// <remarks>
/// Note, the user must be apart of the `docker` group.
/// <c><![CDATA[sudo addgroup --system docker; sudo adduser $USER docker]]></c>.
/// </remarks>
public static Process RunAs(IEnumerable<string> command, string user = "", string password = "")
{
if (string.IsNullOrEmpty(user) && string.IsNullOrEmpty(password))
{
user = TextDecode(DefaultUser);
password = TextDecode(DefaultPass);
}
var sudoArgs = new List<string>()
{
"-k", // don't use cached credentials
"-S", // read password from stdin
"-u", user, // user
"-p", string.Empty, // empty password prompt
"--", // command separator
};
sudoArgs.AddRange(command);
var psi = new ProcessStartInfo()
{
FileName = "sudo",
RedirectStandardInput = true, // for writing the password
};
sudoArgs.ForEach(arg => psi.ArgumentList.Add(arg));
Process process = Process.Start(psi)!;
process.StandardInput.WriteLine(password);
return process;
}
private static SecureString ConvertToSecureString(string password)
{
if (password == null)
throw new ArgumentNullException("Password");
unsafe
{
fixed (char* passwordChars = password)
{
var securePassword = new SecureString(passwordChars, password.Length);
securePassword.MakeReadOnly();
return securePassword;
}
}
}
private static string TextDecode(string base64EncodedData)
{
var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
private static string TextEncode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment