How to ask for a user input from a remote server with SSHKit? - input

I need to ask a user input from a ruby script on a remote server. I managed to perform it with bash with the following code
class ConfirmHandler
def on_data(command, stream_name, data, channel)
puts "data received: #{data}"
if data.to_s =~ /\?$/
prompt = Net::SSH::Prompt.default.start(type: 'confirm')
response = prompt.ask "Please enter your response (y/n)"
channel.send_data "#{response}\n"
end
end
end
require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL
on '<ssh-server-name>' do |host|
cmd = <<-CMD
echo 'Do something?';
read response;
echo response=$response
CMD
capture(cmd.squish , interaction_handler: ConfirmHandler.new)
end
When I run this script on my local machine I see
data received: Do something?
Please enter your response (y/n)
data received: response=y
I try to wrap the bash CMD code into a ruby script:
on '<ssh-server-name>' do |host|
cmd = <<-CMD
ruby -e "
puts 'Do something?';
require 'open3';
response = Open3.capture3('read response; echo $response');
puts 'response=' + response.to_s;
"
CMD
capture(cmd.squish , interaction_handler: ConfirmHandler.new)
end
and get the following result:
data received: Do something?
Please enter your response (y/n)
data received: response=["\n", "", #<Process::Status: pid 9081 exit 0>]
I was writing the code above looking at the Interactive commands section on the SSHKit Github home page
How can I capture the user response from a ruby script with SSKKit on the remote server?

I was able to capture the user response from a ruby script on a remote server with the following code:
# ask_response.rb
puts 'Do something?';
response = `read response; echo $response`;
puts 'response=' + response.to_s;
ask_response.rb is a ruby script which is located on a remote server. And locally I run:
on '<ssh-server-name>' do |host|
capture("ruby ask_response.rb" , interaction_handler: ConfirmHandler.new)
end

Related

ERROR: The remote server returned an error: (400) Bad Request

I am using aspx vb .net to connect with instagram api
I am using the following link as references: https://code.msdn.microsoft.com/Haroon-Said-e1d8d388
ERROR: The remote server returned an error: (400) Bad Request.
It is weird becuase i followed all steps and imported json as showed in above link. any idea? below is my code:
Dim json As String = ""
Try
Dim parameters As New NameValueCollection
parameters.Add("client_id", Client_ID)
parameters.Add("client_secret", ClientSecret)
parameters.Add("grant_type", "authorization_code")
parameters.Add("redirect_uri", Redirect_URI)
parameters.Add("code", Code)
Dim client As WebClient = New WebClient()
Try
'ERROR HERE
Dim result = client.UploadValues("https://api.instagram.com/oauth/access_token", "POST", parameters)
...
Catch ex As Exception
labelTest.Text += "---" & ex.Message
End Try
Thanks. yeah I been working on this for couple months now and trying to debug but I just have no idea whats going on. I mean I looked at insta api webbsite sill no luck. I tested my values also and they seem to be correct:
curl -F 'client_id=CLIENT_ID' \
-F 'client_secret=CLIENT_SECRET' \
-F 'grant_type=authorization_code' \
-F 'redirect_uri=AUTHORIZATION_REDIRECT_URI' \
-F 'code=CODE' \
https://api.instagram.com/oauth/access_token
client_secret = f208d9fc9cec4b69bdd5f8f1386a
client_secret = d836619eede4490fd12983b95961
grant_type = authorization_code
redirect_uri = http://localhost:1861/UI/Home.aspx
code = 6185508825da0c28a33ac5dcc77
note, 'code' i am getting when when user logs into insta. I used the following url to get the code:
https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code
I know authorize is correct becuase it gives me code in url
solved!
just changed response_type from code to token... it will give you access_token

How to execute a command via SSH in Elixir?

I know I can open a ssh connection to a remote server:
:ssh.start
:ssh.connect("11.22.33.44", 22, user: "my_login123")
But how can I actually send a command and receive a response from it? I don't mean the interactive mode, I want to just send a command and receive a reply.
It might just be easier to use an Elixir library such as SSHex as this actually uses the erlang :ssh library but provides a much nicer interface as well as making it simpler to accomplish what you are after.
E.g. From the readme
{:ok, conn} = SSHEx.connect ip: '123.123.123.123', user: 'myuser'
SSHEx.cmd! conn, 'mkdir -p /path/to/newdir'
res = SSHEx.cmd! conn, 'ls /some/path'
Where the value of res will be the response from the command
EDIT
However, if you are set on using :ssh. Then you would need to use the :ssh_connection modules exec command which takes in the :ssh connection as a parameter.
See this link here for more detail on how to do this.
Here is an example that uses only :ssh and no external libraries. To run it you will need to have public key login set up on your target host. For more information, read the Erlang SSH User's Guide.
ssh-connect.exs
#! /usr/bin/env elixir
:ssh.start()
{:ok, conn} = :ssh.connect('raspi', 22,
silently_accept_hosts: true,
user: System.get_env("USER") |> to_charlist(),
user_dir: Path.join(System.user_home!(), ".ssh") |> to_charlist(),
user_interaction: false,
)
{:ok, chan} = :ssh_connection.session_channel(conn, :infinity)
:success = :ssh_connection.exec(conn, chan, 'uname -a', :infinity)
for _ <- 0..3 do
receive do
{:ssh_cm, ^conn, value} -> IO.inspect(value)
end
end
:ok = :ssh.close(conn)
Sample output
{:data, 0, 0, "Linux raspberrypi 4.4.50+ #970 Mon Feb 20 19:12:50 GMT 2017 armv6l GNU/Linux\n"}
{:eof, 0}
{:exit_status, 0, 0}
{:closed, 0}
Use SSHex library can very convenient to build SSH connection.
Here is example below:
defmodule SshDemo do
#moduledoc false
def connect do
{:ok, conn} = SSHEx.connect ip: 'xxx.xxx.xxx.xxx', user: 'root', password: 'xxxxx'
SSHEx.cmd! conn, 'mkdir -p newdir'
end
end
If you use mix to create your project. You just add dependency in mix.exs file and run-- "mix deps.get"
defp deps do
[{:sshex, "2.1.2"}]
end
Then, you may compile this module. use -- "mix deps.compile".
Run above example will make a folder named newdir in ~/ path

JSCH setCommand is not working

No Exception comes and Command is also not making any work based on command mentioned.Here permisson of directory is not created and directory is also not created.Please give your suggestion.
Update :
channelexe.getExitStatus is added but problem is it gives -1, what is the meaning of this ?. I don't know how to find some explaination why command is not doing it's job(update 777 mode of fileDir1) .
String depDir = "/usr/local/FTPReceive/DEPLOYED/fileDir1";
log.info("updateDepositedFilePermission ........ starts");
Session session = new FTPComponent().getSession("");
Channel channel = null;
ChannelSftp channelSftp = null;
try
{
session.connect();
System.out.println("session is alive:" + session.isConnected());
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
ChannelExec channelexe = (ChannelExec) session.openChannel("exec");
channelexe.setCommand("chmod 777 -R " + depDir);
channelexe.connect();
System.out.println("channelexe.getExitStatus:"+channelexe.getExitStatus());
}
catch (Exception e1)
{
e1.printStackTrace();
System.out.println("Manual Exception in updateDepositedFilePermission:" + CommonUtil.getExceptionString(e1));
}
channelexe.setCommand("chmod 777 -R " + depDir);
channelexe.setCommand("mkdir /usr/local/fileStore");
channelexe.connect();
A ChannelExec accepts a single command string to invoke on the remote system. Your second call to setCommand() is discarding the chmod command and replacing it with the mkdir command. Assuming the remote shell is bash or similar, you could use shell syntax to construct a command string which runs both commands:
String cmd = "chmod 777 -R " + depDir + " && mkdir /usr/local/fileStore";
channelexe.setCommand(cmd);
No Exception comes...
ChannelExec doesn't throw an exception when a command merely fails. You can call Channel.getExitStatus() to get the exit status of the remote command. The value will be 0 if chmod and mkdir succeeded, or non-zero if they failed. The channel also has functions to read the standard error of the remote command, which will permit you to read any error messages which they output.
The JSCH website has several example programs, including an example of executing a remote command.

How can i update a jenkins job using the api

I have to create/update a jenkins job using its api because all of my jobs are using parameters which are also used by other scripts and I am trying to centralize the scripts so when i change it in one place, the change reflects in all.
currently, if someone changes the script, they they also have to manually edit the parameters of the jenkins job as well.
I saw the example of the Remote API for creating jobs and was able to successfully create test jobs but how can i edit an existing job besides deleting it and creating it again(which isnt an option as i have to maintain the build history).
You could use python like this:
from jenkinsapi.jenkins import Jenkins
jenkinsSource = 'http://10.52.123.124:8080/'
server = Jenkins(jenkinsSource, username = 'XXXXX', password = 'YYYYY')
myJob=server.get_job("__test")
myConfig=myJob.get_config()
print myConfig
new = myConfig.replace('<string>clean</string>', '<string>string bean</string>')
myJob.update_config(new)
in case anyone else is also looking for the same answer,
It appears the solution is far easier, all you have to do is update the config.xml and post the updated config.xml back to jenkins and your job will be updated.
You can also POST an updated config.xml to the URL which can fetch config.xml, to programmatically update the configuration of a job.
The fetch url pattern: $JENKINS_SERVER/job/$JOB_NAME/config.xml
detailed doc pattern: $JENKINS_SERVER/job/$JOB_NAME/api
example: https://ci.jenkins-ci.org/job/infra_atlassian-base/api/
http://asheepapart.blogspot.ca/2014/03/use-jenkins-rest-api-to-update-job.html
That little bit of scripting looks to be what you are looking for. Uses the REST API to get and set the config with some regex S&R in the middle.
Edit: Code below based on comment. It is copied directly from the blog so I take no credit for it.
# First, get the http://jenkins.example.com/job/folder-name/job/sample-job--template/configure looking like you want
read -s token
# type token from http://jenkins.example.com/user/$userName/configure
# Download the configuration XML for the template job (which will be our model template)
curl -v -u "bvanevery:$token" http://jenkins.example.com/job/folder-name/job/sample-job--template/config.xml > generic-config.xml
# My modules
declare modules=('module1' 'module2' 'module3')
# POST the updated configuration XML to Jenkins
for m in ${modules[#]}; do
echo "module $m";
sed "s/MODULE/$m/g" generic-config.xml > $m-config.xml;
curl -v -X POST --data-binary #$m-config.xml -u "bvanevery:$token" \
-H 'Content-Type: application/xml' \
"http://jenkins.example.com/job/folder-name/job/$m/config.xml" ;
done
For those using RestSharp, I found that I needed to make sure that:
The user ID performing the update had permission to do so under Manage > Global Security > Authorization Matrix
I had a current Jenkins Crumb token, required once CSRF (also under Manage > Security) is enabled.
Send the updated XML using a parameter of the Request object with the value of [ParameterType.RequestBody] (link)1 for the type argument.
private XmlDocument JobConfigGet()
{
Uri JobConfigURI = GetJenkinsURI("job/" + _args.JobName + "/config.xml", null);
RestClient restClient = new RestClient(JobConfigURI);
RestRequest restRequest = new RestRequest(Method.GET);
byte[] ua = Encoding.ASCII.GetBytes(Properties.Settings.Default.UserID + ":" + Properties.Settings.Default.UserPassword);
restRequest.AddHeader("authorization", "Basic " + Convert.ToBase64String(ua));
IRestResponse restResponse = restClient.Execute(restRequest);
if (restResponse.ResponseStatus != ResponseStatus.Completed || restResponse.StatusCode != HttpStatusCode.OK)
throw new Exception(string.Format("Unable to retrieve job config: {0}. Wrong ResponseStatus ({1}) or StatusCode ({2}) returned.\nURL: {3}", _args.JobName, restResponse.ResponseStatus.ToString(), restResponse.StatusCode.ToString(), restClient.BaseUrl.AbsoluteUri));
if (restResponse.ContentType != "application/xml")
throw new Exception("Unexpected data type returned for job config: " + _args.JobName + ". Expected 'application/xml'. Got: " + restResponse.ContentType + ".\nURL: " + restClient.BaseUrl.AbsoluteUri);
XmlDocument jobConfig = new XmlDocument();
jobConfig.LoadXml(restResponse.Content);
return jobConfig;
}
private void JobConfigUpdate(XmlDocument JobConfig, string JenkinCrumb)
{
// Update JobConfig XML as needed here.
Uri JobConfigURI = GetJenkinsURI("job/" + _args.JobName + "/config.xml", null);
RestClient restClient = new RestClient(JobConfigURI);
RestRequest restRequest = new RestRequest(Method.POST);
byte[] ua = Encoding.ASCII.GetBytes(Properties.Settings.Default.UserID + ":" + Properties.Settings.Default.UserPassword);
restRequest.AddHeader("authorization", "Basic " + Convert.ToBase64String(ua));
string[] crumbSplit = JenkinCrumb.Split(':');
restRequest.AddHeader(crumbSplit[0], crumbSplit[1]);
restRequest.AddParameter("text/xml", JobConfig.InnerXml, ParameterType.RequestBody);
IRestResponse restResponse = restClient.Execute(restRequest);
string resp = restResponse.Content;
}
curl -v -X POST https://jenkinsurl.fr:8443/job/jobname/config.xml --data-binary "#config.xml" -u "jenkinsusername:yourjenkinstoken" -H "Content-Type: application/xml"

Cisco VPN Client automatic login

I need to automate the login process of a Cisco VPN Client version 5.0.07.0440.
I've tried using a command line like this but there is something wrong:
vpnclient.exe connect MyVPNConnection user username pwd password
This starts the connection but then a User Authentication dialog is shown, asking for username, password and domain. Username and password are already filled, domain is not necessary.
To continue I must press the OK button.
Is there a way to not show the dialog and automatically login into the vpn?
Run vpnclient.exe /?:
That way just run
vpnclient.exe connect MyVPNConnection -s < file.txt
file.txt
username
password
Below worked for me Cisco AnyConnect Secure Mobility Client:
Try to connect to VPN for the first time using vpncli.exe and note every keystroke i.e every command, every enter( \n ) you press, username & password you enter.
Copy each command sequentially in .login_info file.
Sample .login_info:
connect unkbown.data-protect.com
\n
\n
KC23452
\n
Note: Replace \n with normal enter, these are the exact steps that I followed while connecting via vpncli.exe. Username and group-name were saved automatically that's the reason the 2nd and 3rd lines are \n ( enter ). Also, the last \n is required.
Go to C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client
Open CMD here
vpncli.exe -s < .login_info
First, we need to use the vpncli.exe command line approach with the -s switch.
It works from command line or script. If you were looking for a solution in C#:
//file = #"C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe"
var file = vpnInfo.ExecutablePath;
var host = vpnInfo.Host;
var profile = vpnInfo.ProfileName;
var user = vpnInfo.User;
var pass = vpnInfo.Password;
var confirm = "y";
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = file,
Arguments = string.Format("-s"),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
}
};
proc.OutputDataReceived += (s, a) => stdOut.AppendLine(a.Data);
proc.ErrorDataReceived += (s, a) => stdOut.AppendLine(a.Data);
//make sure it is not running, otherwise connection will fail
var procFilter = new HashSet<string>() { "vpnui", "vpncli" };
var existingProcs = Process.GetProcesses().Where(p => procFilter.Contains(p.ProcessName));
if (existingProcs.Any())
{
foreach (var p in existingProcs)
{
p.Kill();
}
}
proc.Start();
proc.BeginOutputReadLine();
//simulate profile file
var simProfile = string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}"
, Environment.NewLine
, string.Format("connect {0}", host)
, profile
, user
, pass
, confirm
);
proc.StandardInput.Write(simProfile);
proc.StandardInput.Flush();
//todo: these should be a configurable value
var waitTime = 500; //in ms
var maxWait = 10;
var count = 0;
var output = stdOut.ToString();
while (!output.Contains("state: Connected"))
{
output = stdOut.ToString();
if (count > maxWait)
throw new Exception("Unable to connect to VPN.");
count++;
Thread.Sleep(waitTime);
}
stdOut.Append("VPN connection established! ...");
(This might have extra stuff which is not required for you specific case.)
Here is a BAT script for automatic logon using Cisco AnyConnect Secure Mobility Client (version 4.10.03104):
taskkill -im vpnui.exe -f
"%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe" disconnect
SLEEP 3
"%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe" -s < credential.txt
SLEEP 8
"%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpnui.exe"
The file "credential.txt" must contain three lines:
connect <host>
<login>
<password>
where <host> is IP address or hostname of the host to connect, <login> is your login, and <password> is your password. The first line taskkill -im vpnui.exe -f is nesessary for killing the GUI, because when the GUI is running the login through the command line doesn't work. The last line launches the GUI again after successive logon.
The same script written as a VBS file (suitable for Windows Task Sheduler):
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "taskkill -im vpnui.exe -f"
WshShell.Run """%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe"" disconnect"
WScript.Sleep 3000
WshShell.Run "cmd /K ""%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe"" -s < credential.txt"
WScript.Sleep 8000
WshShell.Run """%PROGRAMFILES(x86)%\Cisco\Cisco AnyConnect Secure Mobility Client\vpnui.exe"""
Save this script as "login.vbs", and assign running it as an "Action" in the Windows Task Sheduler.