#!/bin/bash # Command line for ISPConfig remote user REST API using either smart functions or raw methods. # Author: Johan Ehnberg, johan@molnix.com set -e ### Variables VERSION=2018-04-23 PROGNAME=$(basename $0) DATESTAMP=`date +%s` ### Common commands # usage usage() { echo "Command line for ISPConfig remote user REST API using either smart functions or raw methods." echo echo "Usage: $PROGNAME -h/-f /-m [options] ..." echo echo "Common options:" echo echo " -a " echo " Config file. Options are read in the following order: system configuration, user configuration, the file specified in this option, other command line options. The last occurrence overrides previous ones." echo echo " -e " echo " API endpoint url such as https://myserver.example.com:8080/remote/json.php ." echo echo " -h" echo " Show this help text." echo echo " -i " echo " Server id (defaults to 1)." echo echo " -k" echo " Do not validate server certificate (for self-signed certificates)." echo echo " -q" echo " Quiet, only outputs results." echo echo " -v" echo " Verbose, outputs all info where available." echo echo "Functions combine several methods to carry out common tasks. Function options:" echo echo " -c " echo " Client user name to use." echo echo " -f " echo " Function to perform, such as dns_a_add. Cannot be used with -m, -d, -j or -s." echo echo " -p " echo " Password of the remote user specified in ISPConfig." echo echo " -u " echo " Name of the remote user specified in IPSConfig." echo echo " Available functions:" echo echo " clients List all clients" echo " dns_a_add Add or update a DNS A record" echo " dns_a_delete Delete a DNS A record" echo " dns_rr List all records for a zone" echo " dns_zones List all zones" echo " log_in Create a session (not needed by default)" echo " log_out Log out a session (not needed by default)" echo echo "Using a method wraps the raw request on the command line. Method options:" echo echo " -d " echo " Read JSON data from command line. Use escapes and quotes! Cannot be used with -j or -s." echo echo " -j " echo " Read JSON data from file. Cannot be used with -d or -s." echo echo " -m " echo " Raw method to use such as dns_a_add. Cannot be used with -f. Requires one of -d, -j or -s." echo echo " -s" echo " Read JSON data from stdin. Cannot be used with -d or -j." echo echo "For details on methods, see:" echo "https://git.ispconfig.org/ispconfig/ispconfig3/tree/master/remoting_client/API-docs" echo echo "Config files are bash files that can contain the following variables (with examples):" echo echo "remote_user=myuser # see -u" echo "remote_password=mypassword # see -p" echo "remote_url=https://myserver.example.com:8080/remote/json.php # see -e" echo "client_user=myclient # see -c" echo "server_id=1 # see -i" echo "ssl_validate=off # see -k" echo echo "Example uses:" echo " 1. Update a DNS A record or update if it already exists" echo " ispconfig-cli -f \"dns_a_add example.com. johnscomputer 192.168.0.99\"" echo } # message verbosity message message() { if [ $1 -le $VERBOSITY ]; then MESSAGE="${MESSAGE}${2}" fi } # fail dump fail() { echo -e "$MESSAGE" echo "Something went wrong. Here is the last response:" echo $1 log_out exit 1 } # restCall method data restCall() { curl $CURLK -sS -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -d "${2}" "${remote_url}?${1}" } # method method method() { restCall $1 "$JSONDATA" } ### Functions # List all clients # clients clients() { clientGet | jq .response } # Add or update an A record # dns_a_add zone name ip dns_a_add() { dns_zone_id $1 dns_a_id $1 $2 if [[ $dns_a_id =~ ^-?[0-9]+$ ]]; then message 2 "DNS A $2 exists with id $dns_a_id, updating rows: " message 1 "`dnsUpdateAByIdAndIp $dns_a_id $3 | jq -r .response`" elif [[ $dns_a_id == "" ]]; then message 2 "DNS A $2 does not exist, created ID: " message 1 "`dnsAddAByZoneAndNameAndIp $dns_zone_id $2 $3 | jq -r .response`\n" else fail $dns_a_id fi } # Delete an A record if it exists # dns_a_delete zone name dns_a_delete() { dns_zone_id $1 dns_a_id $1 $2 if [[ $dns_a_id =~ ^-?[0-9]+$ ]]; then message 2 "DNS A $2 has id $dns_a_id, deleted rows: " message 1 "`dnsDeleteAById $dns_a_id | jq -r '.response'`\n" else message 2 "DNS A $2 does not exist, skipping.\n" fi } # List all records for a zone # dns_rr zone dns_rr() { dns_zone_id $1 dnsGetRrByZone $dns_zone_id | jq .response } # List all zones # dns_zones dns_zones() { dnsGetZones | jq .response } # Log in log_in() { session_id=`restCall login "{\"username\": \"${remote_user}\",\"password\": \"${remote_password}\"}" | jq -r '.response'` if [[ $session_id == "false" ]]; then fail "Login failed!" else message 3 "Logged in with session_id $session_id as $remote_user.\n" client_id $client_user fi } # Log out # log_out session log_out() { if [[ `restCall logout "{\"session_id\": \"${1}\"}" |jq -r .response` == "true" ]]; then message 3 "Logged out session $1 successfully.\n" else fail "Logging out failed!" fi } ### ID helpers # Due to bash limitations, id's should only be set in _id functions below. # Do not run subshells anywhere else. # This is due to subshell nesting will otherwise lose the variables. # Get client id # client_id client_user client_id() { client_id=`clients | jq -r ".[] | select(.username == \"${1}\") | .client_id"` message 3 "Client $1 has id $client_id.\n" } # Get dns A id # dns_a_id zone name dns_a_id() { dns_a_id=`dns_rr $1 | jq -r ".[] | select(.name == \"${2}\") | .id"` message 3 "DNS A $2 has id $dns_a_id.\n" } # Get zone id # dns_zone_id zone dns_zone_id() { dns_zone_id=`dns_zones | jq -r ".[] | select(.origin == \"${1}\") | .id"` message 3 "DNS zone $1 has id $dns_zone_id.\n" } ### Methods # Get clients clientGet() { restCall client_get "{\"session_id\": \"${session_id}\",\"client_id\":{}}" } # Add A by zone and name and IP dnsAddAByZoneAndNameAndIp() { restCall dns_a_add "{\"session_id\": \"${session_id}\",\"client_id\": \"${client_id}\",\"params\": {\"server_id\": ${SERVER},\"zone\": \"${1}\",\"name\": \"${2}\",\"type\": \"a\",\"data\": \"${3}\",\"aux\": \"0\", \"ttl\": \"3600\", \"active\": \"y\", \"stamp\": \"${datestamp}\", \"serial\": \"1\"}}" } # Delete A by id dnsDeleteAById() { restCall dns_a_delete "{\"session_id\": \"${session_id}\",\"primary_id\": \"${1}\"}" } # Get RR by id dnsGetRrByZone() { restCall dns_rr_get_all_by_zone "{\"session_id\": \"${session_id}\",\"zone_id\": \"${1}\"}" } # Get zones dnsGetZones() { restCall dns_zone_get_by_user "{\"session_id\": \"${session_id}\",\"client_id\": \"${client_id}\",\"server_id\": ${SERVER}}" } # Update A by id and IP dnsUpdateAByIdAndIp() { restCall dns_a_update "{\"session_id\": \"${session_id}\",\"client_id\": \"${client_id}\",\"primary_id\": \"${1}\",\"params\": {\"data\": \"${2}\", \"stamp\": \"${datestamp}\"}}" } ### Run # Check dependencies if ! [ -x "$(command -v curl)" ]; then echo 'Error: curl is not installed.' >&2 exit 1 fi if ! [ -x "$(command -v jq)" ]; then echo 'Error: jq is not installed.' >&2 exit 1 fi # Check config files if [ -r /etc/ispconfig-cli.conf ]; then . /etc/ispconfig-cli.conf fi if [ -r ~/.ispconfig-cli ]; then . ~/.ispconfig-cli fi # Check command line if [[ $1 == "" ]]; then usage exit 1 fi SERVER=1 VERBOSITY=2 while getopts :a:e:hi:kqvc:f:p:u:d:j:m:s opt; do case $opt in a) if [[ -e $OPTARG ]]; then source $OPTARG else echo "Config file $OPTARG not found!" exit 1 fi ;; e) remote_url=$OPTARG ;; h) usage exit 1 ;; i) SERVER=$OPTARG ;; k) $ssl_validate=off ;; q) if [[ $VERBOSITY == "2" ]]; then VERBOSITY=1 else echo "-q and -v cannot be specified at the same time!" exit 1 fi ;; v) if [[ $VERBOSITY == "2" ]]; then VERBOSITY=3 else echo "-q and -v cannot be specified at the same time!" exit 1 fi ;; c) client_user=$OPTARG ;; f) if [[ $METHOD == "" ]]; then FUNCTION=$OPTARG else echo "Function and method cannot be specified at the same time!" exit 1 fi ;; p) remote_password=$OPTARG ;; u) remote_user=$OPTARG ;; d) if [[ $JSONMODE == "" ]]; then JSONMODE=cli JSONDATA=$OPTARG else echo "You can only use one JSON data source!" exit 1 fi ;; j) if [[ $JSONMODE == "" ]]; then JSONMODE=fil JSONDATA=`cat $OPTARG` else echo "You can only use one JSON data source!" exit 1 fi ;; m) if [[ $FUNCTION == "" ]]; then METHOD=$OPTARG else echo "Function and method cannot be specified at the same time!" exit 1 fi ;; s) if [[ $JSONMODE == "" ]]; then JSONMODE=std JSONDATA=`cat` else echo "You can only use one JSON data source!" exit 1 fi ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done if [[ $remote_url == "" ]]; then echo "No url provided!" exit 1 fi if [[ $ssl_validate == "off" ]]; then CURLK="-k" fi if [[ $METHOD != "" ]]; then method $METHOD elif [[ $FUNCTION == log_* ]]; then $FUNCTION elif [[ $FUNCTION != "" ]]; then log_in $FUNCTION log_out $session_id else echo "Neither method nor functions specified!" exit 1 fi echo -e $MESSAGE exit 0