Skip to content
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

[secure-boot] merge changes from init-actions #106

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ python generate_custom_image.py \
default value of 300 seconds will be used.
* **--dry-run**: Dry run mode which only validates input and generates
workflow script without creating image. Disabled by default.
* **--trusted-cert**: a certificate in DER format to be inserted
into the custom image's EFI boot sector. Can be generated by
reading examples/secure-boot/README.md. This argument is mutually
exclusive with base-image-family

* **--trusted-cert**: (Optional) Pass an empty string to this
argument to disable support for shielded-secure-boot.

* **--metadata**: VM metadata which can be read by the customization script
with `/usr/share/google/get_metadata_value attributes/<key>` at runtime. The
value of this flag takes the form of `key1=value1,key2=value2,...`. If the
Expand Down
5 changes: 2 additions & 3 deletions custom_image_utils/args_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,7 @@ def parse_args(args):
type=str,
required=False,
default="tls/db.der",
help="""(Optional) Inserts the specified DER-format certificate into
the custom image's EFI boot sector for use with secure boot.""")

help="""(Optional) Pass an empty string to this argument to
disable support for shielded-secure-boot.""")

return parser.parse_args(args)
60 changes: 35 additions & 25 deletions custom_image_utils/shell_script_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
local -r cmd="$*"

for ((i = 0; i < 3; i++)); do
if eval "$cmd"; then return 0 ; fi
set -x
time eval "$cmd" > "/tmp/{run_id}/install.log" 2>&1 && retval=$? || {{ retval=$? ; cat "/tmp/{run_id}/install.log" ; }}
set +x
if [[ $retval == 0 ]] ; then return 0 ; fi
sleep 5
done
return 1
Expand All @@ -44,23 +47,24 @@
function exit_handler() {{
echo 'Cleaning up before exiting.'

if [[ -f /tmp/{run_id}/vm_created ]]; then
if [[ -f /tmp/{run_id}/vm_created ]]; then ( set +e
echo 'Deleting VM instance.'
execute_with_retries gcloud compute instances delete {image_name}-install \
--project={project_id} --zone={zone} -q
elif [[ -f /tmp/{run_id}/disk_created ]]; then
execute_with_retries \
gcloud compute instances delete {image_name}-install --project={project_id} --zone={zone} -q
) elif [[ -f /tmp/{run_id}/disk_created ]]; then
echo 'Deleting disk.'
execute_with_retries gcloud compute ${{base_obj_type}} delete {image_name}-install --project={project_id} --zone={zone} -q
execute_with_retries \
gcloud compute ${{base_obj_type}} delete {image_name}-install --project={project_id} --zone={zone} -q
fi

echo 'Uploading local logs to GCS bucket.'
gsutil -m rsync -r {log_dir}/ {gcs_log_dir}/

if [[ -f /tmp/{run_id}/image_created ]]; then
echo -e "${{GREEN}}Workflow succeeded, check logs at {log_dir}/ or {gcs_log_dir}/${{NC}}"
echo -e "${{GREEN}}Workflow succeeded${{NC}}, check logs at {log_dir}/ or {gcs_log_dir}/"
exit 0
else
echo -e "${{RED}}Workflow failed, check logs at {log_dir}/ or {gcs_log_dir}/${{NC}}"
echo -e "${{RED}}Workflow failed${{NC}}, check logs at {log_dir}/ or {gcs_log_dir}/"
exit 1
fi
}}
Expand Down Expand Up @@ -111,11 +115,13 @@

local cert_args=""
local num_src_certs="0"
metadata_arg="{metadata_flag}"
if [[ -n '{trusted_cert}' ]] && [[ -f '{trusted_cert}' ]]; then
# build tls/ directory from variables defined near the header of
# the examples/secure-boot/create-key-pair.sh file

eval "$(bash examples/secure-boot/create-key-pair.sh)"
metadata_arg="${{metadata_arg}},public_secret_name=${{public_secret_name}},private_secret_name=${{private_secret_name}},secret_project=${{secret_project}},secret_version=${{secret_version}}"

# by default, a gcloud secret with the name of efi-db-pub-key-042 is
# created in the current project to store the certificate installed
Expand All @@ -132,16 +138,20 @@

local -a cert_list=()

local -a default_cert_list=("{trusted_cert}" "${{MS_UEFI_CA}}")
local -a default_cert_list
default_cert_list=("{trusted_cert}" "${{MS_UEFI_CA}}")
local -a src_img_modulus_md5sums=()

mapfile -t src_img_modulus_md5sums < <(print_img_dbs_modulus_md5sums {dataproc_base_image})
num_src_certs="${{#src_img_modulus_md5sums[@]}}"
echo "${{num_src_certs}} db certificates attached to source image"
if [[ "${{num_src_certs}}" -eq "0" ]]; then
echo "debug - num_src_certs: [${{#src_img_modulus_md5sums[*]}}]"
echo "value of src_img_modulus_md5sums: [${{src_img_modulus_md5sums}}]"
if [[ -z "${{src_img_modulus_md5sums}}" ]]; then
num_src_certs=0
echo "no db certificates in source image"
cert_list=default_cert_list
cert_list=( "${{default_cert_list[@]}}" )
else
echo "${{num_src_certs}} db certificates attached to source image"
echo "db certs exist in source image"
for cert in ${{default_cert_list[*]}}; do
if test_element_in_array "$(print_modulus_md5sum ${{cert}})" ${{src_img_modulus_md5sums[@]}} ; then
Expand Down Expand Up @@ -175,7 +185,8 @@
echo 'Creating image.'
base_obj_type="images"
instance_disk_args='--image-project={project_id} --image={image_name}-install --boot-disk-size={disk_size}G --boot-disk-type=pd-ssd'
time execute_with_retries gcloud compute images create {image_name}-install \
execute_with_retries \
gcloud compute images create {image_name}-install \
--project={project_id} \
--source-image={dataproc_base_image} \
${{cert_args}} \
Expand All @@ -186,7 +197,7 @@
echo 'Creating disk.'
base_obj_type="disks"
instance_disk_args='--disk=auto-delete=yes,boot=yes,mode=rw,name={image_name}-install'
time execute_with_retries gcloud compute disks create {image_name}-install \
execute_with_retries gcloud compute disks create {image_name}-install \
--project={project_id} \
--zone={zone} \
--image={dataproc_base_image} \
Expand All @@ -197,8 +208,7 @@

date
echo 'Creating VM instance to run customization script.'
( set -x
time execute_with_retries gcloud compute instances create {image_name}-install \
execute_with_retries gcloud compute instances create {image_name}-install \
--project={project_id} \
--zone={zone} \
{network_flag} \
Expand All @@ -209,15 +219,16 @@
{accelerator_flag} \
{service_account_flag} \
--scopes=cloud-platform \
{metadata_flag} \
--metadata-from-file startup-script=startup_script/run.sh )
"${{metadata_arg}}" \
--metadata-from-file startup-script=startup_script/run.sh

touch /tmp/{run_id}/vm_created

# clean up intermediate install image
if [[ "${{base_obj_type}}" == "images" ]] ; then
execute_with_retries gcloud compute images delete -q {image_name}-install --project={project_id}
fi
if [[ "${{base_obj_type}}" == "images" ]] ; then ( set +e
# This sometimes returns an API error but deletes the image despite the failure
gcloud compute images delete -q {image_name}-install --project={project_id}
) fi

echo 'Waiting for customization script to finish and VM shutdown.'
execute_with_retries gcloud compute instances tail-serial-port-output {image_name}-install \
Expand All @@ -226,7 +237,7 @@
--port=1 2>&1 \
| grep 'startup-script' \
| sed -e 's/ {image_name}-install.*startup-script://g' \
| dd bs=1 of={log_dir}/startup-script.log \
| dd status=none bs=1 of={log_dir}/startup-script.log \
|| true
echo 'Checking customization script result.'
date
Expand All @@ -243,13 +254,12 @@

date
echo 'Creating custom image.'
( set -x
time execute_with_retries gcloud compute images create {image_name} \
execute_with_retries gcloud compute images create {image_name} \
--project={project_id} \
--source-disk-zone={zone} \
--source-disk={image_name}-install \
{storage_location_flag} \
--family={family} )
--family={family}

touch /tmp/{run_id}/image_created
}}
Expand Down
58 changes: 26 additions & 32 deletions examples/secure-boot/build-current-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ function configure_service_account() {
gcloud secrets add-iam-policy-binding "${public_secret_name}" \
--member="serviceAccount:${GSA}" \
--role="roles/secretmanager.secretAccessor" > /dev/null 2>&1

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${GSA}" \
--role=roles/compute.instanceAdmin.v1 > /dev/null 2>&1

gcloud iam service-accounts add-iam-policy-binding "${GSA}" \
--member="serviceAccount:${GSA}" \
--role=roles/iam.serviceAccountUser > /dev/null 2>&1

}

function revoke_bindings() {
Expand All @@ -66,6 +75,15 @@ function revoke_bindings() {
gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${GSA}" \
--role="roles/secretmanager.viewer" > /dev/null 2>&1

gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${GSA}" \
--role=roles/compute.instanceAdmin.v1 > /dev/null 2>&1

gcloud iam service-accounts remove-iam-policy-binding "${GSA}" \
--member="serviceAccount:${GSA}" \
--role=roles/iam.serviceAccountUser > /dev/null 2>&1

}

export PROJECT_ID="$(jq -r .PROJECT_ID env.json)"
Expand All @@ -85,49 +103,25 @@ configure_service_account
session_name="build-current-images"

readonly timestamp="$(date +%F-%H-%M)"
#readonly timestamp="2024-10-24-04-21"
#readonly timestamp="2024-11-27-06-47"
export timestamp

export tmpdir=/tmp/${timestamp};
mkdir ${tmpdir}
mkdir -p ${tmpdir}
export ZONE="$(jq -r .ZONE env.json)"
gcloud compute instances list --zones "${ZONE}" --format json > ${tmpdir}/instances.json
gcloud compute images list --format json > ${tmpdir}/images.json

# Run generation scripts simultaneously for each dataproc image version
screen -US "${session_name}" -c examples/secure-boot/pre-init.screenrc
screen -L -US "${session_name}" -c examples/secure-boot/pre-init.screenrc

# tail -n 3 /tmp/custom-image-*/logs/workflow.log
# tail -n 3 /tmp/custom-image-*/logs/startup-script.log
# tail -n 3 /tmp/custom-image-${PURPOSE}-2-*/logs/workflow.log
function find_disk_usage() {
test -f /tmp/genline.pl || cat > /tmp/genline.pl<<'EOF'
#!/usr/bin/perl -w
use strict;

my $fn = $ARGV[0];
my( $config ) = ( $fn =~ /custom-image-(.*-(debian|rocky|ubuntu)\d+)-\d+/ );

my @raw_lines = <STDIN>;
my( $l ) = grep { m: /dev/.*/\s*$: } @raw_lines;
my( $stats ) = ( $l =~ m:\s*/dev/\S+\s+(.*?)\s*$: );

my( $dp_version ) = ($config =~ /-pre-init-(.+)/);
$dp_version =~ s/-/./;

my($max) = map { / maximum-disk-used: (\d+)/ } @raw_lines;
$max+=3;
my $i_dp_version = sprintf(q{%-15s}, qq{"$dp_version"});

print( qq{ $i_dp_version) disk_size_gb="$max" ;; # $stats # $config}, $/ );
EOF
for f in $(grep -l 'Customization script suc' /tmp/custom-image-*/logs/workflow.log|sed -e 's/workflow.log/startup-script.log/')
do
grep -A20 'Filesystem.*Avail' $f | perl /tmp/genline.pl $f
grep 'Customization script' /tmp/custom-image-*/logs/workflow.log
# grep maximum-disk-used /tmp/custom-image-*/logs/startup-script.log
for workflow_log in $(grep -l "Customization script" /tmp/custom-image-*/logs/workflow.log) ; do
startup_log=$(echo "${workflow_log}" | sed -e 's/workflow.log/startup-script.log/')
grep -A5 'Filesystem.*1K-blocks' "${startup_log}" | perl examples/secure-boot/genline.pl "${workflow_log}"
done
}

# sleep 8m ; grep 'Customization script' /tmp/custom-image-*/logs/workflow.log
# grep maximum-disk-used /tmp/custom-image-*/logs/startup-script.log

revoke_bindings
1 change: 0 additions & 1 deletion examples/secure-boot/create-key-pair.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ function create_key () {
fi

if [[ -f "${PRIVATE_KEY}" ]]; then
echo "key already exists. Skipping generation." >&2
modulus_md5sum="$(cat tls/modulus-md5sum.txt)"
return
fi
Expand Down
Loading