Skip to content

Instantly share code, notes, and snippets.

@DavidWittman
Last active December 10, 2022 18:02
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save DavidWittman/eaee7d909cef478ab898 to your computer and use it in GitHub Desktop.
Save DavidWittman/eaee7d909cef478ab898 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# Loads and mounts an ISO over SMB via the
# SuperMicro IPMI web interface
#
# usage: supermicro-mount-iso.sh <ipmi-host> <smb-host> <path>
# e.g.: supermicro-mount-iso.sh 10.0.0.1 10.0.0.2 '\foo\bar\windows.iso'
set -x
IPMI_HOST="$1"
SMB_HOST="$2"
ISO="$3"
USER=ADMIN
PASS=ADMIN
if [[ $# -ne 3 ]]; then
echo "usage: $0 <ipmi-host> <smb-host> <path>"
exit 1
fi
SESSION_ID=$(curl -d "name=${USER}&pwd=${PASS}" "https://${IPMI_HOST}/cgi/login.cgi" --silent --insecure -i | awk '/Set-Cookie/ && NR != 2 { print $2 }')
curl "https://${IPMI_HOST}/cgi/virtual_media_share_img.cgi" -H "Cookie: ${SESSION_ID}" --data-urlencode "host=${SMB_HOST}" --data-urlencode "path=${ISO}" --data-urlencode "user=" --data-urlencode "pwd=" --data-urlencode "_=" --insecure
sleep 1
curl "https://${IPMI_HOST}/cgi/uisopin.cgi" -H "Cookie: ${SESSION_ID}" --silent --insecure --data ""
@Contik
Copy link

Contik commented Sep 24, 2017

On X8DTi-F mainboards with latest IPMI firmware version 3.03 the URIs are a bit different. I used this script as a starting point and identified the following URIs.


Login:

/rpc/WEBSES/create.asp --data-urlencode WEBVAR_USERNAME="${USER}" --data-urlencode WEBVAR_PASSWORD="${PASS}"

Output looks like this:

//Dynamic Data Begin
 WEBVAR_JSONVAR_WEB_SESSION =
 {
 WEBVAR_STRUCTNAME_WEB_SESSION :
 [
 { 'SESSION_COOKIE' : 'aiceSUQz753HaXhdNxEah8ifOzmbleFb004' },  {} ],
 HAPI_STATUS:0 };
//Dynamic data end

Mount ISO image.
Notice that "${SMB_SHARE}" now exists in addition to hostname and path to ISO image. X8DTi-F boards with latest IPMI firmware explicitly specify the share name in this separate field. Also if necessary set "${SMB_USER}" and "${SMB_PASS}" but that part hasn't changed from before.

/rpc/setvmdrive.asp --cookie SessionCookie="${sessionCookie}" --data-urlencode VMDrive=1 --data-urlencode ImageType=1 --data-urlencode ShareHost="${SMB_HOST}" --data-urlencode ShareName="${SMB_SHARE}" --data-urlencode PathToImage="${ISO}" --data-urlencode Username="${SMB_USER}" --data-urlencode Password="${SMB_PASS}"

Bonus, log out:

/rpc/WEBSES/logout.asp --cookie SessionCookie="${sessionCookie}"

Bonus, verify login state:

/rpc/WEBSES/validate.asp --cookie SessionCookie="${sessionCookie}"

That last one returns for example HAPI_STATUS:2, not logged in, no session present for session cookie:

//Dynamic Data Begin
 WEBVAR_JSONVAR_WEB_SESSION_VALIDATE =
 {
 WEBVAR_STRUCTNAME_WEB_SESSION_VALIDATE :
 [
 {} ],
 HAPI_STATUS:2 };
//Dynamic data end

... or it returns HAPI_STATUS:0, cookie corresponds to valid active session (i.e. you're currently logged in):

//Dynamic Data Begin
 WEBVAR_JSONVAR_WEB_SESSION_VALIDATE =
 {
 WEBVAR_STRUCTNAME_WEB_SESSION_VALIDATE :
 [
 {} ],
 HAPI_STATUS:0 };
//Dynamic data end

Bonus, unmount ISO image:

/rpc/unsetvmdrive.asp --cookie SessionCookie="${sessionCookie}" --data-urlencode Type=1

Reworked script
For shits and giggles I'm also posting the script that resulted from above URI discoveries. Working on X8DTi-F mainboards with latest IPMI firmware version 3.03, it's based on above script, it's ShellCheck'd. You run it via either one of the following:

script.sh --mount 'https://ipmi.lan/' '10.20.30.40' 'iso-images' '/centos/centos-7-custom.iso'
script.sh --cleanup 'https://ipmi.lan/'

The first one makes sure your image is mounted, the second one cleans up, meaning it unmounts the currently mounted ISO image. It doesn't care if there's actually anything mounted or not, It just tries unmounting the ISO image and possibly fails if there wasn't an image in the first place. On a side note, using a values for "${SMB_SHARE}" without forward or backward slashes and using a value for "${ISO}" with a leading forward slash (to denote the ISO file location underneath your share) works on X8DTi-F. Backslashes may or may not work, No slashes may or may not work as well. I didn't try those combinations.

#!/bin/bash

declare scriptBasename usageText ipmiHost ipmiHost ipmiUser ipmiPassword smbHost smbShare smbISOPath smbUser smbPassword sessionCookie

scriptBasename=$(basename "${BASH_SOURCE[0]}")
usageText="[ERROR] Usage: ${scriptBasename} <action> <ipmiHost> [<smbHost> <smbShare> <smbISOPath>]\n               ${scriptBasename} --mount 'https://ipmi.lan/' '10.20.30.40' 'iso-images' '/centos/centos-7-custom.iso'\n               ${scriptBasename} --cleanup 'https://ipmi.lan/'"

if [[ "${1}" = '--mount' ]]; then
    [[ "${#}" -ne '5' || "${1}" != '--mount' ]] && { >&2 echo -e "${usageText}"; exit 1; }
elif [[ "${1}" = '--cleanup' ]]; then
    [[ "${#}" -ne '2' ]] && { >&2 echo -e "${usageText}"; exit 1; }
else
    { >&2 echo -e "${usageText}"; exit 1; }
fi

action="${1}"
ipmiHost="${2%/}"
smbHost="${3}"
smbShare="${4}"
smbISOPath="${5}"

smbUser=''
smbPassword=''

ipmiUser='ADMIN'
ipmiPassword='ADMIN'

function apiReturnCode () {
    declare curlOutput returnCode
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && curlOutput="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the cURL output to verify."; exit 1; }
    returnCode=$(grep -Pio '(?<=hapi_status:)[[:digit:]]+' <<< "${curlOutput}")
    [[ "${returnCode}" -eq '0' ]] && return 0 || return 1
}

function logIn () {
    declare curlOutput sessionCookie
    curlOutput=$(curl --silent --location "${ipmiHost}"/rpc/WEBSES/create.asp --data-urlencode WEBVAR_USERNAME="${ipmiUser}" --data-urlencode WEBVAR_PASSWORD="${ipmiPassword}")
    if ! apiReturnCode "${curlOutput}"; then { >&2 echo "[ERROR] Failed to login. Double-check your credentials."; exit 1; }; fi
    sessionCookie=$(grep -Pio '(?<=session_cookie'"'"' : '"'"')[[:alnum:]]+' <<< "${curlOutput}")
    echo -n "${sessionCookie}"
    return 0
}

function isValidLogin () {
    declare sessionCookie curlOutput
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    curlOutput=$(curl --silent --location "${ipmiHost}"/rpc/WEBSES/validate.asp --cookie SessionCookie="${sessionCookie}")
    if apiReturnCode "${curlOutput}"; then return 0; else return 1; fi
}

function removeMountedISOImage () {
    # Don't care if image is even mounted or not. Just remove, exit code
    # of this operation doesn't matter.
    declare sessionCookie curlOutput
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    curlOutput=$(curl --silent --location "${ipmiHost}"/rpc/unsetvmdrive.asp --cookie SessionCookie="${sessionCookie}" --data-urlencode Type=1)
    return 0
}

function mountISOImage () {
    declare sessionCookie curlOutput
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    echo '[INFO ] Mounting ISO image ...'
    curlOutput=$(curl --silent --location "${ipmiHost}"/rpc/setvmdrive.asp --cookie SessionCookie="${sessionCookie}" --data-urlencode VMDrive=1 --data-urlencode ImageType=1 --data-urlencode ShareHost="${smbHost}" --data-urlencode ShareName="${smbShare}" --data-urlencode PathToImage="${smbISOPath}" --data-urlencode Username="${smbUser}" --data-urlencode Password="${smbPassword}")
    if apiReturnCode "${curlOutput}"; then return 0; else return 1; fi
}

function logOut () {
    declare sessionCookie curlOutput
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    echo '[INFO ] Logging out ...'
    curlOutput=$(curl --silent --location "${ipmiHost}"/rpc/WEBSES/logout.asp --cookie SessionCookie="${sessionCookie}")
    if apiReturnCode "${curlOutput}"; then
        if ! isValidLogin "${sessionCookie}"; then
            echo '[INFO ] All done.'
            return 0
        else
            >&2 echo "[ERROR] Logout failed, session will time out on its own."
            return 0
        fi
    else
        return 1
    fi
}

function cleanup () {
    declare sessionCookie
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    if isValidLogin "${sessionCookie}"; then
        [[ "${action}" = '--cleanup' ]] && echo '[INFO ] Unmounting ISO image ...'
        removeMountedISOImage "${sessionCookie}"
    fi
}

function mount () {
    declare sessionCookie
    [[ "${1:+isSetToNonNull}" = 'isSetToNonNull' ]] && sessionCookie="${1}" || { >&2 echo "[ERROR] ${FUNC[0]} is missing mandatory positional argument 1, the session cookie."; exit 1; }
    mountISOImage "${sessionCookie}"
}

echo '[INFO ] Logging in ...'
sessionCookie=$(logIn)
[[ "${action}" = '--cleanup' ]] && cleanup "${sessionCookie}"
[[ "${action}" = '--mount' ]] && mount "${sessionCookie}"
logOut "${sessionCookie}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment