I've been trying very hard to automatically ssh into a Linux server. What's crazy, is that I can create a .bat script, that will do it, but I have to be there physically, to type in the password.
I've tried automating this using System.Diagnostics.Process object in c# to no end. There is no way, I've found, to make this object allow you to see the password prompt. It out puts every line up until that point, and then doesn't output any more lines.
cmd.exe, does really well at allowing you to jump from process to process in a script; It consolidates everything into one screen, and just prompts you for things such as passwords, and then you type them in.
Is there any simple way, in C#, to make cmd.exe think you are a human being typing in it, so I can simulated this programmatically? Otherwise, System.Diagnostics.Process, doesn't seem to offer a way to interacted with a process that outputs a password prompt. You never get to see the prompt that clearly happens when you run the same thing in cmd.exe
cmd.exe is not the SSH client; it's just a shell for one. Explore the capabilities of the actual client. In PuTTY (very popular!) there's an autologon feature, if you have an SSH key.
You could use
Windows.Forms.SendKeys()
to send password as keystrokes
Reference Material
Without knowing which commandline tool you are using to ssh to the server, isn't there a commandline parameter to provide a password.
C:\> ssh user#ip -p password
Or have you tried input redirection:
ssh user#ip <
Yourpassword
EOF
In looking into your earlier question, what is the trouble you are having in using the programmer interface?
Are you sure you need a full fledged ssh client, or do you just want to execute a command remotely. If it is the latter you might want to check out plink.
plink.exe -ssh user#host -2 -i user.ppk -m command_line.txt
How to create the ppk file: http://theillustratednetwork.mvps.org/Ssh/Private-publicKey.html
Related
I created a program using Renci SSH.NET library. Its sending all the commands and reading the result normally. However, when I send the command below:
client.RunCommand("cli");
The program hangs on this line indefinitely.
Any explanation of what is happening?
The cli is a command is used on Juniper switches/routers.
AFAIK, cli is a kind of a shell/interactive program. So I assume you have tried to do something like:
client.RunCommand("cli");
client.RunCommand("some cli subcommand");
That's wrong. cli will keep waiting for subcommands and never exit, until you explicitly close it with a respective command (like exit). And after it exits, the server will try to execute the cli subcommand as a separate top-level command, failing too.
You have to feed the "cli subcommand" to the input of the cli command. But SSH.NET unfortunately does not support providing an input with the SshClient.RunCommand/SshClient.CreateCommand interface. See Allow writing to SshCommand.
There are two solutions:
Use the appropriate syntax of the server's shell to generate the input on the server, like:
client.RunCommand("echo \"cli subcommand\" | cli");
Or use a shell session (what is otherwise a not recommended approach for automating a command execution).
Use SshClient.CreateShellStream or SshClient.CreateShell and send the commands to its input:
"cli\n" + "cli subcommand\n"
For a sample code see Providing subcommands to a command (sudo/su) executed with SSH.NET SshClient.CreateShellStream or C# send Ctrl+Y over SSH.NET.
I'm using the following code to export a copy of MySql database on a remove server using SSH.NET:
using (SshClient client = new SshClient(sshConnectionInfo))
{
client.Connect();
//strCmd is:
// mysqldump -h "<server>.dreamhosters.com" -u "<dbuser>" -p"<actual_password>" "<dbid>" > "/home/<user_name>/<temp_file_name>.sql"
//
// with <...> parts are obviously filled in with correct credentials
SshCommand resCmd = client.RunCommand(strCmd);
//Check result
string strExpDesc = resCmd.Error;
if (string.IsNullOrWhiteSpace(strExpDesc))
{
Console.WriteLine("Exported OK");
}
else
{
Console.WriteLine("Error: " + strExpDesc);
}
}
This worked really well until this month when the shared hosting company (that my database is hosted with) had upgraded their version of Ubuntu server, so the mysqldump command above started returning the following warning:
[Warning] Using a password on the command line interface can be
insecure.
Which my script interprets as an error and fails.
I contacted the hosting company, but their tech support was less than useful. They told me to type in the password instead of specifying it in the command line. Thus my questions:
How to use SSH.NET to interact with the remote server via SSH and send it the password after its prompt?
Otherwise, can I mute that warning from my script w/o having access to the server configuration?
The normal approach to suppressing that warning from MySQL is to put the username and password in an options file, and include the options file in in place of the -u and -p , using the --defaults-extra-file option, e.g.
mysqldump --defaults-extra-file=/path/config.cnf -h "myhost" "<dbid>"
(NOTE: if it's provided, the --defaults-extra-file option must be first option.)
The file would have the user and password options under the [mysqldump] section, something like:
[mysqldump]
user=jonsnow
password=kn0wsn0thing
The credentials could be provided in the [client] section of the optionas file. Since mysqldump is a "standard client" it reads the [client] section.
[client]
user=alannister
password=alwayspayshisdebts
The options file should be properly protected, since it contains credentials. Read permission should be restricted, only the unix user that is connecting via ssh.
chown sshuser /path/config.cnf
chmod 600 /path/config.cnf
(where sshuser is the username used in the ssh connection)
I think there may also be some specific paths and filenames that are automatically included as options files, if they are found, when mysqldump starts. Similar to how MySQL server finds the my.cnf file when it starts. It might be possible to leverage that, in place of explicit --defaults-extra-file option.
MySQL Reference Manual explains usage of options files here:
https://dev.mysql.com/doc/refman/8.0/en/option-files.html
You can write the credentials to input of the remote mysqldump process. But SSH.NET unfortunately does not support providing an input with the CreateCommand interface.
You have to open a shell session (what is otherwise a not recommended approach for automating a command execution).
Use SshClient.CreateShellStream or SshClient.CreateShell and send the mysqldump command and credentials to its input:
"mysqldump ...\nusername\npassword"
For a sample code see C# send Ctrl+Y over SSH.NET.
See also Providing input/subcommands to a command (cli) executed with SSH.NET SshClient.RunCommand.
I have a web service that uses SSH.NET to call a shell script on a Unix box.
If I run the script normally, it works fine, does its work correctly on the Informix DB.
Just some background:
I call a script that executes a .4gl (cant show this as its business knowledge).
The g4l is giving the following error back in a log, when I execute it with SSH.NET:
fglgo: error while loading shared libraries: libiffgisql.so: cannot open shared object file: No such file or directory
file_load ended: 2017-09-21 15:37:01
C# code to execute SSH.NET script
sshclients = new SshClient(p, 22, username, password);
sshclients.Connect();
sshclients.KeepAliveInterval = new TimeSpan(0, 0, 1);
sshclients.RunCommand("sh " + Script_dir);
I added the KeepAliveInterval, to see, if it helps.
My question is the error I am getting from Unix/4gl.
Why is this happening and who can I get the script to execute correctly?
The SshClient.RunCommand uses SSH "exec" channel internally. It, by default, (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the shared object cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your g4l command. Check Session > Close window on exit > Never and open the session. You should get the same "No such file or directory" error.
Ways to fix this, in preference order:
Fix the scripts not to rely on a specific environment.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
sshclients.RunCommand("PATH=\"$PATH;/path/to/g4l\" && sh ...");
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel.
Though SSH.NET does not support this. You would have to modify its code issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).
You can also try to use "shell" channel using .CreateShell method. For it, SSH.NET does support pseudo terminal allocation.
Though, using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Renci SSH.NET - no result string returned for opmnctl
Certain Unix commands fail with "... not found", when executed through Java using JSch
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Have seen similar questions asked by Informix-4gl developers as they transition to FourJs Genero and using its Web Services functionality. The question I'll put to them is "who owns the fglgo/fglrun process that the Genero Application Server has launched, where is it running from, and what is its environment". If needed, I'll illustrate with a simple program that does something like ...
MAIN
RUN "env > /tmp/myname.txt"
RUN "who >> /tmp/myname.txt"
RUN "pwd >> /tmp/myname.txt"
END MAIN
... and say compare with when program is running from command line. It is normally a case like in the earlier answer of configuring so that the environment is set correctly before the 4gl program is executed.
I am using a .NET library Renci.SshNet to connect to remote Solaris machine (its a VM on ESXi). It connects fine.
I use the following method to execute the commands and get the Standard Output. This works fine on any Linux machine and almost all command on Solaris (Except few - which is where the issue is)
outstring = sshClient.RunCommand(command).Execute();
For example when command = "cat /etc/release | grep Solaris" -it works fine.
However, when command = "smbios -t SMB_TYPE_SYSTEM" - it doesn't return anything. I try redirecting it to a file. The file gets created - but doesn't have anything on it.
I connect to the system using PuTTY and run the command - it runs perfectly and gives the desired output.
I am perplexed by this behavior. I am using a username with root privilloginto logon. So privileges are ruled out (anyway the same user gets the output in PuTTY).
I am wondering if there is any setting or restriction on Solaris (I am running ver 11.3) which does not allow the smbios command to run like this over a remote connection? Or it is something else? Any guidance will be extremely helpful. If any further info is required, please let me know.
Well, it turns out that it was to do with Path settings. When you login through Putty the $PATH as defined gets set. Hence smbios runs from Putty.
But in a SSH session the $PATH environment variable does not get applied. So its not able to find smbios to run. If you give the full path of smbios - like /usr/sbin/smbios it executes fine over Renci.SshNet.
I'm not sure if this question is more appropriate for Stackoverflow or SuperUser or what StackExchange site...
Basically I'm launching a third-party app from C# with Process.Start with several command line parameters. One of those command line parameters is a password.
I think I'm doing a really good job of securing that password everywhere in my app, except if you open the Processes tab in Task Manager, you can add the "Command Line" column and see all of those command line parameters.
Can anyone think of a way to launch a process that somehow has the command line params hidden? Is this possible at all?
Thank you!
EDIT:
This is a Windows Service wrapper for plink.exe (SSH/Putty stuff). It will prompt for a password if I don't specify the password in the command line, but I get this weird warning:
Plink.exe - 3/30/2013 2:40:47 PM - Attempting keyboard-interactive authentication
Plink.exe - 3/30/2013 2:40:47 PM - Server refused keyboard-interactive authentication
Plink.exe - 3/30/2013 2:40:49 PM - user#hostname.com's password:
I have specified to redirect the standard input, but perhaps I will continue looking in to that and see if I can work-around it. Also, as David Heffernan recommended, I'm going to look further into Pageant. Thank you everyone - I will post an update once I figure out a better solution!
There's no way to pass a command line argument to a process, so that the process can see it, but everything else in the system cannot.
This is an obvious flaw and when programs allow passwords to be passed as arguments, it's usually done for convenience for the user that is not concerned about eavesdroppers. Well designed programs will usually provide, in addition, other secure means of authentication.
If you set an ACL for the new process, it should restrict who can read the command line information. An empty ACL, granting no permissions, might block access to administrators using Task Manager, though my first guess is that it will not. (Note that an empty security descriptor is not the same thing as an empty ACL. One implicitly grants permission to everyone, the other implicitly denies it.)
Of course, an administrator could always replace plink.exe with something that stores the password somewhere. So I'm not sure that worrying about what the administrator can see with Task Manager makes sense!