-
Notifications
You must be signed in to change notification settings - Fork 20
ENT-13005: Added integrity check to cf-remote remote installation #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,70 @@ | ||||||
| #!/bin/bash | ||||||
|
|
||||||
| set -e | ||||||
|
|
||||||
| # Arg parsing | ||||||
|
|
||||||
| INSECURE=0 | ||||||
| HAS_WGET=0 | ||||||
| CHECKSUM="" | ||||||
|
|
||||||
| while getopts c:IW option; do | ||||||
| case "${option}" in | ||||||
| I) | ||||||
| INSECURE=1 | ||||||
| ;; | ||||||
| W) | ||||||
| HAS_WGET=1 | ||||||
| ;; | ||||||
| c) | ||||||
| CHECKSUM=$OPTARG | ||||||
| ;; | ||||||
| *) | ||||||
| echo "Usage: $0 [-I] [-c checksum] package_url" | ||||||
| exit 1 | ||||||
| ;; | ||||||
| esac | ||||||
| done | ||||||
|
|
||||||
| shift $((OPTIND - 1)) | ||||||
|
|
||||||
| PACKAGE=$1 | ||||||
| if [ -z "$PACKAGE" ]; then | ||||||
| echo "Usage: $0 [-I] [-c checksum] package_url" | ||||||
| exit 1; | ||||||
| fi | ||||||
|
|
||||||
| # temp file | ||||||
|
|
||||||
| tmpfile=$(mktemp) | ||||||
| cleanup() { | ||||||
| rm -f "$tmpfile" | ||||||
| } | ||||||
| trap cleanup EXIT QUIT TERM | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should |
||||||
|
|
||||||
| # Download | ||||||
|
|
||||||
| if [ "$HAS_WGET" -eq 1 ]; then | ||||||
| wget -nv -O "$tmpfile" "$PACKAGE" | ||||||
| else | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| curl --fail -sS -o "$tmpfile" "$PACKAGE" | ||||||
| fi | ||||||
|
|
||||||
| # Checksum | ||||||
|
|
||||||
| filename="$(basename "$PACKAGE")" | ||||||
|
|
||||||
| if [ -n "$CHECKSUM" ]; then | ||||||
| hash="$(sha256sum "$tmpfile" | awk '{print $1}')" | ||||||
|
|
||||||
| if [[ "$CHECKSUM" != "$hash" ]]; then | ||||||
| if [ "$INSECURE" -eq 0 ]; then | ||||||
| echo "Package '$PACKAGE' doesn't match the expected checksum '$CHECKSUM'. Run with --insecure to skip" | ||||||
| exit 1 | ||||||
| fi | ||||||
| echo "Package '$PACKAGE' doesn't match the expected checksum '$CHECKSUM'. Continuing due to insecure flag" | ||||||
| fi | ||||||
| fi | ||||||
|
|
||||||
| mv "$tmpfile" "$filename" | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| #!/usr/bin/env python3 | ||||||
| import sys | ||||||
| import re | ||||||
|
|
||||||
| from os.path import basename, dirname, join, exists | ||||||
| from collections import OrderedDict | ||||||
| from typing import Union | ||||||
|
|
@@ -276,7 +277,17 @@ def get_info(host, *, users=None, connection=None): | |||||
| data["role"] = "hub" if discovery.get("NTD_CFHUB") else "client" | ||||||
|
|
||||||
| data["bin"] = {} | ||||||
| for bin in ["dpkg", "rpm", "yum", "apt", "pkg", "zypper", "curl"]: | ||||||
| for bin in [ | ||||||
| "dpkg", | ||||||
| "rpm", | ||||||
| "yum", | ||||||
| "apt", | ||||||
| "pkg", | ||||||
| "zypper", | ||||||
| "curl", | ||||||
| "wget", | ||||||
| "sha256sum", | ||||||
| ]: | ||||||
| path = discovery.get("NTD_{}".format(bin.upper())) | ||||||
victormlg marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| if path: | ||||||
| data["bin"][bin] = path | ||||||
|
|
@@ -432,7 +443,7 @@ def bootstrap_host(host_data, policy_server, *, connection=None, trust_server=Tr | |||||
| def _package_from_list(tags, extension, packages): | ||||||
| artifacts = [Artifact(None, p) for p in packages] | ||||||
| artifact = filter_artifacts(artifacts, tags, extension)[-1] | ||||||
| return artifact.url | ||||||
| return artifact.url, artifact | ||||||
|
|
||||||
|
|
||||||
| def _package_from_releases( | ||||||
|
|
@@ -445,7 +456,7 @@ def _package_from_releases( | |||||
| release = releases.pick_version(version) | ||||||
| if release is None: | ||||||
| print("Could not find a release for version {}".format(version)) | ||||||
| return None | ||||||
| return None, None | ||||||
|
|
||||||
| release.init_download() | ||||||
|
|
||||||
|
|
@@ -455,7 +466,7 @@ def _package_from_releases( | |||||
| version, edition | ||||||
| ) | ||||||
| ) | ||||||
| return None | ||||||
| return None, None | ||||||
|
|
||||||
| artifacts = release.find(tags, extension) | ||||||
| if not artifacts: | ||||||
|
|
@@ -464,13 +475,16 @@ def _package_from_releases( | |||||
| "hub" if "hub" in tags else "client" | ||||||
| ) | ||||||
| ) | ||||||
| return None | ||||||
| return None, None | ||||||
| artifact = artifacts[-1] | ||||||
| if remote_download: | ||||||
| return artifact.url | ||||||
| return artifact.url, artifact | ||||||
| else: | ||||||
| return download_package( | ||||||
| artifact.url, checksum=artifact.checksum, insecure=insecure | ||||||
| return ( | ||||||
| download_package( | ||||||
| artifact.url, checksum=artifact.checksum, insecure=insecure | ||||||
| ), | ||||||
| artifact, | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -514,13 +528,85 @@ def get_package_from_host_info( | |||||
| tags.extend(tag for tag in package_tags if tag != "msi") | ||||||
|
|
||||||
| if packages is None: # No command line argument given | ||||||
| package = _package_from_releases( | ||||||
| package, artifact = _package_from_releases( | ||||||
| tags, extension, version, edition, remote_download, insecure | ||||||
| ) | ||||||
| else: | ||||||
| package = _package_from_list(tags, extension, packages) | ||||||
| package, artifact = _package_from_list(tags, extension, packages) | ||||||
|
|
||||||
| return package, artifact | ||||||
|
|
||||||
| return package | ||||||
|
|
||||||
| def _remote_download( | ||||||
| host, package, artifact, pkg_binary, insecure=False, connection=None | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A doc string explaining the arguments would be very helpful |
||||||
| ): | ||||||
|
|
||||||
| if not pkg_binary: | ||||||
| return None | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be an error message here? |
||||||
| if "sha256sum" not in pkg_binary: | ||||||
| if not insecure: | ||||||
| log.error( | ||||||
| "Cannot check file integrity. sha256sum is not installed on host. Run with --insecure to skip" | ||||||
| ) | ||||||
| return None | ||||||
| log.warning( | ||||||
| "Cannot check file integrity. sha256sum is not installed on host. Continuing due to insecure flag" | ||||||
| ) | ||||||
|
|
||||||
| if "wget" in pkg_binary: | ||||||
| has_wget = True | ||||||
| elif "curl" in pkg_binary: | ||||||
| has_wget = False | ||||||
| else: | ||||||
| log.error( | ||||||
| "Cannot download remotely. wget and/or curl are not installed on host" | ||||||
| ) | ||||||
| return None | ||||||
|
|
||||||
| if not (artifact and artifact.checksum): | ||||||
| if not insecure: | ||||||
| log.error( | ||||||
| "Cannot check file integrity. No artifact associated with package '{}' found. Run with --insecure to skip".format( | ||||||
| package | ||||||
| ) | ||||||
| ) | ||||||
| return None | ||||||
| log.warning( | ||||||
| "Cannot check file integrity. No artifact associated with package '{}' found. Continuing due to insecure flag".format( | ||||||
| package | ||||||
| ) | ||||||
| ) | ||||||
|
|
||||||
| cf_remote_dir = dirname(__file__) | ||||||
| script_path = join(cf_remote_dir, "remote-download.sh") | ||||||
| if not exists(script_path): | ||||||
| sys.exit("%s does not exist" % script_path) | ||||||
| scp( | ||||||
| script_path, | ||||||
| host, | ||||||
| connection, | ||||||
| hide=True, | ||||||
| ) | ||||||
|
|
||||||
| args = "" | ||||||
| if insecure: | ||||||
| args += "-I " | ||||||
| if has_wget: | ||||||
| args += "-W " | ||||||
| if artifact: | ||||||
| args += "-c {} ".format(artifact.checksum) | ||||||
| args += package | ||||||
|
|
||||||
| ret = ssh_cmd(connection, "bash remote-download.sh {}".format(args), errors=True) | ||||||
|
|
||||||
| if ret is None: | ||||||
| return None | ||||||
| if insecure: | ||||||
| log.warning(ret) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is logged here? The return code? The entire SSH output? |
||||||
|
|
||||||
| log.debug("Successfully remotely installed package on host") | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The script only downloads, it does not install?
Suggested change
|
||||||
|
|
||||||
| return basename(package) | ||||||
|
|
||||||
|
|
||||||
| @auto_connect | ||||||
|
|
@@ -552,9 +638,10 @@ def install_host( | |||||
| elif packages and len(packages) == 1: | ||||||
| package = packages[0] | ||||||
|
|
||||||
| artifact = None | ||||||
| if not package: | ||||||
| try: | ||||||
| package = get_package_from_host_info( | ||||||
| package, artifact = get_package_from_host_info( | ||||||
| data.get("package_tags"), | ||||||
| data.get("bin"), | ||||||
| data.get("arch"), | ||||||
|
|
@@ -574,19 +661,16 @@ def install_host( | |||||
| return 1 | ||||||
|
|
||||||
| if remote_download: | ||||||
| if ("bin" not in data) or ("curl" not in data["bin"]): | ||||||
| log.error( | ||||||
| "Couldn't download remotely. Curl is not installed on host '%s'" % host | ||||||
| ) | ||||||
| return 1 | ||||||
|
|
||||||
| print("Downloading '%s' on '%s' using curl" % (package, host)) | ||||||
| r = ssh_cmd( | ||||||
| cmd="curl --fail -O {}".format(package), connection=connection, errors=True | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code preferred |
||||||
| package = _remote_download( | ||||||
| host, | ||||||
| package, | ||||||
| artifact, | ||||||
| data.get("bin"), | ||||||
| connection=connection, | ||||||
| insecure=insecure, | ||||||
| ) | ||||||
| if r is None: | ||||||
| if package is None: | ||||||
| return 1 | ||||||
| package = basename(package) | ||||||
| elif not getattr(connection, "is_local", False): | ||||||
| scp(package, host, connection=connection) | ||||||
| package = basename(package) | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.