github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/script/keyring_validate.sh (about)

     1  #!/bin/bash
     2  # Copyright (C) 2023 SUSE LLC.
     3  # Copyright (C) 2023 Open Containers Authors
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #   http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  set -Eeuo pipefail
    18  
    19  project="runc"
    20  root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")"
    21  
    22  function log() {
    23  	echo "[*]" "$@" >&2
    24  }
    25  
    26  function bail() {
    27  	log "$@"
    28  	exit 1
    29  }
    30  
    31  # Temporary GPG keyring for messing around with.
    32  tmp_gpgdir="$(mktemp -d --tmpdir "$project-validate-tmpkeyring.XXXXXX")"
    33  trap 'rm -r "$tmp_gpgdir"' EXIT
    34  
    35  # Get the set of MAINTAINERS.
    36  readarray -t maintainers < <(sed -E 's|.* <.*> \(@?(.*)\)$|\1|' <"$root/MAINTAINERS")
    37  echo "------------------------------------------------------------"
    38  echo "$project maintainers:"
    39  printf " * %s\n" "${maintainers[@]}"
    40  echo "------------------------------------------------------------"
    41  
    42  # Create a dummy gpg keyring from the set of MAINTAINERS.
    43  while IFS="" read -r username || [ -n "$username" ]; do
    44  	curl -sSL "https://github.com/$username.gpg" |
    45  		gpg --no-default-keyring --keyring="$tmp_gpgdir/$username.keyring" --import
    46  done < <(printf '%s\n' "${maintainers[@]}")
    47  
    48  # Make sure all of the keys in the keyring have a github=... comment.
    49  awk <"$root/$project.keyring" '
    50  	/^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ { key_idx++; in_pgp=1; has_comment=0; }
    51  
    52  	# PGP comments are never broken up over several lines, and we only have one
    53  	# comment entry in our keyring file anyway.
    54  	in_pgp && /^Comment:.* github=\w+.*/ { has_comment=1 }
    55  
    56  	/^-----END PGP PUBLIC KEY BLOCK-----$/ {
    57  		if (!has_comment) {
    58  			print "[!] Key", key_idx, "in '$project'.keyring is missing a github= comment."
    59  			exit 1
    60  		}
    61  	}
    62  '
    63  
    64  echo "------------------------------------------------------------"
    65  echo "$project release managers:"
    66  sed -En "s|^Comment:.* github=(\w+).*| * \1|p" <"$root/$project.keyring" | sort -u
    67  echo "------------------------------------------------------------"
    68  gpg --no-default-keyring --keyring="$tmp_gpgdir/keyring" \
    69  	--import --import-options=show-only <"$root/$project.keyring"
    70  echo "------------------------------------------------------------"
    71  
    72  # Check that each entry in the kering is actually a maintainer's key.
    73  while IFS="" read -d $'\0' -r block || [ -n "$block" ]; do
    74  	username="$(sed -En "s|^Comment:.* github=(\w+).*|\1|p" <<<"$block")"
    75  
    76  	# FIXME: This is to work around codespell thinking that f-p-r is a
    77  	# misspelling of some other word, and the lack of support for inline
    78  	# ignores in codespell.
    79  	fprfield="f""p""r"
    80  
    81  	# Check the username is actually a maintainer. This is just a sanity check,
    82  	# since you can put whatever you like in the Comment field.
    83  	[ -f "$tmp_gpgdir/$username.keyring" ] || bail "User $username in runc.keyring is not a maintainer!"
    84  	grep "(@$username)$" "$root/MAINTAINERS" >/dev/null || bail "User $username in runc.keyring is not a maintainer!"
    85  
    86  	# Check that the key in the block actually matches a known key for that
    87  	# maintainer. Note that a block can contain multiple keys, so we need to
    88  	# check all of them. Since we have to handle multiple keys anyway, we'll
    89  	# also verify all of the subkeys (this is simpler to implement anyway since
    90  	# the --with-colons format outputs fingerprints for both primary and
    91  	# subkeys in the same way).
    92  	#
    93  	# Fingerprints have a field 1 of $fprfield and field 10 containing the
    94  	# fingerprint. See <https://github.com/gpg/gnupg/blob/master/doc/DETAILS>
    95  	# for more details.
    96  	while IFS="" read -r key || [ -n "$key" ]; do
    97  		gpg --no-default-keyring --keyring="$tmp_gpgdir/$username.keyring" \
    98  			--list-keys --with-colons | grep "$fprfield:::::::::$key:" >/dev/null ||
    99  			bail "(Sub?)Key $key in $project.keyring is NOT actually one of $username's keys!"
   100  		log "Successfully verified $username's (sub?)key $key is legitimate."
   101  	done < <(gpg --no-default-keyring \
   102  		--import --import-options=show-only --with-colons <<<"$block" |
   103  		grep "^$fprfield:" | cut -d: -f10)
   104  done < <(awk <"$root/$project.keyring" '
   105  	/^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ { in_block=1 }
   106  	in_block { print }
   107  	/^-----END PGP PUBLIC KEY BLOCK-----$/   { in_block=0; printf("\0"); }
   108  ')