github.com/decred/dcrlnd@v0.7.6/scripts/verify-install.sh (about)

     1  #!/bin/bash
     2  
     3  REPO=lightningnetwork
     4  PROJECT=lnd
     5  
     6  RELEASE_URL=https://github.com/$REPO/$PROJECT/releases
     7  API_URL=https://api.github.com/repos/$REPO/$PROJECT/releases
     8  MANIFEST_SELECTOR=". | select(.name | test(\"manifest-v.*(\\\\.txt)$\")) | .name"
     9  SIGNATURE_SELECTOR=". | select(.name | test(\"manifest-.*(\\\\.sig)$\")) | .name"
    10  HEADER_JSON="Accept: application/json"
    11  HEADER_GH_JSON="Accept: application/vnd.github.v3+json"
    12  
    13  # All keys that can sign lnd releases. The key must be downloadable/importable
    14  # from the URL given after the space.
    15  KEYS=()
    16  KEYS+=("F4FC70F07310028424EFC20A8E4256593F177720 https://keybase.io/guggero/pgp_keys.asc")
    17  KEYS+=("15E7ECF257098A4EF91655EB4CA7FE54A6213C91 https://keybase.io/carlakirkcohen/pgp_keys.asc")
    18  KEYS+=("9C8D61868A7C492003B2744EE7D737B67FA592C7 https://keybase.io/bitconner/pgp_keys.asc")
    19  KEYS+=("E4D85299674B2D31FAA1892E372CBD7633C61696 https://keybase.io/roasbeef/pgp_keys.asc")
    20  KEYS+=("729E9D9D92C75A5FBFEEE057B5DD717BEF7CA5B1 https://keybase.io/wpaulino/pgp_keys.asc")
    21  KEYS+=("7E81EF6B9989A9CC93884803118759E83439A9B1 https://keybase.io/eugene_/pgp_keys.asc")
    22  KEYS+=("7AB3D7F5911708842796513415BAADA29DA20D26 https://keybase.io/halseth/pgp_keys.asc")
    23  
    24  function check_command() {
    25    echo -n "Checking if $1 is installed... "
    26    if ! command -v "$1"; then
    27      echo "ERROR: $1 is not installed or not in PATH!"
    28      exit 1
    29    fi
    30  }
    31  
    32  # By default we're picking up lnd and lncli from the system $PATH.
    33  LND_BIN=$(which lnd)
    34  LNCLI_BIN=$(which lncli)
    35  
    36  # If exactly two parameters are specified, we expect the first one to be lnd and
    37  # the second one to be lncli.
    38  if [[ $# -eq 2 ]]; then
    39    LND_BIN=$(realpath $1)
    40    LNCLI_BIN=$(realpath $2)
    41    
    42    # Make sure both files actually exist.
    43    if [[ ! -f $LND_BIN ]]; then
    44      echo "ERROR: $LND_BIN not found!"
    45      exit 1
    46    fi
    47    if [[ ! -f $LNCLI_BIN ]]; then
    48      echo "ERROR: $LNCLI_BIN not found!"
    49      exit 1
    50    fi
    51  elif [[ $# -eq 0 ]]; then
    52    # Make sure both binaries can be found and are executable.
    53    check_command lnd
    54    check_command lncli
    55  else
    56    echo "ERROR: invalid number of parameters!"
    57    echo "Usage: verify-install.sh [lnd-binary lncli-binary]"
    58    exit 1
    59  fi
    60  
    61  check_command curl
    62  check_command jq
    63  check_command gpg
    64  
    65  LND_VERSION=$($LND_BIN --version | cut -d'=' -f2)
    66  LNCLI_VERSION=$($LNCLI_BIN --version | cut -d'=' -f2)
    67  
    68  # Make this script compatible with both linux and *nix.
    69  SHA_CMD="sha256sum"
    70  if ! command -v "$SHA_CMD"; then
    71    if command -v "shasum"; then
    72      SHA_CMD="shasum -a 256"
    73    else
    74      echo "ERROR: no SHA256 sum binary installed!"
    75      exit 1
    76    fi
    77  fi
    78  LND_SUM=$($SHA_CMD $LND_BIN | cut -d' ' -f1)
    79  LNCLI_SUM=$($SHA_CMD $LNCLI_BIN | cut -d' ' -f1)
    80  
    81  echo "Detected lnd $LND_BIN version $LND_VERSION with SHA256 sum $LND_SUM"
    82  echo "Detected lncli $LNCLI_BIN version $LNCLI_VERSION with SHA256 sum $LNCLI_SUM"
    83  
    84  # Make sure lnd and lncli are installed with the same version and is an actual
    85  # version string.
    86  if [[ "$LNCLI_VERSION" != "$LND_VERSION" ]]; then
    87    echo "ERROR: Version $LNCLI_VERSION of lncli does not match $LND_VERSION of lnd!"
    88    exit 1
    89  fi
    90  version_regex="^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]"
    91  if [[ ! "$LND_VERSION" =~ $version_regex ]]; then
    92    echo "ERROR: Invalid version of lnd detected: $LND_VERSION"
    93    exit 1
    94  fi
    95  
    96  # Make sure the hash was actually calculated by looking at its length.
    97  if [[ ${#LND_SUM} -ne 64 ]]; then
    98    echo "ERROR: Invalid hash for lnd: $LND_SUM!"
    99    exit 1
   100  fi
   101  if [[ ${#LNCLI_SUM} -ne 64 ]]; then
   102    echo "ERROR: Invalid hash for lncli: $LNCLI_SUM!"
   103    exit 1
   104  fi
   105  
   106  # If we're inside the docker image, there should be a shasums.txt file in the
   107  # root directory. If that's the case, we first want to make sure we still have
   108  # the same hash as we did when building the image.
   109  if [[ -f /shasums.txt ]]; then
   110    if ! grep -q "$LND_SUM" /shasums.txt; then
   111      echo "ERROR: Hash $LND_SUM for lnd not found in /shasums.txt: "
   112      cat /shasums.txt
   113      exit 1
   114    fi
   115    if ! grep -q "$LNCLI_SUM" /shasums.txt; then
   116      echo "ERROR: Hash $LNCLI_SUM for lnd not found in /shasums.txt: "
   117      cat /shasums.txt
   118      exit 1
   119    fi
   120  fi
   121  
   122  # Import all the signing keys.
   123  for key in "${KEYS[@]}"; do
   124    KEY_ID=$(echo $key | cut -d' ' -f1)
   125    IMPORT_URL=$(echo $key | cut -d' ' -f2)
   126    echo "Downloading and importing key $KEY_ID from $IMPORT_URL"
   127    curl -L -s $IMPORT_URL | gpg --import
   128  
   129    # Make sure we actually imported the correct key.
   130    if ! gpg --list-key "$KEY_ID"; then
   131      echo "ERROR: Imported key from $IMPORT_URL doesn't match ID $KEY_ID."
   132    fi
   133  done
   134  
   135  echo ""
   136  
   137  # Download the JSON of the release itself. That'll contain the release ID we need for the next call.
   138  RELEASE_JSON=$(curl -L -s -H "$HEADER_JSON" "$RELEASE_URL/$LND_VERSION")
   139  
   140  TAG_NAME=$(echo $RELEASE_JSON | jq -r '.tag_name')
   141  RELEASE_ID=$(echo $RELEASE_JSON | jq -r '.id')
   142  echo "Release $TAG_NAME found with ID $RELEASE_ID"
   143  
   144  # Now download the asset list and filter by the manifest and the signatures.
   145  ASSETS=$(curl -L -s -H "$HEADER_GH_JSON" "$API_URL/$RELEASE_ID" | jq -c '.assets[]')
   146  MANIFEST=$(echo $ASSETS | jq -r "$MANIFEST_SELECTOR")
   147  SIGNATURES=$(echo $ASSETS | jq -r "$SIGNATURE_SELECTOR")
   148  
   149  # Download the main "manifest-*.txt" and all "manifest-*.sig" files containing
   150  # the detached signatures.
   151  TEMP_DIR=$(mktemp -d /tmp/lnd-sig-verification-XXXXXX)
   152  echo "Downloading $MANIFEST"
   153  curl -L -s -o "$TEMP_DIR/$MANIFEST" "$RELEASE_URL/download/$LND_VERSION/$MANIFEST"
   154  
   155  for signature in $SIGNATURES; do
   156    echo "Downloading $signature"
   157    curl -L -s -o "$TEMP_DIR/$signature" "$RELEASE_URL/download/$LND_VERSION/$signature"
   158  done
   159  
   160  echo ""
   161  cd $TEMP_DIR || exit 1
   162  
   163  # Before we even look at the content of the manifest, we first want to make sure
   164  # the signatures actually sign that exact manifest.
   165  NUM_CHECKS=0
   166  for signature in $SIGNATURES; do
   167    echo "Verifying $signature"
   168    if gpg --verify "$signature" "$MANIFEST" 2>&1 | grep -q "Good signature"; then
   169      echo "Signature for $signature appears valid: "
   170      gpg --verify "$signature" "$MANIFEST" 2>&1 | grep "using"
   171    elif gpg --verify "$signature" 2>&1 | grep -q "No public key"; then
   172      echo "Unable to verify signature $signature, no key available, skipping"
   173      continue
   174    else
   175      echo "ERROR: Did not get valid signature for $MANIFEST in $signature!"
   176      echo "  The developer signature $signature disagrees on the expected"
   177      echo "  release binaries in $MANIFEST. The release may have been faulty or"
   178      echo "  was backdoored."
   179      exit 1
   180    fi
   181  
   182    echo "Verified $signature against $MANIFEST"
   183    ((NUM_CHECKS=NUM_CHECKS+1))
   184  done
   185  
   186  # We want at least five signatures (out of seven public keys) that sign the
   187  # hashes of the binaries we have installed. If we arrive here without exiting,
   188  # it means no signature manifests were uploaded (yet) with the correct naming
   189  # pattern.
   190  MIN_REQUIRED_SIGNATURES=5
   191  if [[ $NUM_CHECKS -lt $MIN_REQUIRED_SIGNATURES ]]; then
   192    echo "ERROR: Not enough valid signatures found!"
   193    echo "  Valid signatures found: $NUM_CHECKS"
   194    echo "  Valid signatures required: $MIN_REQUIRED_SIGNATURES"
   195    echo
   196    echo "  Make sure the release $LND_VERSION contains the required "
   197    echo "  number of signatures on the manifest, or wait until more "
   198    echo "  signatures have been added to the release."
   199    exit 1
   200  fi
   201  
   202  # Then make sure that the hash of the installed binaries can be found in the
   203  # manifest that we now have verified the signatures for.
   204  if ! grep -q "^$LND_SUM" "$MANIFEST"; then
   205    echo "ERROR: Hash $LND_SUM for lnd not found in $MANIFEST: "
   206    cat "$MANIFEST"
   207    echo "  The expected release binaries have been verified with the developer "
   208    echo "  signatures. Your binary's hash does not match the expected release "
   209    echo "  binary hashes. Make sure you're using an official binary."
   210    exit 1
   211  fi
   212  
   213  if ! grep -q "^$LNCLI_SUM" "$MANIFEST"; then
   214    echo "ERROR: Hash $LNCLI_SUM for lncli not found in $MANIFEST: "
   215    cat "$MANIFEST"
   216    echo "  The expected release binaries have been verified with the developer "
   217    echo "  signatures. Your binary's hash does not match the expected release "
   218    echo "  binary hashes. Make sure you're using an official binary."
   219    exit 1
   220  fi
   221  
   222  echo ""
   223  echo "SUCCESS! Verified lnd and lncli against $MANIFEST signed by $NUM_CHECKS developers."