-1

I'm developping a script to search for patterns within scripts executed from CRON on a bunch of remote servers through SSH.

Script on client machine -- SSH --> Remote Servers CRON/Scripts

For now I can't get the correct output.

Script on client machine

#!/bin/bash
server_list=( '172.x.x.x' '172.x.x.y' '172.x.x.z' )
for s in ${server_list[@]}; do
    ssh -i /home/user/.ssh/my_key.rsa user@${s} crontab -l | grep -v '^#\|^[[:space:]]*$' | cut -d ' ' -f 6- | awk '{print $1}' | grep -v '^$\|^echo\|^find\|^PATH\|^/usr/bin\|^/bin/' | xargs -0 grep -in 'server.tld\|10.x.x.x'
done

This only gives me the paths of scripts from crontab, not the matched lines and line number plus the first line is prefixed with "grep:" keyword (example below):

grep: /opt/directory/script1.sh
/opt/directory/script2.sh
/opt/directory/script3.sh
/opt/directory/script4.sh

How to get proper output, meaning the script path plus line number plus line of matching pattern?

Remote CRON examples

OO 6 * * * /opt/directory/script1.sh foo 
30 6 * * * /opt/directory/script2.sh bar

Remote script content examples

1 ) This will match grep pattern

#!/bin/bash
ping -c 4 server.tld && echo "server.tld ($1)"

2 ) This won't match grep pattern

#!/bin/bash
ping -c 4 8.x.x.x && echo "8.x.x.x ($1)"
donmelchior
  • 893
  • 3
  • 13
  • 31
  • I can't figure out what it's supposed to do. Which parts are supposed to execute on the remote computer, and which parts local? At present, only `crontab -l` runs remotely, the rest of the pipe is local. Also, `xargs -0` only makes sense if its input is null-delimited (that's what `-0` means here), and the output from `grep` will not be. – Gordon Davisson Feb 01 '22 at 09:53
  • @gordon-davisson: I need to get the content of script(s) triggered by remote crontab and of course only the ones matching the grep search pattern (server.tld or 10.x.x.x) – donmelchior Feb 01 '22 at 10:08

1 Answers1

1

Without example input, it's really hard to see what your script is attempting to do. But the cron parsing could almost certainly be simplified tremendously by refactoring all of it into a single Awk script. Here is a quick stab, with obviously no way to test.

#!/bin/sh
# No longer using an array for no good reason, so /bin/sh will work
for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
    ssh -i /home/user/.ssh/my_key.rsa "user@${s}" crontab -l |
    awk '! /^#|^[[:space:]]*$/ && $6 !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print $6 }' |
    # no -0; use grep -E and properly quote literal dot
    xargs grep -Ein 'server\.tld|10.x.x.x'
done

Your command would not output null-delimited data to xargs so probably the immediate problem was that xargs -0 would receive all the file names as a single file name which obviously does not exist, and you forgot to include the ": file not found" from the end of the error message.

The use of grep -E is a minor hack to enable a more modern regex syntax which is more similar to that in Awk, where you don't have to backslash the "or" pipe etc.

This script, like your original, runs grep on the local system where you run the SSH script. If you want to run the commands on the remote server, you will need to refactor to put the entire pipeline in single quotes or a here document:

for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
    ssh -i /home/user/.ssh/my_key.rsa "user@${s}" <<\________HERE
        crontab -l |
        awk '! /^#|^[[:space:]]*$/ && $6 !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print $6 }' |
        xargs grep -Ein 'server\.tld|10.x.x.x'
________HERE
done

The refactored script contains enough complexities in the quoting that you probably don't want to pass it as an argument to ssh, which requires you to figure out how to quote strings both locally and remotely. It's easier then to pass it as standard input, which obviously just gets transmitted verbatim.

If you get "Pseudo-terminal will not be allocated because stdin is not a terminal.", try using ssh -t. Sometimes you need to add multiple -t options to completely get rid of this message.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • awk part triggers an "unterminated regexp" error – donmelchior Feb 02 '22 at 10:04
  • That's why it would be important to provide a [mre] but yeah, thanks for the feedback; fixed. – tripleee Feb 02 '22 at 10:06
  • example is already provided and issue is on the awk part of your command – donmelchior Feb 02 '22 at 10:07
  • Thanks for the update, but it's still not clear e.g. where the files are supposed to be sitting. Do you want to run `grep` on the system where the `cron` jobs are installed, or locally? – tripleee Feb 02 '22 at 10:08
  • yes there is, see CRON examples and Script content examples – donmelchior Feb 02 '22 at 10:09
  • grep should pickup patterns from remote CRON/scripts (where the cron are located) – donmelchior Feb 02 '22 at 10:15
  • So then the second code snippet in this answer is the solution. Thanks for clarifying. You might still want to edit your question to document this. – tripleee Feb 02 '22 at 10:17
  • passing heredoc this way to SSH triggers an "Pseudo-terminal will not be allocated because stdin is not a terminal.". Also awk returns an error "awk: fatal: Unmatched ( or \(: /^$|^(echo|find|PATH|\/usr\/bin|\/bin\//". I replaced heredoc by regular double quoted command and get rid of pseudo terminal message but awk returns an error – donmelchior Feb 02 '22 at 10:40
  • You will need quite a bit more quoting to refactor from a here document to a quoted string. You can probably remove the TTY message by adding a `-t` option (sometimes multiple) to the `ssh` command. – tripleee Feb 02 '22 at 10:48
  • Moved the misplaced closing parenthesis; sorry about that. – tripleee Feb 02 '22 at 10:50