Skip to content

Commit 8cb770f

Browse files
committed
Added integrity check to cf-remote remote installation
Ticket: ENT-13005 Signed-off-by: Victor Moene <victor.moene@northern.tech>
1 parent 82392ef commit 8cb770f

3 files changed

Lines changed: 152 additions & 23 deletions

File tree

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ include cf_remote/nt-discovery.sh
22
include cf_remote/Vagrantfile
33
include cf_remote/default_provision.sh
44
include cf_remote/demo.sql
5+
include cf_remote/remote-download.sh
6+

cf_remote/remote-download.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
# Arg parsing
6+
7+
INSECURE=0
8+
CHECKSUM=""
9+
10+
while getopts c:I option; do
11+
case "${option}" in
12+
I)
13+
INSECURE=1
14+
;;
15+
c)
16+
CHECKSUM=$OPTARG
17+
;;
18+
*)
19+
echo "Usage: $0 [-I] [-c checksum] package_url"
20+
exit 1
21+
;;
22+
esac
23+
done
24+
25+
shift $((OPTIND - 1))
26+
27+
PACKAGE=$1
28+
if [ -z "$PACKAGE" ]; then
29+
echo "Usage: $0 [-I] [-c checksum] package_url"
30+
exit 1;
31+
fi
32+
33+
# checks
34+
35+
HAS_SHA256SUM="$(command -v sha256sum)"
36+
37+
if [ -z "$HAS_SHA256SUM" ]; then
38+
if [ "$INSECURE" -eq 0 ]; then
39+
echo "Cannot check file integrity. sha256sum is not installed on host. Run with --insecure to skip"
40+
exit 1
41+
fi
42+
echo "Cannot check file integrity. sha256sum is not installed on host. Continuing due to insecure flag"
43+
fi
44+
45+
if [ -z "$CHECKSUM" ]; then
46+
if [ "$INSECURE" -eq 0 ]; then
47+
echo "Cannot check file integrity. No artifact associated with package '$PACKAGE' found. Run with --insecure to skip"
48+
exit 1
49+
fi
50+
echo "Cannot check file integrity. No artifact associated with package '$PACKAGE' found. Continuing due to insecure flag"
51+
fi
52+
53+
# temp file
54+
55+
tmpfile=$(mktemp)
56+
cleanup() {
57+
rm -f "$tmpfile"
58+
}
59+
trap cleanup EXIT QUIT TERM
60+
61+
# Download
62+
63+
if [ -n "$(command -v wget)" ]; then
64+
wget -nv -O "$tmpfile" "$PACKAGE"
65+
elif [ -n "$(command -v curl)" ]; then
66+
curl --fail -sS -o "$tmpfile" "$PACKAGE"
67+
else
68+
echo "Cannot download remotely. wget and/or curl are not installed on host"
69+
exit 1
70+
fi
71+
72+
# Checksum
73+
74+
filename="$(basename "$PACKAGE")"
75+
76+
if [ -n "$HAS_SHA256SUM" ] && [ -n "$CHECKSUM" ]; then
77+
hash="$(sha256sum "$tmpfile" | cut -d ' ' -f 1)"
78+
79+
if [[ "$CHECKSUM" != "$hash" ]]; then
80+
if [ "$INSECURE" -eq 0 ]; then
81+
echo "Package '$PACKAGE' doesn't match the expected checksum '$CHECKSUM'. Run with --insecure to skip"
82+
exit 1
83+
fi
84+
echo "Package '$PACKAGE' doesn't match the expected checksum '$CHECKSUM'. Continuing due to insecure flag"
85+
fi
86+
fi
87+
88+
mv "$tmpfile" "$filename"
89+

cf_remote/remote.py

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
import sys
33
import re
4+
45
from os.path import basename, dirname, join, exists
56
from collections import OrderedDict
67
from typing import Union
@@ -276,7 +277,17 @@ def get_info(host, *, users=None, connection=None):
276277
data["role"] = "hub" if discovery.get("NTD_CFHUB") else "client"
277278

278279
data["bin"] = {}
279-
for bin in ["dpkg", "rpm", "yum", "apt", "pkg", "zypper", "curl"]:
280+
for bin in [
281+
"dpkg",
282+
"rpm",
283+
"yum",
284+
"apt",
285+
"pkg",
286+
"zypper",
287+
"curl",
288+
"wget",
289+
"sha256sum",
290+
]:
280291
path = discovery.get("NTD_{}".format(bin.upper()))
281292
if path:
282293
data["bin"][bin] = path
@@ -432,7 +443,7 @@ def bootstrap_host(host_data, policy_server, *, connection=None, trust_server=Tr
432443
def _package_from_list(tags, extension, packages):
433444
artifacts = [Artifact(None, p) for p in packages]
434445
artifact = filter_artifacts(artifacts, tags, extension)[-1]
435-
return artifact.url
446+
return artifact.url, artifact
436447

437448

438449
def _package_from_releases(
@@ -445,7 +456,7 @@ def _package_from_releases(
445456
release = releases.pick_version(version)
446457
if release is None:
447458
print("Could not find a release for version {}".format(version))
448-
return None
459+
return None, None
449460

450461
release.init_download()
451462

@@ -455,7 +466,7 @@ def _package_from_releases(
455466
version, edition
456467
)
457468
)
458-
return None
469+
return None, None
459470

460471
artifacts = release.find(tags, extension)
461472
if not artifacts:
@@ -464,13 +475,16 @@ def _package_from_releases(
464475
"hub" if "hub" in tags else "client"
465476
)
466477
)
467-
return None
478+
return None, None
468479
artifact = artifacts[-1]
469480
if remote_download:
470-
return artifact.url
481+
return artifact.url, artifact
471482
else:
472-
return download_package(
473-
artifact.url, checksum=artifact.checksum, insecure=insecure
483+
return (
484+
download_package(
485+
artifact.url, checksum=artifact.checksum, insecure=insecure
486+
),
487+
artifact,
474488
)
475489

476490

@@ -514,13 +528,44 @@ def get_package_from_host_info(
514528
tags.extend(tag for tag in package_tags if tag != "msi")
515529

516530
if packages is None: # No command line argument given
517-
package = _package_from_releases(
531+
package, artifact = _package_from_releases(
518532
tags, extension, version, edition, remote_download, insecure
519533
)
520534
else:
521-
package = _package_from_list(tags, extension, packages)
535+
package, artifact = _package_from_list(tags, extension, packages)
536+
537+
return package, artifact
538+
539+
540+
def _remote_download(host, package, artifact, insecure=False, connection=None):
541+
542+
cf_remote_dir = dirname(__file__)
543+
script_path = join(cf_remote_dir, "remote-download.sh")
544+
if not exists(script_path):
545+
sys.exit("%s does not exist" % script_path)
546+
scp(
547+
script_path,
548+
host,
549+
connection,
550+
hide=True,
551+
)
552+
args = ""
553+
if insecure:
554+
args += "-I "
555+
if artifact:
556+
args += "-c {} ".format(artifact.checksum)
557+
args += package
522558

523-
return package
559+
ret = ssh_cmd(connection, "bash remote-download.sh {}".format(args), errors=True)
560+
561+
if ret is None:
562+
return None
563+
if insecure:
564+
log.warning(ret)
565+
566+
log.debug("Successfully remotely installed package on host")
567+
568+
return basename(package)
524569

525570

526571
@auto_connect
@@ -552,9 +597,10 @@ def install_host(
552597
elif packages and len(packages) == 1:
553598
package = packages[0]
554599

600+
artifact = None
555601
if not package:
556602
try:
557-
package = get_package_from_host_info(
603+
package, artifact = get_package_from_host_info(
558604
data.get("package_tags"),
559605
data.get("bin"),
560606
data.get("arch"),
@@ -574,19 +620,11 @@ def install_host(
574620
return 1
575621

576622
if remote_download:
577-
if ("bin" not in data) or ("curl" not in data["bin"]):
578-
log.error(
579-
"Couldn't download remotely. Curl is not installed on host '%s'" % host
580-
)
581-
return 1
582-
583-
print("Downloading '%s' on '%s' using curl" % (package, host))
584-
r = ssh_cmd(
585-
cmd="curl --fail -O {}".format(package), connection=connection, errors=True
623+
package = _remote_download(
624+
host, package, artifact, connection=connection, insecure=insecure
586625
)
587-
if r is None:
626+
if package is None:
588627
return 1
589-
package = basename(package)
590628
elif not getattr(connection, "is_local", False):
591629
scp(package, host, connection=connection)
592630
package = basename(package)

0 commit comments

Comments
 (0)