Skip to content

Commit 8fa07be

Browse files
committed
HBASE-23339 release scripts should use host gpg-agent when running in container.
* put together a docker container that can use host gpg-agent forwarded over ssh. * use gpg-agent forwarding container on OS X and directly forward the agent on Linux * clean up the release container on exit * use docker mounts instead of the deprecated volume syntax * use image names within our project namespace
1 parent 74c8673 commit 8fa07be

File tree

7 files changed

+299
-67
lines changed

7 files changed

+299
-67
lines changed

dev-support/create-release/README.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ anomalies are explained up in JIRA.
1717

1818
See http://hbase.apache.org/book.html#maven.release
1919

20+
Before starting an RC build, make sure your local gpg-agent has configs
21+
to properly handle your credentials, especially if you want to avoid
22+
typing the passphrase to your secret key.
23+
24+
e.g. if you are going to run and step away, best to increase the TTL
25+
on caching the unlocked secret via ~/.gnupg/gpg-agent.conf
26+
# in seconds, e.g. a day
27+
default-cache-ttl 86400
28+
max-cache-ttl 86400
29+
2030
Running a build on GCE is easy enough. Here are some notes if of use.
2131
Create an instance. 4CPU/15G/10G disk seems to work well enough.
2232
Once up, run the below to make your machine fit for RC building:

dev-support/create-release/do-release-docker.sh

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Options:
7575
-s [step] runs a single step of the process; valid steps are: tag|publish-dist|publish-release.
7676
If none specified, runs tag, then publish-dist, and then publish-release.
7777
'publish-snapshot' is also an allowed, less used, option.
78+
-x debug. do less clean up. (env file, gpg forwarding on mac)
7879
EOF
7980
exit 0
8081
}
@@ -84,7 +85,7 @@ IMGTAG=latest
8485
JAVA=
8586
RELEASE_STEP=
8687
GIT_REPO=
87-
while getopts "d:fhj:p:r:s:t:" opt; do
88+
while getopts "d:fhj:p:r:s:t:x" opt; do
8889
case $opt in
8990
d) WORKDIR="$OPTARG" ;;
9091
f) DRY_RUN=0 ;;
@@ -93,6 +94,7 @@ while getopts "d:fhj:p:r:s:t:" opt; do
9394
p) PROJECT="$OPTARG" ;;
9495
r) GIT_REPO="$OPTARG" ;;
9596
s) RELEASE_STEP="$OPTARG" ;;
97+
x) DEBUG=1 ;;
9698
h) usage ;;
9799
?) error "Invalid option. Run with -h for help." ;;
98100
esac
@@ -113,12 +115,26 @@ if [ -d "$WORKDIR/output" ]; then
113115
fi
114116
fi
115117

118+
if [ -f "${WORKDIR}/gpg-proxy.ssh.pid" ] || \
119+
[ -f "${WORKDIR}/gpg-proxy.cid" ] || \
120+
[ -f "${WORKDIR}/release.cid" ]; then
121+
read -r -p "container/pid files from prior run exists. Overwrite and continue? [y/n] " ANSWER
122+
if [ "$ANSWER" != "y" ]; then
123+
error "Exiting."
124+
fi
125+
fi
126+
116127
cd "$WORKDIR"
117128
rm -rf "$WORKDIR/output"
129+
rm -rf "${WORKDIR}/gpg-proxy.ssh.pid" "${WORKDIR}/gpg-proxy.cid" "${WORKDIR}/release.cid"
118130
mkdir "$WORKDIR/output"
119131

132+
banner "Gathering release details."
133+
HOST_OS="$(get_host_os)"
120134
get_release_info
121135

136+
banner "Setup"
137+
122138
# Place all RM scripts and necessary data in a local directory that must be defined in the command
123139
# line. This directory is mounted into the image. Its WORKDIR, the arg passed with -d.
124140
for f in "$SELF"/*; do
@@ -127,25 +143,65 @@ for f in "$SELF"/*; do
127143
fi
128144
done
129145

130-
GPG_KEY_FILE="$WORKDIR/gpg.key"
146+
# We need to import that public key in the container in order to use the private key via the agent.
147+
GPG_KEY_FILE="$WORKDIR/gpg.key.public"
148+
echo "Exporting public key for ${GPG_KEY}"
131149
fcreate_secure "$GPG_KEY_FILE"
132-
$GPG --passphrase "$GPG_PASSPHRASE" --export-secret-key --armor "$GPG_KEY" > "$GPG_KEY_FILE"
150+
$GPG "${GPG_ARGS[@]}" --export "${GPG_KEY}" > "${GPG_KEY_FILE}"
151+
152+
function cleanup {
153+
local id
154+
banner "Release Cleanup"
155+
if is_debug; then
156+
echo "skipping due to debug run"
157+
return 0
158+
fi
159+
echo "details in cleanup.log"
160+
if [ -f "${ENVFILE}" ]; then
161+
rm -f "$ENVFILE"
162+
fi
163+
rm -f "$GPG_KEY_FILE"
164+
if [ -f "${WORKDIR}/gpg-proxy.ssh.pid" ]; then
165+
id=$(cat "${WORKDIR}/gpg-proxy.ssh.pid")
166+
echo "Stopping ssh tunnel for gpg-agent at PID ${id}" | tee -a cleanup.log
167+
kill -9 "${id}" >>cleanup.log 2>&1 || true
168+
rm -f "${WORKDIR}/gpg-proxy.ssh.pid" >>cleanup.log 2>&1
169+
fi
170+
if [ -f "${WORKDIR}/gpg-proxy.cid" ]; then
171+
id=$(cat "${WORKDIR}/gpg-proxy.cid")
172+
echo "Stopping gpg-proxy container with ID ${id}" | tee -a cleanup.log
173+
docker kill "${id}" >>cleanup.log 2>&1 || true
174+
rm -f "${WORKDIR}/gpg-proxy.cid" >>cleanup.log 2>&1
175+
# TODO we should remove the gpgagent volume?
176+
fi
177+
if [ -f "${WORKDIR}/release.cid" ]; then
178+
id=$(cat "${WORKDIR}/release.cid")
179+
echo "Stopping release container with ID ${id}" | tee -a cleanup.log
180+
docker kill "${id}" >>cleanup.log 2>&1 || true
181+
rm -f "${WORKDIR}/release.cid" >>cleanup.log 2>&1
182+
fi
183+
}
184+
185+
trap cleanup EXIT
186+
187+
echo "Host OS: ${HOST_OS}"
188+
if [ "${HOST_OS}" == "DARWIN" ]; then
189+
run_silent "Building gpg-agent-proxy image with tag ${IMGTAG}..." "docker-proxy-build.log" \
190+
docker build --build-arg "UID=${UID}" --build-arg "RM_USER=${USER}" \
191+
--tag "org.apache.hbase/gpg-agent-proxy:${IMGTAG}" "${SELF}/mac-sshd-gpg-agent"
192+
fi
133193

134194
run_silent "Building hbase-rm image with tag $IMGTAG..." "docker-build.log" \
135-
docker build -t "hbase-rm:$IMGTAG" --build-arg UID=$UID "$SELF/hbase-rm"
195+
docker build --tag "org.apache.hbase/hbase-rm:$IMGTAG" --build-arg "UID=$UID" \
196+
--build-arg "RM_USER=${USER}" "$SELF/hbase-rm"
136197

198+
banner "Final prep for container launch."
199+
echo "Writing out environment for container."
137200
# Write the release information to a file with environment variables to be used when running the
138201
# image.
139202
ENVFILE="$WORKDIR/env.list"
140203
fcreate_secure "$ENVFILE"
141204

142-
function cleanup {
143-
rm -f "$ENVFILE"
144-
rm -f "$GPG_KEY_FILE"
145-
}
146-
147-
trap cleanup EXIT
148-
149205
cat > "$ENVFILE" <<EOF
150206
PROJECT=$PROJECT
151207
DRY_RUN=$DRY_RUN
@@ -161,15 +217,15 @@ GIT_NAME=$GIT_NAME
161217
GIT_EMAIL=$GIT_EMAIL
162218
GPG_KEY=$GPG_KEY
163219
ASF_PASSWORD=$ASF_PASSWORD
164-
GPG_PASSPHRASE=$GPG_PASSPHRASE
165220
RELEASE_STEP=$RELEASE_STEP
166221
API_DIFF_TAG=$API_DIFF_TAG
222+
HOST_OS=$HOST_OS
167223
EOF
168224

169-
JAVA_VOL=()
225+
JAVA_MOUNT=()
170226
if [ -n "$JAVA" ]; then
171227
echo "JAVA_HOME=/opt/hbase-java" >> "$ENVFILE"
172-
JAVA_VOL=(--volume "$JAVA:/opt/hbase-java")
228+
JAVA_MOUNT=(--mount "type=bind,src=${JAVA},dst=/opt/hbase-java,readonly")
173229
fi
174230

175231
#TODO some debug output would be good here
@@ -223,12 +279,59 @@ if [ -n "${GIT_REPO}" ]; then
223279
echo "GIT_REPO=${GIT_REPO}" >> "${ENVFILE}"
224280
fi
225281

226-
echo "Building $RELEASE_TAG; output will be at $WORKDIR/output"
227-
cmd=(docker run -ti \
282+
GPG_PROXY_MOUNT=()
283+
if [ "${HOST_OS}" == "DARWIN" ]; then
284+
GPG_PROXY_MOUNT=(--mount "type=volume,src=gpgagent,dst=/home/${USER}/.gnupg/")
285+
echo "Setting up GPG agent proxy container needed on OS X."
286+
echo " we should clean this up for you. If that fails the container ID is below and in " \
287+
"gpg-proxy.cid"
288+
#TODO the key pair used should be configurable
289+
docker run --rm -p 62222:22 \
290+
--detach --cidfile "${WORKDIR}/gpg-proxy.cid" \
291+
--mount \
292+
"type=bind,src=${HOME}/.ssh/id_rsa.pub,dst=/home/${USER}/.ssh/authorized_keys,readonly" \
293+
"${GPG_PROXY_MOUNT[@]}" \
294+
"org.apache.hbase/gpg-agent-proxy:${IMGTAG}"
295+
# gotta trust the container host
296+
ssh-keyscan -p 62222 localhost 2>/dev/null | sort > "${WORKDIR}/gpg-agent-proxy.ssh-keyscan"
297+
cat "${HOME}/.ssh/known_hosts" | sort | comm -1 -3 - "${WORKDIR}/gpg-agent-proxy.ssh-keyscan" > "${WORKDIR}/gpg-agent-proxy.known_hosts"
298+
if [ -s "${WORKDIR}/gpg-agent-proxy.known_hosts" ]; then
299+
declare host_key
300+
echo "Your ssh known_hosts does not include the entries for the gpg-agent proxy container."
301+
echo "The following entry(ies) arre missing:"
302+
cat "${WORKDIR}/gpg-agent-proxy.known_hosts" | sed -e 's/^/ /'
303+
read -r -p "Okay to add these entries to ${HOME}/.ssh/known_hosts? [y/n] " ANSWER
304+
if [ "$ANSWER" != "y" ]; then
305+
error "Exiting."
306+
fi
307+
cat "${WORKDIR}/gpg-agent-proxy.known_hosts" >> "${HOME}/.ssh/known_hosts"
308+
fi
309+
echo "Launching ssh reverse tunnel from the container to gpg agent."
310+
echo " we should clean this up for you. If that fails the PID is in gpg-proxy.ssh.pid"
311+
ssh -p 62222 -R "/home/${USER}/.gnupg/S.gpg-agent:$(gpgconf --list-dir agent-extra-socket)" \
312+
-i "${HOME}/.ssh/id_rsa" -N -n localhost >gpg-proxy.ssh.log 2>&1 &
313+
echo $! > "${WORKDIR}/gpg-proxy.ssh.pid"
314+
else
315+
# TODO this presumes we are still trying to make a local gpg-agent available to the container.
316+
# add an option so that we can run the buid on a remote machine and get the forwarded
317+
# gpg-agent in the container. Should look like the side-car container mount above.
318+
# it is important not to do that for a local linux agent because we only want the container
319+
# to get access to the restricted extra socket on our local gpg-agent.
320+
GPG_PROXY_MOUNT=(--mount \
321+
"type=bind,src=$(gpgconf --list-dir agent-extra-socket),dst=/home/${USER}/.gnupg/S.gpg-agent")
322+
fi
323+
324+
banner "Building $RELEASE_TAG; output will be at $WORKDIR/output"
325+
echo "We should clean the container up when we are done. If that fails then the container ID " \
326+
"is in release.cid"
327+
echo
328+
cmd=(docker run --rm -ti \
228329
--env-file "$ENVFILE" \
229-
--volume "$WORKDIR:/opt/hbase-rm" \
230-
"${JAVA_VOL[@]}" \
330+
--cidfile "${WORKDIR}/release.cid" \
331+
--mount "type=bind,src=${WORKDIR},dst=/home/${USER}/hbase-rm" \
332+
"${JAVA_MOUNT[@]}" \
231333
"${GIT_REPO_MOUNT[@]}" \
232-
"hbase-rm:$IMGTAG")
334+
"${GPG_PROXY_MOUNT[@]}" \
335+
"org.apache.hbase/hbase-rm:$IMGTAG")
233336
echo "${cmd[*]}"
234337
"${cmd[@]}"

dev-support/create-release/do-release.sh

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,41 @@ fi
4242

4343
# If running in docker, import and then cache keys.
4444
if [ "$RUNNING_IN_DOCKER" = "1" ]; then
45-
# Run gpg agent.
46-
eval "$(gpg-agent --disable-scdaemon --daemon --no-grab --allow-preset-passphrase \
47-
--default-cache-ttl=86400 --max-cache-ttl=86400)"
48-
echo "GPG Version: $(gpg --version)"
49-
# Inside docker, need to import the GPG keyfile stored in the current directory.
50-
# (On workstation, assume GPG has access to keychain/cache with key_id already imported.)
51-
echo "$GPG_PASSPHRASE" | $GPG --passphrase-fd 0 --import "$SELF/gpg.key"
45+
# when Docker Desktop for mac is running under load there is a delay before the mounted volume
46+
# becomes available. if we do not pause then we may try to use the gpg-agent socket before docker
47+
# has got it ready and we will not think there is a gpg-agent.
48+
if [ "${HOST_OS}" == "DARWIN" ]; then
49+
sleep 5
50+
fi
51+
# in docker our working dir is set to where all of our scripts are held
52+
# and we want default output to go into the "output" directory that should be in there.
53+
if [ -d "output" ]; then
54+
cd output
55+
fi
56+
echo "GPG Version: $("${GPG}" "${GPG_ARGS[@]}" --version)"
57+
# Inside docker, need to import the GPG key stored in the current directory.
58+
$GPG "${GPG_ARGS[@]}" --import "$SELF/gpg.key.public"
5259

5360
# We may need to adjust the path since JAVA_HOME may be overridden by the driver script.
5461
if [ -n "$JAVA_HOME" ]; then
62+
echo "Using JAVA_HOME from host."
5563
export PATH="$JAVA_HOME/bin:$PATH"
5664
else
5765
# JAVA_HOME for the openjdk package.
58-
export JAVA_HOME=/usr
66+
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
5967
fi
6068
else
6169
# Outside docker, need to ask for information about the release.
6270
get_release_info
6371
fi
72+
6473
GPG_TTY="$(tty)"
6574
export GPG_TTY
75+
echo "Testing gpg signing."
76+
echo "foo" > gpg_test.txt
77+
"${GPG}" "${GPG_ARGS[@]}" --detach --armor --sign gpg_test.txt
78+
# In --batch mode we have to be explicit about what we are verifying
79+
"${GPG}" "${GPG_ARGS[@]}" --verify gpg_test.txt.asc gpg_test.txt
6680

6781
if [[ -z "$RELEASE_STEP" ]]; then
6882
# If doing all stages, leave out 'publish-snapshot'

dev-support/create-release/hbase-rm/Dockerfile

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@ RUN wget -qO- "https://www.apache.org/dyn/mirrors/mirrors.cgi?action=download&fi
5050
tar xvz -C /opt
5151
ENV YETUS_HOME /opt/apache-yetus-${YETUS_VERSION}
5252

53-
WORKDIR /opt/hbase-rm/output
54-
5553
ARG UID
56-
RUN useradd -m -s /bin/bash -p hbase-rm -u $UID hbase-rm
57-
USER hbase-rm:hbase-rm
54+
ARG RM_USER
55+
RUN groupadd hbase-rm && \
56+
useradd --create-home --shell /bin/bash -p hbase-rm -u $UID $RM_USER && \
57+
mkdir /home/$RM_USER/.gnupg && \
58+
chown -R $RM_USER:hbase-rm /home/$RM_USER && \
59+
chmod -R 700 /home/$RM_USER
60+
61+
USER $RM_USER:hbase-rm
62+
WORKDIR /home/$RM_USER/hbase-rm/
5863

59-
ENTRYPOINT [ "/opt/hbase-rm/do-release.sh" ]
64+
ENTRYPOINT [ "./do-release.sh" ]

0 commit comments

Comments
 (0)