I have errors in a script, but only when it runs as a cron job - bash

I have a script that runs every hour to facilitate port forwarding with openvpn. It all works well when run from CLI, but fails when run through the same users cron. The part that fails is the end where it uses the value $PORT.
You can see that the values PORT and VPN_IP are not returning a value and the deluge command is failing.
Here is the result run directly:
Your VPN ipaddress is 10.107.1.6
Contacting PIA for port forwarding .......
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 106 100 14 100 92 4 30 0:00:03 0:00:03 --:--:-- 30
Port forwarding is currently using port 37186
Changing port settings on deluge....
Setting random_port to False..
Configuration value successfully updated.
Setting listen_ports to (37186, 37186)..
and here is the same script run through cron
crontab:
34 * * * * bash /home/alleyoopster/scripts/pia_portforward.sh > /home/alleyoopster/pia_port.log 2>&1
Result with no VPN address or Port address returned and errors:
Your VPN ipaddress is
Contacting PIA for port forwarding .......
Port forwarding is currently using port
Changing port settings on deluge....
Setting random_port to False..
Configuration value successfully updated.
malformed expression (,)
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/main.py", line 344, in do_command
ret = self._commands[cmd].handle(*args, **options.__dict__)
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/commands/config.py", line 104, in handle
return self._set_config(*args, **options)
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/commands/config.py", line 140, in _set_config
val = simple_eval(options["set"][1] + " " .join(args))
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/commands/config.py", line 87, in simple_eval
res = atom(src.next, src.next())
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/commands/config.py", line 56, in atom
out.append(atom(next, token))
File "/usr/lib/python2.7/dist-packages/deluge/ui/console/commands/config.py", line 79, in atom
raise SyntaxError("malformed expression (%s)" % token[1])
SyntaxError: malformed expression (,)
#! /bin/sh
#Simple bash script to facilitate Port Forwarding use with openvpn and PIA
#Use as a cron job to run every hour
#This script will also change the port in deluge. It needs deluge-console installed
#Transmission should also work with the correct commands
#YOUR SETTINGS
#Private Internet Access Username and Password here
USERNAME="username"
PASSWORD="password"
#Enter the correct tun here. Normally tun0. The command ifconfig will list your network config
TUN="tun0"
#Get the local ip address
VPN_IP=$(ifconfig $TUN|grep -oE "inet addr: *10\.[0-9]+\.[0-9]+\.[0-9]+"|tr -d "a-z :")
echo "Your VPN ipaddress is " $VPN_IP
echo Contacting PIA for port forwarding .......
TMP_PORT=$(curl -d "user=$USERNAME&pass=$PASSWORD&client_id=$(cat ~/.pia_client_id)&local_ip=$VPN_IP" https://www.privateinternetaccess.com/vpninfo/port_forward_assignment)
PORT=$(echo $TMP_PORT | sed "s/[^0-9]*//g")
echo "Port forwarding is currently using port "$PORT
echo "Changing port settings on deluge...."
deluge-console "config --set random_port False"
deluge-console "config --set listen_ports ($PORT,$PORT)"

It sounds like the PATH setting in your cron job doesn't match your user's PATH, and cron may not be finding the ifconfig command so that it can obtain the VPN IP address.
Either specify the full path to /sbin/ifconfig to get the local IP address, or add the following line at the top of your crontab (I'm just listing standard paths - adjust as necessary to suit your setup):
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

Related

rsyslogd does not write data to logfile when configured with TLS

I'm trying to set up rsyslog with TLS to forward specific records from /var/log/auth.log from host A to a remote server B.
The configuration file I wrote for rsyslog is the following:
$DefaultNetstreamDriverCAFile /etc/licensing/certificates/ca.pem
$DefaultNetstreamDriverCertFile /etc/licensing/certificates/client-cert.pem
$DefaultNetstreamDriverKeyFile /etc/licensing/certificates/client-key.pem
$InputFilePollInterval 10
#Read from the auth.log file and assign the tag "ssl-auth" for its messages
input
(type="imfile"
File="/var/log/auth.log"
reopenOnTruncate="on"
deleteStateOnFileDelete="on"
Tag="ssl-auth")
$template auth_log, " %msg% "
# Send ssl traffic to server on port 514
if ($syslogtag == 'ssl-auth') then{action
(type="omfwd"
protocol="tcp"
target="<ip#server>"
port="514"
template="auth_log"
StreamDriver="gtls"
StreamDriverMode="1"
StreamDriverAuthMode="x509/name"
)}
Using this configuration, when I try to ssh-login the first time into the host A from another host X everything works fine; the file /var/log/auth.log is written and the tcpdump shows traffic towards server B.
But from then on, it does not work anymore.
Even if I try to exit from host A and login back again whenever I do, the file /var/log/auth.log is not ever written and no traffic appears over tcpdump.
The very strange things is that if I remove the TLS from the configuration it works.

I'm using a 3 hop SSH with netmiko in Python and it does not like switching between the 2nd and 3rd hops

My topology is: Laptop -> 1st Jumphost (my company) -> 2nd jumphost (my clients company) -> Various network devices (my clients network devices).
The network devices are only accessible from the 2nd jumphost, and the 2nd jumphost is only accessible from the 1st jumphost. So I'm using netmiko in Python to achieve this. My code is below.
The 1st block of code SSH's to the 1st jumphost, and then from there SSH's to the 2nd jumphost. This works correctly.
The 2nd block of code then opens a text file containing the hostnames or IP's of the individual network devices that need to be queried. For each host in that file, it SSH's to it, issues the "show version" command and then disconnects from the device (using "exit") so that the session is returned to the 2nd jumphost, ready for the next device in the file.
This works correctly for the very first device, but crashes upon the "output = device.send_command('exit')" line. Netmiko claims that the pattern is not detected. I think I understand why, because netmiko is using the name in the hostname prompt, when this changes back to the 2nd jumphost hostname upon the disconnect it gets confused and throws an error. If this is the case I have 2 questions:
How come it copes OK when moving from the 1st jumphost to the 2nd jumphost AND from the 2nd jumphost to the network device. In both of these cases the hostname prompt also changes...
What's the solution? How can I safely move between the 2nd jumphost and network devices in order to achieve the loop?
from netmiko import ConnectHandler
import time
jump1 = "x.x.x.x"
jump2 = "y.y.y.y"
jump1_username = "myusername"
jump1_password = "mypassword"
jump2_username = "myusername"
jump2_password = "mypassword"
jump_type = "linux"
cmd_jump = "ssh " + jump2_username + "#" + jump2 + "\n"
device = ConnectHandler(device_type=jump_type, ip=jump1, username=jump1_username, password=jump1_password) # ssh to 1st jumphost
output = device.send_command('cat /proc/sys/kernel/hostname') #just shows me that login worked
print(output, flush=True) # just shows me that login worked
device.write_channel(cmd_jump) # enters ssh command for 2nd jumphost
time.sleep(1)
device.write_channel(jump2_password + "\n") # enters password for 2nd jumphost
time.sleep(1)
output = device.send_command('cat /proc/sys/kernel/hostname') #just shows me that second login worked
print(output, flush=True) # just shows me that second login worked
host_list = open(r'C:\device_list.txt','r') # a simple list of IP addresses you want to connect to each one on a new line
for host in host_list: # loop through network devices
host = host.strip()
cmd_device = "ssh " + host
device.write_channel(cmd_device + "\n") # ssh to each device
time.sleep(1)
device.write_channel(jump2_password + "\n") # enter ssh password (credientals are the same as the 2nd jumphost)
time.sleep(1)
output = device.send_command('sh ver') # run show version command
print(output, flush=True)
output = device.send_command('exit') ' disconnect from network device to return to the 2nd jumphost
time.sleep(1)
print(output, flush=True)
Ignore me, I've solved my own problem. It's because I wasn't using the "write_channel" to enter the 'exit' command. Doh!

How do I test the speed between my site and a proxy server?

I'm getting complaints from employees in the field that our site is slow. When I check it -- the speed is acceptable. They are all going through a proxy server that is not controlled by me.
I'd like to run a continuous ping to the proxy server, but I haven't found anything to do that.
How do I check the speed from my site to a proxy server?
You can set up a cronjob to ping a site of your choice, at the frequency you choose. Here I ping google.com every 15 minutes. I can adjust the number of times I ping with the flag -c count and the time between pings with -i interval. This time is in seconds, I can use shorter intervals if required, for example 0.5.
I then pipe to tail -n to only use the last line with the results. At this stage my output is as follows:
rtt min/avg/max/mdev = 12.771/17.448/23.203/4.022 ms
We then use awk to only take the 4th field and use tr to replace the slashes with commas. Finally we store the result in a CSV file.
Here is the whole line in crontab:.
*/15 * * * * ping -c 5 -i 1 google.com | tail -n 1 | awk '{ print $4 }' | tr "/" "," >> /home/john/pingLog.csv
It is important to run this as root. To do so we edit the crontab using sudo:
sudo crontab -e
The end result is a comma separated file that you can open in Excel or equivalent, or process as you wish.
As noted in the ping output the 4 figures are min/avg/max/mdev.
Here is a version for Windows. The result is not so refined as we had in the Linux version but we're still getting the essentiels. You could put it in a .bat file and run it with a planned task or put it directly in the planned task.
ping google.com | findstr Minimum >> TotalPings.txt
Which adds the following line every time it is run:
Minimum = 23ms, Maximum = 23ms, Moyenne = 23ms
You can change the server pinged to suit your needs.

command output not captured by shell script when invoked by snmp pass

The problem
SNMPD is correctly delegating SNMP polling requests to another program but the response from that program is not valid. A manual run of the program with the same arguments is responding correctly.
The detail
I've installed the correct LSI raid drivers on a server and want to configure SNMP. As per the instructions, I've added the following to /etc/snmp/snmpd.conf to redirect SNMP polling requests with a given OID prefix to a program:
pass .1.3.6.1.4.1.3582 /usr/sbin/lsi_mrdsnmpmain
It doesn't work correctly for SNMP polling requests:
snmpget -v1 -c public localhost .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1
I get the following response:
Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
Failed object: SNMPv2-SMI::enterprises.3582.5.1.4.2.1.2.1.32.1
What I've tried
SNMPD passes two arguments, -g and <oid> and expects a three line response <oid>, <data-type> and <data-value>.
If I manually run the following:
/usr/sbin/lsi_mrdsnmpmain -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
I correctly get a correct three line response:
.1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
integer
30
This means that the pass command is working correctly and the /usr/sbin/lsi_mrdsnmpmain program is working correctly in this example
I tried replacing /usr/sbin/lsi_mrdsnmpmain with a bash script. The bash script delegates the call and logs the supplied arguments and output from the delegated call:
#!/bin/bash
echo "In: '$#" > /var/log/snmp-pass-test
RETURN=$(/usr/sbin/lsi_mrdsnmpmain $#)
echo "$RETURN"
echo "Out: '$RETURN'" >> /var/log/snmp-pass-test
And modified the pass command to redirect to the bash script. If I run the bash script manually /usr/sbin/snmp-pass-test -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0 I get the correct three line response as I did when I ran /usr/sbin/lsi_mrdsnmpmain manually and I get the following logged:
In: '-g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
Out: '.1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
integer
30'
When I rerun the snmpget test, I get the same Error in packet... error and the bash script's logging shows that the captured delegated call output is empty:
In: '-g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.0
Out: ''
If I modify the bash script to only echo an empty line I also get the same Error in packet... message.
I've also tried ensuring that the environment variables that are present when I manually call /usr/sbin/lsi_mrdsnmpmain are the same for the bash script but I get the same empty output.
Finally, my questions
Why would the bash script behave differently in these two scenarios?
Is it likely that the problem that exists with the bash scripts is the same as originally noticed (manually running program has different output to SNMPD run program)?
Updates
eewanco's suggestions
What user is running the program in each scenario?
I added echo "$(whoami)" > /var/log/snmp-pass-test to the bash script and root was added to the logs
Maybe try executing it in cron
Adding the following to root's crontab and the correct three line response was logged:
* * * * * /usr/sbin/lsi_mrdsnmpmain -g .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1 >> /var/log/snmp-test-cron 2>&1
Grisha Levit's suggestion
Try logging the stderr
There aren't any errors logged
Checking /var/log/messages
When I run it via SNMPD, I get MegaRAID SNMP AGENT: Error in getting Shared Memory(lsi_mrdsnmpmain) logged. When I run it directly, I don't. I've done a bit of googling and I may need lm_sensors installed; I'll try this.
I installed lm_sensors & compat-libstdc++-33.i686 (the latter because it said it was a pre-requisite from the instructions and I was missing it), uninstalled and reinstalled the LSI drivers and am experiencing the same issue.
SELinux
I accidently stumbled upon a page about extending snmpd with scripts and it says to check the script has the right SELinux context. I ran grep AVC /var/log/audit/audit.log | grep snmp before and after running a snmpget and the following entry is added as a direct result from running snmpget:
type=AVC msg=audit(1485967641.075:271): avc: denied { unix_read unix_write } for pid=5552 comm="lsi_mrdsnmpmain" key=558265 scontext=system_u:system_r:snmpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=shm
I'm now assuming that SELinux is causing the call to fail; I'll dig further...see answer for solution.
strace (eewanco's suggestion)
Try using strace with and without snmp and see if you can catch a system call failure or some additional hints
For completeness, I wanted to see if strace would have hinted that SELinux was denying. I had to remove the policy packages using semodule -r <policy-package-name> to reintroduce the problem then ran the following:
strace snmpget -v1 -c public localhost .1.3.6.1.4.1.3582.5.1.4.2.1.2.1.32.1 >> strace.log 2>&1
The end of strace.log is as follows and unless I'm missing something, it doesn't seem to provide any hints:
...
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(161), sin_addr=inet_addr("127.0.0.1")}, msg_iov(1)= [{"0;\2\1\0\4\20public\240$\2\4I\264-m\2"..., 61}], msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_DONTWAIT|MSG_NOSIGNAL) = 61
select(4, [3], NULL, NULL, {0, 999997}) = 1 (in [3], left {0, 998475})
brk(0xab9000) = 0xab9000
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(161), sin_addr=inet_addr("127.0.0.1")}, msg_iov(1)= [{"0;\2\1\0\4\20public\242$\2\4I\264-m\2"..., 65536}], msg_controllen=0, msg_flags=0}, MSG_DONTWAIT) = 61
write(2, "Error in packet\nReason: (noSuchN"..., 81Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
) = 81
write(2, "Failed object: ", 15Failed object: ) = 15
write(2, "SNMPv2-SMI::enterprises.3582.5.1"..., 48SNMPv2- SMI::enterprises.3582.5.1.4.2.1.2.1.32.1
) = 48
write(2, "\n", 1
) = 1
brk(0xaa9000) = 0xaa9000
close(3) = 0
exit_group(2) = ?
+++ exited with 2 +++
It was SELinux that was denying snmpd a delegated call to /usr/sbin/lsi_mrdsnmpmain (and probably beyond).
To identify it, I ran grep AVC /var/log/audit/audit.log and for each entry, I ran the following:
echo "<grepped-output>" | audit2allow -a -M <filename>
This creates a SELinux policy package that should allow the delegated call through. The package is then loaded using the following:
semodule -i <filename>.pp
I had to do this 5 times as there were different causes of denial (unix_read unix_write, associate, read write). I'll look to combine the modules into one.
Now when I run snmpget I get the correct delegated output:
SNMPv2-SMI::enterprises.3582.5.1.4.2.1.2.1.32.1 = INTEGER: 34

Loop for checking string change in system function output (monitoring a DNS update)

I am switching DNS servers and I'd like to write a short ruby script that runs every 10s and triggers a local Mac OS X system notification as soon as my website resolves to a different IP.
Using terminal-notifier sending a system notification is as easy as this
terminal-notifier -message "DNS Changed"
I'd like to trigger it as soon as the output of
ping -i 10 mywebsite.com
... changes or simply does not contain a defined IP string anymore.
> 64 bytes from 12.34.56.789: icmp_seq=33 ttl=41 time=241.564 ms
in this case "12.34.56.789".
How do I monitor the change of the output string of the ping -i 10 mywebsite.com and call the notification function once a change has been detected?
I thought this might be a nice practice while waiting for the DNS to be updated.
Try this:
IP = "12.34.56.789"
p = IO.popen("ping -i 10 mywebsite.com")
p.each_line do |l|
if(! l =~ /from #{IP}/) #The IP has changed
system("terminal-notifier -message \"DNS Changed\"")
end
end

Resources