Every now and again, it becomes necessary to remove a user's SSH key from a system. At End Point, we'll often allow multiple developers into multiple user accounts, so cleaning up these keys can be cumbersome. I decided to write a shell script to brush up on those skills, make sure I completed my task comprehensively, and automate future work.
Initial Design and Dependencies
My plan for this script is to accept a single
I've always found mlocate
to be very helpful; it makes finding files extremely fast and its usage is trivial. For this script, we'll use the output from locate
to find all authorized_keys files in the system. Of course, we'll want to make sure that the mlocate.db has recently been updated. So let's show the user when the database was last updated and offer them a chance to update it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | mlocate_path= "/var/lib/mlocate/mlocate.db" if [ -r $mlocate_path ] then echo -n "mlocate database last updated: " stat -c %y $mlocate_path echo -n "Do you want to update the locate database this script depends on? [y/n]: " read update_locate if [ "$update_locate" = "y" ] then echo "Updating locate database. This may take a few minutes..." updatedb echo "Update complete." fi else echo "Cannot read the mlocate db path: $mlocate_path" exit 2 fi |
First we define the path where we can find the mlocate database. Then we check to see if we can read that file. If we can't read the file, we let the user know and exit. If we can read the file, print the date and time it was last modified and offer the user a chance to update the database. While this is functional, it's pretty brittle. Let's make things a bit more flexible by letting locate
tell us where its database is.
1 2 3 4 5 6 7 8 9 10 11 | if mlocate_path=` locate -S` then # locate -S command will output database path in following format: # Database /full/path/to/db: (more output)... mlocate_path=${mlocate_path%:*} #remove content after colon mlocate_path=${mlocate_path #'Database '*} #remove 'Database ' else echo "Couldn't run locate command. Is mlocate installed?" exit 5 fi |
Instead of hard-coding the path to the database, we collect the locate database details using the -S
parameter. By using some string manipulation functions we can tease out the file path from the output.
Because we are going to offer to update the location database (as well as eventually manipulate authorized_keys files), it makes sense to check that we are root before proceeding. Additionally, let's check to see that we get a pattern from our user, and provide some usage guidance.
1 2 3 4 5 6 7 8 9 10 11 | if [ ! ` whoami ` = "root" ] then echo "Please run as root." exit 4 fi if [ -z $1 ] then echo "Usage: check_authorized_keys PATTERN" exit 3 fi |
Checking and modifying authorized_keys for a pattern
With some prerequisites in place, we're finally ready to scan the system's authorized_keys files. Let's just start with the syntax for that loop.
1 2 3 | for key_file in ` locate authorized_keys`; do echo "Searching $key_file..." done |
We do not specify a dollar sign ($) in front of key_file when defining the loop, but once inside our loop we use the regular syntax. We use command substitution by placing a command around back quotes (`) around the output of the command we want to use. We're now scanning each file, but how do we find matching entries?
1 2 3 4 5 6 | IFS=$ '\n' for matching_entry in ` grep "$1" $key_file`; do IFS= ' ' echo "Found an entry in $key_file:" echo $matching_entry done |
For each $key_file, we now grep our user's pattern ($1) and store it in $matching_entry. We have to change the Input Field Seperator (IFS) to a new line, instead of the default space, in order to capture each grepped line in its entriety. (Thanks to Brian Miller for that one!)
With a matching entry found in a key file, it's time to finally offer the user a chance to remove the entry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | echo "Found an entry in $key_file:" echo $matching_entry echo -n "Remove entry? [y/n]: " read remove_entry if [ "$remove_entry" = "y" ] then if [ ! -w $key_file ] then echo "Cannot write to $key_file." exit 1 else sed -i "/$matching_entry/d" $key_file echo "Deleted." fi else echo "Not deleted." fi |
We prompt the user if they want to delete the shown entry, verify we can write to the $key_file, and then delete the $matching entry. By using the -i
option to the sed
command, we are able to make modifications in place.
The Final Product
I'm sure there is a lot of room for improvement on this script and I'd welcome pull requests on the GitHub repo I setup for this little block of code. As always, be very careful when running automated scripts as root. Please test this script out on a non-production system before use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #!/bin/bash if [ ! ` whoami ` = "root" ] then echo "Please run as root." exit 4 fi if [ -z $1 ] then echo "Usage: check_authorized_keys PATTERN" exit 3 fi if mlocate_path=` locate -S` then # locate -S command will output database path in following format: # Database /full/path/to/db: (more output)... mlocate_path=${mlocate_path%:*} #remove content after colon mlocate_path=${mlocate_path #'Database '*} #remove 'Database ' else echo "Couldn't run locate command. Is mlocate installed?" exit 5 fi if [ -r $mlocate_path ] then echo -n "mlocate database last updated: " stat -c %y $mlocate_path echo -n "Do you want to update the locate database this script depends on? [y/n]: " read update_locate if [ "$update_locate" = "y" ] then echo "Updating locate database. This may take a few minutes..." updatedb echo "Update complete." echo "" fi else echo "Cannot read from $mlocate_path" exit 2 fi for key_file in ` locate authorized_keys`; do echo "Searching $key_file..." IFS=$ '\n' for matching_entry in ` grep "$1" $key_file`; do IFS= ' ' echo "Found an entry in $key_file:" echo $matching_entry echo -n "Remove entry? [y/n]: " read remove_entry if [ "$remove_entry" = "y" ] then if [ ! -w $key_file ] then echo "Cannot write to $key_file." exit 1 else sed -i "/$matching_entry/d" $key_file echo "Deleted." fi else echo "Not deleted." fi done done echo "Search complete." |
No comments:
Post a Comment