244 lines
9.9 KiB
C#
244 lines
9.9 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace SshStatusUpdater
|
|
{
|
|
internal class Program
|
|
{
|
|
// ================= НАСТРОЙКИ ПОДКЛЮЧЕНИЯ =================
|
|
private const string RemoteUser = "depfile"; // Логин пользователя на сервере
|
|
private const string RemoteHost = "10.228.48.110"; // IP-адрес или DNS-имя сервера
|
|
private const int RemotePort = 22; // Порт SSH-сервера
|
|
|
|
// Путь к приватному ключу по умолчанию (если не задан аргументом)
|
|
private const string DefaultKeyPath = @"D:\sys\C-Sharp\deployfile\key\test_key.key";
|
|
// ========================================================
|
|
|
|
static int Main(string[] args)
|
|
{
|
|
// 1. Определение пути к ключу
|
|
string rawKeyPath = (args.Length > 0 && !string.IsNullOrWhiteSpace(args[0]))
|
|
? args[0].Trim('"')
|
|
: DefaultKeyPath;
|
|
|
|
string keyPath = Environment.ExpandEnvironmentVariables(rawKeyPath);
|
|
|
|
if (!File.Exists(keyPath))
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine($"Ошибка: Файл ключа не найден по указанному пути: {keyPath}");
|
|
Console.ResetColor();
|
|
return 2;
|
|
}
|
|
|
|
Console.WriteLine($"Используется ключ: {keyPath}");
|
|
|
|
// 2. Проверка доступности OpenSSH клиента (с возможностью автоустановки)
|
|
string sshExecutable = EnsureOpenSshAvailable();
|
|
if (string.IsNullOrEmpty(sshExecutable))
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine("\n[ОШИБКА] Клиент OpenSSH не найден в системе и не удалось выполнить его установку.");
|
|
Console.WriteLine("Для работы приложения необходимо установить компонент OpenSSH вручную.");
|
|
Console.WriteLine("Откройте PowerShell от имени Администратора и выполните:");
|
|
Console.WriteLine(" Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0");
|
|
Console.ResetColor();
|
|
return 4;
|
|
}
|
|
|
|
// 3. Формирование полезной нагрузки и удаленной команды
|
|
string randomContent = GenerateRandomPayload(linesCount: 5, charsPerLine: 32);
|
|
|
|
// Относительные пути от домашнего каталога /var/lib/depfile
|
|
string remoteCommand = "mkdir -p status && cat > status/change.me && (cd status && sha256sum change.me > sha256.hash)";
|
|
|
|
string sshArgs = $"-i \"{keyPath}\" -p {RemotePort} -o BatchMode=yes -o StrictHostKeyChecking=accept-new {RemoteUser}@{RemoteHost} \"{remoteCommand}\"";
|
|
|
|
ProcessStartInfo psi = new ProcessStartInfo
|
|
{
|
|
FileName = sshExecutable,
|
|
Arguments = sshArgs,
|
|
UseShellExecute = false,
|
|
RedirectStandardInput = true,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
Console.WriteLine($"Подключение к {RemoteUser}@{RemoteHost}:{RemotePort}...");
|
|
|
|
try
|
|
{
|
|
using (Process proc = new Process { StartInfo = psi })
|
|
{
|
|
proc.Start();
|
|
|
|
// Передаем сгенерированный текст в STDIN удаленного процесса ssh
|
|
using (StreamWriter sw = new StreamWriter(proc.StandardInput.BaseStream, new UTF8Encoding(false)))
|
|
{
|
|
sw.Write(randomContent);
|
|
}
|
|
|
|
string stdOut = proc.StandardOutput.ReadToEnd();
|
|
string stdErr = proc.StandardError.ReadToEnd();
|
|
|
|
proc.WaitForExit();
|
|
|
|
if (proc.ExitCode == 0)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Green;
|
|
Console.WriteLine("Файл status/change.me и хеш status/sha256.hash успешно созданы на сервере.");
|
|
Console.ResetColor();
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine($"Ошибка выполнения SSH (Код: {proc.ExitCode}):");
|
|
if (!string.IsNullOrWhiteSpace(stdErr))
|
|
Console.WriteLine(stdErr.Trim());
|
|
if (!string.IsNullOrWhiteSpace(stdOut))
|
|
Console.WriteLine(stdOut.Trim());
|
|
Console.ResetColor();
|
|
return proc.ExitCode;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Console.WriteLine($"Критическая ошибка выполнения SSH: {ex.Message}");
|
|
Console.ResetColor();
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
private static string EnsureOpenSshAvailable()
|
|
{
|
|
string path = FindSshPath();
|
|
if (!string.IsNullOrEmpty(path))
|
|
return path;
|
|
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
Console.WriteLine("Клиент OpenSSH не обнаружен. Попытка автоматической установки компонента Windows...");
|
|
Console.ResetColor();
|
|
|
|
bool installed = TryInstallOpenSsh();
|
|
if (installed)
|
|
{
|
|
path = FindSshPath();
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Green;
|
|
Console.WriteLine("Клиент OpenSSH успешно установлен.");
|
|
Console.ResetColor();
|
|
return path;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string FindSshPath()
|
|
{
|
|
string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
|
|
|
|
string[] candidates = new[]
|
|
{
|
|
Path.Combine(windir, "Sysnative", "OpenSSH", "ssh.exe"),
|
|
Path.Combine(windir, "System32", "OpenSSH", "ssh.exe"),
|
|
@"C:\Windows\System32\OpenSSH\ssh.exe"
|
|
};
|
|
|
|
foreach (var candidate in candidates)
|
|
{
|
|
if (File.Exists(candidate))
|
|
return candidate;
|
|
}
|
|
|
|
try
|
|
{
|
|
ProcessStartInfo testPsi = new ProcessStartInfo
|
|
{
|
|
FileName = "ssh",
|
|
Arguments = "-V",
|
|
UseShellExecute = false,
|
|
RedirectStandardError = true,
|
|
RedirectStandardOutput = true,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
using (var testProc = Process.Start(testPsi))
|
|
{
|
|
testProc?.WaitForExit(1000);
|
|
return "ssh";
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string TryInstallOpenSsh()
|
|
{
|
|
try
|
|
{
|
|
ProcessStartInfo installPsi = new ProcessStartInfo
|
|
{
|
|
FileName = "powershell.exe",
|
|
Arguments = "-NoProfile -ExecutionPolicy Bypass -Command \"Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0\"",
|
|
UseShellExecute = true,
|
|
Verb = "runas",
|
|
WindowStyle = ProcessWindowStyle.Normal
|
|
};
|
|
|
|
using (Process installProc = Process.Start(installPsi))
|
|
{
|
|
if (installProc == null)
|
|
return false;
|
|
|
|
installProc.WaitForExit();
|
|
return installProc.ExitCode == 0;
|
|
}
|
|
}
|
|
catch (Win32Exception)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
Console.WriteLine("Запрос прав Администратора был отклонён пользователем.");
|
|
Console.ResetColor();
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
Console.WriteLine($"Не удалось запустить автоматическую установку: {ex.Message}");
|
|
Console.ResetColor();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static string GenerateRandomPayload(int linesCount, int charsPerLine)
|
|
{
|
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
StringBuilder sb = new StringBuilder();
|
|
Random rng = new Random();
|
|
|
|
for (int i = 0; i < linesCount; i++)
|
|
{
|
|
char[] buffer = new char[charsPerLine];
|
|
for (int j = 0; j < charsPerLine; j++)
|
|
{
|
|
buffer[j] = chars[rng.Next(chars.Length)];
|
|
}
|
|
sb.AppendLine(new string(buffer));
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
} |