public async Task <bool> ConfigureMachine(Machine machine)//TODO: Unifinished due to unknown elements related to lack of access to images on open nebula { //Construct configuration string List <MachineConfigurationUser> configurationUsers = new(); var assignments = await GetContext().MachineAssignments.Where(ass => ass.MachineID == machine.MachineID).ToListAsync(); foreach (var assignment in assignments)//TODO: Expecting issues with resolution of virtual properties on the items from the database { if (assignment.GroupID == null) { if (await GetContext().Users.FindAsync(assignment.UserUsername) == null) { GetContext().Users.Add(await UserFactory.Create(assignment.UserUsername)); } MachineConfigurationUser machineConfigurationUser = new(); machineConfigurationUser.Groups = machine.LinuxGroups; machineConfigurationUser.Username = assignment.UserUsername; machineConfigurationUser.UserPassword = assignment.OneTimePassword; User user = (await GetContext().Users.FindAsync(assignment.UserUsername)); machineConfigurationUser.UserPublicKey = user.UserPublicKey; configurationUsers.Add(machineConfigurationUser); } else { foreach (var groupAssignment in GetContext().GroupAssignments.Where(ga => ga.GroupID == assignment.GroupID)) { MachineConfigurationUser machineConfigurationUser = new(); machineConfigurationUser.Groups = machine.LinuxGroups; machineConfigurationUser.Username = groupAssignment.User.Username; machineConfigurationUser.UserPassword = assignment.OneTimePassword; machineConfigurationUser.UserPublicKey = groupAssignment.User.UserPublicKey; configurationUsers.Add(machineConfigurationUser); } } } var connectionInfo = new ConnectionInfo(machine.MachineStatus.MachineIp, _defaultUsername, new PrivateKeyAuthenticationMethod("admin", new PrivateKeyFile($"{SERVER_SCALABLE_TEACHING_PATH}/.ssh/id_rsa"))); Console.WriteLine($"Connection info, ip: {machine.MachineStatus.MachineIp}, Default username: {_defaultUsername}, "); using (var client = new SshClient(connectionInfo)) { MemoryStream stdin = new(); MemoryStream stdout = new(); MemoryStream extout = new(); client.Connect(); //Add groups foreach (var group in machine.LinuxGroups) { var command = client.CreateCommand($"sudo groupadd {group}"); var result = command.BeginExecute(); await Task.Run(() => result.AsyncWaitHandle.WaitOne()); } //Add users foreach (MachineConfigurationUser user in configurationUsers) { var p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = "openssl"; p.StartInfo.Arguments = $"passwd -6 -salt {RandomString(10)} {user.UserPassword}"; p.Start(); await p.WaitForExitAsync(); string output = (await p.StandardOutput.ReadToEndAsync()).Trim(); Console.WriteLine($"Add user: {user.Username} hashed pw: {output}"); //Create User var useraddCommand = await PerformSSHCommand(client, $"sudo useradd -s \"/usr/bin/bash\" -mp {output} {user.Username.ToLower()}"); Console.WriteLine($"MachineConfigurator: useraddCommand text: {useraddCommand.CommandText} Result: {useraddCommand.Result}"); //Add user to linux groups foreach (var group in user.Groups) { var usermodCommand = await PerformSSHCommand(client, $"sudo usermod -aG {group} {user.Username.ToLower()}"); Console.WriteLine($"MachineConfigurator: usermodCommand text: {usermodCommand.CommandText} .Result {usermodCommand.Result}"); } //Prep authorized_keys var AuthorizedKeysCommand = await PerformSSHCommand(client, $"sudo mkdir -p /home/{user.Username.ToLower()}/.ssh &&" + $" sudo touch /home/{user.Username.ToLower()}/.ssh/authorized_keys &&" + $" sudo chown -R {user.Username.ToLower()}:{user.Username.ToLower()} /home/{user.Username.ToLower()}/.ssh &&" + $" sudo chmod -R 755 /home/{user.Username.ToLower()}/.ssh"); Console.WriteLine($"MachineConfigurator: AuthorizedKeysCommand text: {AuthorizedKeysCommand.CommandText} .Result {AuthorizedKeysCommand.Result}"); //Add key var addKeyCommand = client.CreateCommand($"sudo sh -c 'echo \"{user.UserPublicKey}\" >> /home/{user.Username.ToLower()}/.ssh/authorized_keys'"); var addKeyResult = addKeyCommand.BeginExecute(); await Task.Run(() => addKeyResult.AsyncWaitHandle.WaitOne()); Console.WriteLine($"MachineConfigurator: addKeyCommand text {addKeyCommand.CommandText} .Result {addKeyCommand.Result}"); //Remove password requirement from sudo var nopasswdCommand = await PerformSSHCommand(client, $"sudo sh -c 'echo \"{user.Username.ToLower()} ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/{user.Username.ToLower()}'"); Console.WriteLine($"MachineConfigurator: nopasswdCommand text {nopasswdCommand.CommandText} .Result {nopasswdCommand.Result}"); } //Update var aptUpdateUpgradeCommand = await PerformSSHCommand(client, $"sudo apt-get update"); Console.WriteLine($"MachineConfigurator: aptUpdateUpgradeCommand.Result {aptUpdateUpgradeCommand.Result}"); //Prep for ppa var ppaPrepCommand = await PerformSSHCommand(client, $"sudo apt-get install -y software-properties-common"); Console.WriteLine($"MachineConfigurator: ppaPrepCommand.Result {ppaPrepCommand.Result}"); //PPA foreach (var ppa in machine.Ppa) { var ppaAddCommand = await PerformSSHCommand(client, $"sudo add-apt-repository -y {ppa}"); Console.WriteLine($"MachineConfigurator: ppaAddCommand.Result {ppaAddCommand.Result}"); } //Post ppa update and upgrade var postPpaUpdateCommand = await PerformSSHCommand(client, $"sudo apt-get update && sudo apt-get upgrade -y"); Console.WriteLine($"MachineConfigurator: postPpaUpdateCommand.Result {postPpaUpdateCommand.Result}"); //Install apts foreach (var apt in machine.Apt) { var aptInstallCommand = await PerformSSHCommand(client, $"sudo apt-get install -y {apt}"); Console.WriteLine($"MachineConfigurator: aptInstallCommand.Result {aptInstallCommand.Result}"); } //Apt cleanup SshCommand aptCleanupCommand = await PerformSSHCommand(client, $"sudo apt-get autoremove -y"); Console.WriteLine($"MachineConfigurator: aptCleanupCommand.Result {aptCleanupCommand.Result}"); client.Disconnect(); } return(true); //TODO: Implement error handeling for configuration }
public async Task <bool> ConfigureMachineWithFile(Machine machine)//TODO: Unifinished due to unknown elements related to lack of access to images on open nebula { //Construct configuration string List <MachineConfigurationUser> configurationUsers = new(); List <MachineAssignment> assignments = await GetContext().MachineAssignments.Where(assignment => assignment.MachineID == machine.MachineID).ToListAsync(); Console.WriteLine($"Machine assignment count: {assignments.Count}"); foreach (var assignment in assignments)//TODO: Expecting issues with resolution of virtual properties on the items from the database { Console.WriteLine($"Calculating machine assignments"); if (assignment.GroupID == null) { var context = GetContext(); if (await context.Users.FindAsync(assignment.UserUsername) == null) { context.Users.Add(await UserFactory.Create(assignment.UserUsername)); } MachineConfigurationUser machineConfigurationUser = new(); machineConfigurationUser.Groups = machine.LinuxGroups; machineConfigurationUser.Username = assignment.UserUsername; machineConfigurationUser.UserPassword = assignment.OneTimePassword; User user = (await context.Users.FindAsync(assignment.UserUsername)); machineConfigurationUser.UserPublicKey = user.UserPublicKey; configurationUsers.Add(machineConfigurationUser); } else { Console.WriteLine($"Calculating for group based assignments"); foreach (var groupAssignment in await GetContext().GroupAssignments.Where(ga => ga.GroupID == assignment.GroupID).ToListAsync()) { MachineConfigurationUser machineConfigurationUser = new(); machineConfigurationUser.Groups = machine.LinuxGroups; machineConfigurationUser.Username = groupAssignment.User.Username; machineConfigurationUser.UserPassword = assignment.OneTimePassword; machineConfigurationUser.UserPublicKey = groupAssignment.User.UserPublicKey; configurationUsers.Add(machineConfigurationUser); } } } //var connectionInfo = new ConnectionInfo(machine.MachineStatus.MachineIp, _defaultUsername, new PrivateKeyAuthenticationMethod("admin", new PrivateKeyFile($"{SERVER_SCALABLE_TEACHING_PATH}/.ssh/id_rsa"))); //Console.WriteLine("Connection info, ip: {machine.MachineStatus.MachineIp}, Default username: {_defaultUsername}, "); var builder = new StringBuilder(); //Update Admin password to prevent lockout by ssh key mishap builder.AppendLine($"echo \"admin:{Environment.GetEnvironmentVariable("ADMIN_PASSWD")}\" | chpasswd"); //Add groups foreach (var group in machine.LinuxGroups) { builder.AppendLine($"sudo groupadd {group}"); } Console.WriteLine("Configuration users count {}"); //Add users foreach (var user in configurationUsers) { Console.WriteLine($"Configuring user {user.Username}"); var p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = "openssl"; p.StartInfo.Arguments = $"passwd -6 -salt {RandomString(10)} {user.UserPassword}"; p.Start(); await p.WaitForExitAsync(); //TODO: Its clipping the data when reading single line var output = (await p.StandardOutput.ReadToEndAsync()).TrimEnd('\n', '\r', ' '); //Trim any trailing non hash chars //Create User builder.AppendLine($"useradd -s \"/usr/bin/bash\" -mp {output} {user.Username.ToLower()}"); //Add user to linux groups foreach (var group in user.Groups) { builder.AppendLine($"usermod -aG {group} {user.Username.ToLower()}"); } //Prep authorized_keys builder.AppendLine($"mkdir -p /home/{user.Username.ToLower()}/.ssh &&" + $" touch /home/{user.Username.ToLower()}/.ssh/authorized_keys &&" + $" chown -R {user.Username.ToLower()}:{user.Username.ToLower()} /home/{user.Username.ToLower()}/.ssh &&" + $" chmod -R 755 /home/{user.Username.ToLower()}/.ssh"); //Add key builder.AppendLine($"sudo sh -c 'echo \"{user.UserPublicKey}\" >> /home/{user.Username.ToLower()}/.ssh/authorized_keys'"); //Remove password requirement from sudo builder.AppendLine($"sudo sh -c 'echo \"{user.Username.ToLower()} ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/{user.Username.ToLower()}'"); } //Update builder.AppendLine($"sudo apt-get update"); //Prep for ppa builder.AppendLine($"sudo apt-get install -y software-properties-common"); //PPA foreach (var ppa in machine.Ppa) { builder.AppendLine($"sudo add-apt-repository -y {ppa}"); } //Post PPA update builder.AppendLine($"sudo apt-get update"); //Install apts foreach (var apt in machine.Apt) { builder.AppendLine($"sudo apt-get install -y {apt}"); } //Post ppa update and upgrade builder.AppendLine($"sudo apt-get update && sudo apt-get upgrade -y"); //Apt cleanup builder.AppendLine($"sudo apt-get autoremove -y"); await File.WriteAllBytesAsync($"{SERVER_SCALABLE_TEACHING_PATH}/configfile/{machine.HostName}.sh", Encoding.UTF8.GetBytes(builder.ToString())); var p_scp = new Process(); p_scp.StartInfo.UseShellExecute = false; p_scp.StartInfo.RedirectStandardOutput = true; p_scp.StartInfo.RedirectStandardError = true; p_scp.StartInfo.FileName = "scp"; p_scp.StartInfo.Arguments = $"-i {SERVER_SCALABLE_TEACHING_PATH}/.ssh/id_rsa -o StrictHostKeyChecking=no -B {SERVER_SCALABLE_TEACHING_PATH}/configfile/{machine.HostName}.sh admin@{machine.MachineStatus.MachineIp}:/home/admin/configfile.sh"; p_scp.Start(); await p_scp.WaitForExitAsync(); Console.WriteLine($"Did scp into {machine.HostName} {machine.MachineStatus.MachineIp}, status: Exit code: {p_scp.ExitCode}\nout: {p_scp.StandardOutput.ReadToEnd()} \n err{p_scp.StandardError.ReadToEnd()}"); //Using task as a hack - this is an odd boy that sometimes does not return //TODO: Better solution Task.Run(async() => { Console.WriteLine($"Starting ssh: {machine.HostName}, {machine.MachineStatus.MachineIp}"); var p_ssh = new Process(); p_ssh.StartInfo.UseShellExecute = false; p_ssh.StartInfo.RedirectStandardOutput = true; p_ssh.StartInfo.RedirectStandardError = true; p_ssh.StartInfo.FileName = "ssh"; p_ssh.StartInfo.Arguments = $"-o StrictHostKeyChecking=no -i {SERVER_SCALABLE_TEACHING_PATH}/.ssh/id_rsa admin@{machine.MachineStatus.MachineIp} \"sudo chmod 777 /home/admin/configfile.sh && sudo sh -c '/home/admin/configfile.sh'; sudo rm /home/admin/configfile.sh; touch /home/admin/ranConfig; exit\""; p_ssh.Start(); await p_ssh.WaitForExitAsync(); Console.WriteLine($"Finished ssh: {machine.HostName}, {machine.MachineStatus.MachineIp}"); }); return(true); //TODO: Implement error handeling for configuration }