diff --git a/README.md b/README.md index ed306f30..8dc7e497 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Ubuntu 16.04 | deb | 3.5 or 2.7 | systemd Ubuntu 18.04 | deb | 3.6 | systemd Ubuntu 19.04 | deb | 3.7 | systemd Debian 9 | deb | 3.5 or 2.7 | systemd +Debian 10 | deb | 3.7 | systemd We build the following packages for the Linux guest environment. @@ -75,6 +76,12 @@ packaging directory for more details. #### Version Updates +Versions are described as 1:YYYYMMDD.NN-gN, meaning epoch 1 to denote from a +distro maintained package which will be 0, a date string formatted as year, +month, day, an incrementing minor release, and gN representing the Google +package release. Debian, Ubuntu, and SUSE maintain distro packages which may be +out of date, have different versioning, or naming. + The method for making version updates differs by package. * All packages need the `VERSION` variable set in the `setup_{deb,rpm}.sh` build @@ -102,11 +109,13 @@ Add the public repo key to your system: curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - ``` -Add a source list file `/etc/apt/sources.list.d/google-cloud.list`: +Add a source list file `/etc/apt/sources.list.d/google-cloud.list` and change +`DIST` to either `stretch` for Debian 9 or `buster` for Debian 10: ``` +DIST=stretch sudo tee /etc/apt/sources.list.d/google-cloud.list << EOM -deb http://packages.cloud.google.com/apt google-compute-engine-stretch-stable main -deb http://packages.cloud.google.com/apt google-cloud-packages-archive-keyring-stretch main +deb http://packages.cloud.google.com/apt google-compute-engine-${DIST}-stable main +deb http://packages.cloud.google.com/apt google-cloud-packages-archive-keyring-${DIST} main EOM ``` @@ -119,8 +128,8 @@ You are then able to install any of the packages from this repo. **For RedHat based distributions, run the following commands as root:** -Add the yum repo to a repo file `/etc/yum.repos.d/google-cloud.repo` for either -EL6 or EL7. Change `DIST` to either 6 or 7 respectively: +Add the yum repo to a repo file `/etc/yum.repos.d/google-cloud.repo` for EL6, +EL7, or EL8. Change `DIST` to either 6, 7, or 8 respectively: ``` DIST=7 tee /etc/yum.repos.d/google-cloud.repo << EOM diff --git a/daisy_workflows/build_deb_packages.sh b/daisy_workflows/build_deb_packages.sh index 0eb14080..d8770645 100755 --- a/daisy_workflows/build_deb_packages.sh +++ b/daisy_workflows/build_deb_packages.sh @@ -56,7 +56,13 @@ done # Copy the deb and dsc files to the output. cd "${workdir}/output" -gsutil cp *.dsc *.deb ${OUTPUT} +# For Debian 10 right now, only copy OS Login package. +if [[ "$(cut -d. -f1 Mon, 08 Jul 2019 10:20:47 -0700 + gce-disk-expand (2.0.0-1) stable; urgency=low * Initial debian package import. diff --git a/packages/gce-disk-expand/packaging/debian/control b/packages/gce-disk-expand/packaging/debian/control index 1b18aef8..ac38b5d2 100644 --- a/packages/gce-disk-expand/packaging/debian/control +++ b/packages/gce-disk-expand/packaging/debian/control @@ -2,8 +2,7 @@ Source: gce-disk-expand Section: admin Priority: optional Maintainer: Google Cloud Team -Build-Depends: debhelper (>= 9), - dh-systemd (>= 1.5), +Build-Depends: debhelper (>= 10) Standards-Version: 3.9.8 Homepage: https://github.com/GoogleCloudPlatform/compute-image-packages diff --git a/packages/gce-disk-expand/packaging/debian/install b/packages/gce-disk-expand/packaging/debian/install index e621f63e..976acf12 100644 --- a/packages/gce-disk-expand/packaging/debian/install +++ b/packages/gce-disk-expand/packaging/debian/install @@ -1 +1,2 @@ src/usr/share/initramfs-tools usr/share/ +src/expandfs-lib.sh usr/share/initramfs-tools/scripts/ diff --git a/packages/gce-disk-expand/packaging/gce-disk-expand.spec b/packages/gce-disk-expand/packaging/gce-disk-expand.spec index a1fb6ddb..409d8981 100644 --- a/packages/gce-disk-expand/packaging/gce-disk-expand.spec +++ b/packages/gce-disk-expand/packaging/gce-disk-expand.spec @@ -13,8 +13,9 @@ # limitations under the License. Name: gce-disk-expand Summary: Google Compute Engine root disk expansion module -Version: 2.0.0 -Release: 1 +Epoch: 1 +Version: %{_version} +Release: g1 License: Apache Software License Group: System Environment/Base URL: https://github.com/GoogleCloudPlatform/compute-image-packages diff --git a/packages/gce-disk-expand/packaging/setup_deb.sh b/packages/gce-disk-expand/packaging/setup_deb.sh index ca5be922..cc817114 100755 --- a/packages/gce-disk-expand/packaging/setup_deb.sh +++ b/packages/gce-disk-expand/packaging/setup_deb.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="gce-disk-expand" -VERSION="2.0.0" +VERSION="20190708.00" working_dir=${PWD} if [[ $(basename "$working_dir") != $NAME ]]; then diff --git a/packages/gce-disk-expand/packaging/setup_rpm.sh b/packages/gce-disk-expand/packaging/setup_rpm.sh index 106aa6f6..1bc6c389 100755 --- a/packages/gce-disk-expand/packaging/setup_rpm.sh +++ b/packages/gce-disk-expand/packaging/setup_rpm.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="gce-disk-expand" -VERSION="2.0.0" +VERSION="20190708.00" rpm_working_dir=/tmp/rpmpackage/ working_dir=${PWD} @@ -30,6 +30,8 @@ rm -rf ${rpm_working_dir} mkdir -p ${rpm_working_dir}/{SOURCES,SPECS} cp packaging/${NAME}.spec ${rpm_working_dir}/SPECS/ -tar czvf ${rpm_working_dir}/SOURCES/${NAME}_${VERSION}.orig.tar.gz --exclude .git --exclude packaging --transform "s/^\./${NAME}-${VERSION}/" . +tar czvf ${rpm_working_dir}/SOURCES/${NAME}_${VERSION}.orig.tar.gz \ + --exclude .git --exclude packaging --transform "s/^\./${NAME}-${VERSION}/" . -rpmbuild --define "_topdir ${rpm_working_dir}/" -ba ${rpm_working_dir}/SPECS/${NAME}.spec +rpmbuild --define "_topdir ${rpm_working_dir}/" --define "_version ${VERSION}" \ + -ba ${rpm_working_dir}/SPECS/${NAME}.spec diff --git a/packages/gce-disk-expand/src/expandfs-lib.sh b/packages/gce-disk-expand/src/expandfs-lib.sh index 92e9ed5b..69f67ed9 100755 --- a/packages/gce-disk-expand/src/expandfs-lib.sh +++ b/packages/gce-disk-expand/src/expandfs-lib.sh @@ -67,14 +67,15 @@ parted_fix_gpt() { local disk="$1" [ -z "$disk" ] && return - if parted -sm "$rootdisk" print 2>&1 | grep "fix the GPT"; then + if parted -sm "$disk" print 2>&1 | grep -q "fix the GPT"; then # Running parted prompts the user to fix this condition, but only does so in # the interactive exception handler. In order to pass input we must use the # hidden triple-dash flag and pass both print and Fix arguments. `print` # alone will not perform the fix, but `Fix` alone will fail the argument # parser. - parted -m ---pretend-input-tty "$rootdisk" print Fix - if parted -sm "$rootdisk" print 2>&1 | grep "fix the GPT"; then + parted -m ---pretend-input-tty "$disk" print Fix >/dev/null 2>&1 /dev/null 2>&1 &1 | grep -q "fix the GPT"; then echo "Failed to fix the GPT." return 1 fi @@ -110,13 +111,15 @@ parted_needresize() { return 1 fi - if ! echo -e "$out" | sed '$!d' | grep -q "^${partnum}:"; then + udevadm settle + + if ! printf "$out" | sed '$!d' | grep -q "^${partnum}:"; then echo "Root partition is not final partition on disk. Not resizing." return 1 fi - disksize=$(echo -e "$out" | grep "^${disk}" | cut -d: -f2) - partend=$(echo -e "$out" | sed '$!d' | cut -d: -f4) + disksize=$(printf "$out" | grep "^${disk}" | cut -d: -f2) + partend=$(printf "$out" | sed '$!d' | cut -d: -f4) [ -n "$disksize" -a -n "$partend" ] || return 1 disksize=${disksize%%B} @@ -138,52 +141,3 @@ parted_resizepart() { fi udevadm settle } - -# Resizes partition by deleting and recreating with end position. -# This is a subshell function to safeguard against modifications of IFS. -parted_resize_mkpart() ( - local disk="$1" partnum="$2" - [ -z "$disk" -o -z "$partnum" ] && return - - local partnum="" partbegin="" partend="" partsize="" - local fstype="" partname="" flags="" temp="" - - # $ parted -sm /dev/sda unit b print - # BYT; - # /dev/sda:18253611008B:scsi:512:4096:msdos:Google PersistentDisk:; - # 1:2097152B:18252611583B:18250514432B:ext4::boot; - # - if ! out=$(parted -sm "$disk" unit b print 2>&1); then - echo "Unable to get partition info." - return 1 - fi - - temp=/tmp/my_temp - echo -e "$out" | sed '$!d' > $temp - IFS=: read partnum partbegin partend partsize fstype partname flags < $temp - rm $temp - - if ! out=$(parted -sm "$disk" rm $partnum 2>&1); then - echo "Failed to delete partition: ${out}" - return 1 - fi - - if ! out=$(parted -sm "$disk" -- mkpart pri $fstype $partbegin -1 2>&1); then - echo "Failed to recreate original partition: ${out}" - echo "Trying to create with original parameters." - if ! out=$(parted -sm "$disk" mkpart pri $fstype $partbegin $partend 2>&1); then - echo "Failed to recreate original partition: ${out}" - return 1 - fi - fi - - flags=${flags%%;} - IFS=, - for flag in $flags; do - if ! out=$(parted -sm "$disk" set $partnum $flag on 2>&1); then - echo "Failed to set \"$flag\" on ${disk} part ${partnum}: ${out}" - return 1 - fi - done - udevadm settle -) diff --git a/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/expand_rootfs.sh b/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/expand_rootfs.sh index cc831b3f..3d12b353 100755 --- a/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/expand_rootfs.sh +++ b/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/expand_rootfs.sh @@ -15,17 +15,22 @@ # Contains dracut-specific logic for detecting disk, then calls appropriate # library functions. + +kmsg() { + echo "expand_rootfs: $@" > /dev/kmsg +} + main() { local disk="" partnum="" fs_type="" rootdev="" # Remove 'block:' prefix and find the root device. if ! rootdev=$(readlink -f "${root#block:}") || [ -z "${rootdev}" ]; then - echo "Unable to find root device." + kmsg "Unable to find root device." return fi if ! out=$(split_partition "$rootdev"); then - echo "Failed to detect disk and partition info: ${out}" + kmsg "Failed to detect disk and partition info: ${out}" return fi @@ -33,32 +38,28 @@ main() { partnum=${out#*:} if ! parted_needresize "$disk" "$partnum"; then - echo "Disk does not need resizing." + kmsg "Disk ${rootdev} doesn't need resizing" return fi - echo "Resizing disk ${rootdev}" - - if ! out=$(parted_fix_gpt); then - echo "$out" + if ! parted --help | grep -q 'resizepart'; then + kmsg "No 'resizepart' command in this parted" return fi - if parted --help | grep -q 'resizepart'; then + kmsg "Resizing disk ${rootdev}" + + if ! out=$(parted_resizepart "$disk" "$partnum"); then + # Try fixing the GPT and try resizing again. + parted_fix_gpt "$disk" if ! out=$(parted_resizepart "$disk" "$partnum"); then - echo "Failed to resize partition: ${out}" - return - fi - else - echo "No 'resizepart' command in this parted, trying rm&&mkpart." - if ! out=$(parted_resize_mkpart "$disk" "$partnum"); then - echo "Failed to resize partition: ${out}" + kmsg "Failed to resize partition: ${out}" return fi fi if ! out=$(resize_filesystem "$rootdev"); then - echo "Failed to resize filesystem: ${out}" + kmsg "Failed to resize filesystem: ${out}" return fi } diff --git a/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/xfs_growfs.sh b/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/xfs_growfs.sh index 0e771858..f4013f61 100755 --- a/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/xfs_growfs.sh +++ b/packages/gce-disk-expand/src/usr/share/dracut/modules.d/50expand_rootfs/xfs_growfs.sh @@ -13,29 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +kmsg() { + echo "xfs_growfs: $@" >/dev/kmsg +} + main() { if [ ! -e /tmp/xfs_resize ]; then return fi - if ! command -v xfs_growfs >/dev/null; then - echo "XFS resize requested, but xfs_growfs not installed." + if ! type xfs_growfs >/dev/null; then + kmsg "XFS resize requested, but xfs_growfs not installed." + return + fi + + kmsg "Mounting filesystem rw for resize." + if ! $(mount -o rw,remount /sysroot); then + kmsg "Remount failed." return fi - if xfs_growfs -d -n /sysroot; then - echo "Mounting filesystem rw." - if ! $(mount -o rw,remount /sysroot); then - echo "Remount failed." - return - fi - echo "Resizing XFS filesystem" - if ! out=$(xfs_growfs -d /sysroot); then - echo "Failed to resize: ${out}" - mount -o ro,remount /sysroot - return - fi - mount -o ro,remount /sysroot + + kmsg "Resizing XFS filesystem" + if ! out=$(xfs_growfs -d /sysroot); then + kmsg "Failed to resize: ${out}" fi + mount -o ro,remount /sysroot } main diff --git a/packages/gce-disk-expand/src/usr/share/initramfs-tools/hooks/expand-rootfs b/packages/gce-disk-expand/src/usr/share/initramfs-tools/hooks/expand-rootfs index b1f38b76..dae461aa 100755 --- a/packages/gce-disk-expand/src/usr/share/initramfs-tools/hooks/expand-rootfs +++ b/packages/gce-disk-expand/src/usr/share/initramfs-tools/hooks/expand-rootfs @@ -28,5 +28,11 @@ esac . /usr/share/initramfs-tools/hook-functions -parted=$(command -v "parted") -copy_exec "$parted" /bin +copy_exec /sbin/parted /bin +copy_exec /bin/grep /bin +copy_exec /bin/sed /bin +copy_exec /usr/bin/cut /bin +copy_exec /sbin/e2fsck /bin +copy_exec /sbin/blkid /bin +copy_exec /sbin/resize2fs /bin +copy_exec /bin/udevadm /bin diff --git a/packages/gce-disk-expand/src/usr/share/initramfs-tools/scripts/local-premount/expand_rootfs b/packages/gce-disk-expand/src/usr/share/initramfs-tools/scripts/local-premount/expand_rootfs index c95f50df..a380bc24 100755 --- a/packages/gce-disk-expand/src/usr/share/initramfs-tools/scripts/local-premount/expand_rootfs +++ b/packages/gce-disk-expand/src/usr/share/initramfs-tools/scripts/local-premount/expand_rootfs @@ -26,7 +26,7 @@ case $1 in esac . /scripts/functions -. /local-premount/expandfs-lib.sh +. /scripts/expandfs-lib.sh if ! rootdev=$(resolve_device "${ROOT}"); then log_failure_message "Failed to resolve root device for \"${ROOT}\"" @@ -36,34 +36,32 @@ if ! fs_type=$(get_fstype "${rootdev}"); then log_failure_message "Failed to determine fstype for \"${rootdev}\"" fi -if ! out=$(get_partition "$rootdev"); then - echo "Failed to detect disk and partition info: ${out}" +if ! out=$(split_partition "$rootdev"); then + log_failure_message "Failed to detect disk and partition info: ${out}" exit 0 fi disk=${out%:*} partnum=${out#*:} -if ! parted_needresize "$rootdisk" "$partnum"; then +if ! parted_needresize "${disk}" "${partnum}"; then + log_success_message "Disk ${rootdev} doesn't need resizing." exit 0 fi -echo "Resizing disk ${rootdev}" +if ! out=$(parted_fix_gpt "$disk"); then + log_failure_message "Failed to fix GPT: ${out}" + exit 0 +fi -if ! out=$(parted_fix_gpt); then - echo "$out" +echo "Resizing partition on ${rootdev}" +if ! out=$(parted_resizepart "${disk}" "${partnum}"); then + log_failure_message "Failed to resize partition: ${out}" exit 0 fi -if parted --help | grep -q 'resizepart'; then - if ! out=$(parted_resizepart "$disk" "$partnum"); then - echo "Failed to resize partition: ${out}" - exit 0 - fi -else - echo "No 'resizepart' command in this parted, trying rm&&mkpart" - if ! out=$(parted_resize_mkpart "$disk" "$partnum"); then - echo "Failed to resize partition: ${out}" - exit 0 - fi +echo "Resizing filesystem on ${rootdev}" +if ! out=$(resize_filesystem "${rootdev}"); then + log_failure_message "Failed to resize filesystem: ${out}" + exit 0 fi diff --git a/packages/google-compute-engine-oslogin/Makefile b/packages/google-compute-engine-oslogin/Makefile index 1d7295e9..b22c16ff 100644 --- a/packages/google-compute-engine-oslogin/Makefile +++ b/packages/google-compute-engine-oslogin/Makefile @@ -1,158 +1,19 @@ -SHELL = /bin/sh +all install : + $(MAKE) -C src $@ -BASENAME = oslogin -NAME = google-compute-engine-$(BASENAME) -MAJOR = 1 -MINOR = 5 -REVISION = 3 +tests : + $(MAKE) -C test $@ -LIBNSS_CACHE_OSLOGIN = libnss_cache_$(BASENAME) -LIBNSS_CACHE_OSLOGIN_NAME = libnss_cache_$(NAME)-$(MAJOR).$(MINOR).$(REVISION).so -LIBNSS_CACHE_OSLOGIN_SONAME = $(LIBNSS_CACHE_OSLOGIN).so.2 -NSS_LIBRARY_NAME = libnss_$(NAME)-$(MAJOR).$(MINOR).$(REVISION).so -NSS_LIBRARY_SONAME = libnss_$(BASENAME).so.2 -NSS_INSTALL_PATH = /lib -PAM_INSTALL_PATH = /lib/security -AUTHKEYS_INSTALL_PATH = /usr/bin +clean : + $(MAKE) -C src clean + $(MAKE) -C test clean -JSON_INCLUDE_PATH = /usr/include/json-c -INCLUDE_FLAGS = -I$(JSON_INCLUDE_PATH) +prowbuild : debian_deps all -CXX ?= g++ -CXXFLAGS += -fPIC# -Wall -CC ?= gcc -PAMFLAGS = $(LDFLAGS) $(INCLUDE_FLAGS) -shared -NSSFLAGS = $(LDFLAGS) $(INCLUDE_FLAGS) -shared -Wl,-soname,$(NSS_LIBRARY_SONAME) -LIBNSSFLAGS = $(LDFLAGS) -Wall -Wstrict-prototypes -fPIC -g -LIBNSS_SO_FLAGS = $(LIBNSSFLAGS) -shared -Wl,-soname,$(LIBNSS_CACHE_OSLOGIN_SONAME) +prowtest : debian_deps tests -# UTILS -UTILS_DIR = utils -UTILS_SRC = $(UTILS_DIR)/$(BASENAME)_utils.cc -UTILS = $(UTILS_DIR)/$(BASENAME)_utils.o - -# AUTHORIZED KEYS -AUTHKEYS_DIR = authorized_keys -AUTHKEYS_SRC = $(AUTHKEYS_DIR)/authorized_keys.cc -AUTHKEYS_BIN = google_authorized_keys - -# NSS -NSS = nss_$(BASENAME) -NSS_DIR = nss_module -NSS_SRC = $(NSS_DIR)/$(NSS).cc - -# NSS CACHE -NSS_CACHE = nss_cache -NSS_CACHE_DIR = nss_cache -NSS_CACHE_BIN = google_$(BASENAME)_nss_cache -NSS_CACHE_SRC = $(NSS_CACHE_DIR)/$(NSS_CACHE).cc - -# LIBNSS OSLOGIN CACHE -LIBNSS_CACHE_OSLOGIN_DIR = $(LIBNSS_CACHE_OSLOGIN) -LIBNSS_CACHE = nss_cache_$(BASENAME) -LIBNSS_CACHE_SRC = $(LIBNSS_CACHE_OSLOGIN_DIR)/$(LIBNSS_CACHE).c -LIBNSS_CACHE_OBJ = $(LIBNSS_CACHE_OSLOGIN_DIR)/$(LIBNSS_CACHE).o -LIBNSS_COMPAT = compat/getpwent_r -LIBNSS_COMPAT_SRC = $(LIBNSS_CACHE_OSLOGIN_DIR)/$(LIBNSS_COMPAT).c -LIBNSS_COMPAT_OBJ = $(LIBNSS_CACHE_OSLOGIN_DIR)/$(LIBNSS_COMPAT).o - -# PAM -PAM = pam_$(BASENAME) -PAM_DIR = pam_module -PAM_ADMIN = $(PAM)_admin -PAM_ADMIN_SRC = $(PAM_DIR)/$(PAM_ADMIN).cc -PAM_ADMIN_OBJ = $(PAM_DIR)/$(PAM_ADMIN).o -PAM_ADMIN_MOD = $(PAM_ADMIN).so -PAM_LOGIN = $(PAM)_login -PAM_LOGIN_SRC = $(PAM_DIR)/$(PAM_LOGIN).cc -PAM_LOGIN_OBJ = $(PAM_DIR)/$(PAM_LOGIN).o -PAM_LOGIN_MOD = $(PAM_LOGIN).so - -# HELPER SCRIPTS -BIN_DIR = bin -OSLOGIN_HELPER = $(BIN_DIR)/google_oslogin_control -BIN_INSTALL_PATH = /usr/bin - -# SELINUX POLICY -INSTALL_SELINUX = -POLICY_DIR = policy -SELINUX_INSTALL_NAME = oslogin.pp -SELINUX_MODULE = $(POLICY_DIR)/$(SELINUX_INSTALL_NAME) -SELINUX_INSTALL_PATH = /usr/share/selinux/packages - -LIBS = -lcurl -ljson-c -PAM_LIBS = -lpam $(LIBS) - -ifdef INSTALL_SELINUX -all: $(NSS) $(NSS_CACHE_BIN) $(LIBNSS_CACHE_OSLOGIN_NAME) $(PAM) $(AUTHKEYS_BIN) -else -all: $(NSS) $(NSS_CACHE_BIN) $(LIBNSS_CACHE_OSLOGIN_NAME) $(PAM) $(AUTHKEYS_BIN) -endif - -$(NSS): $(NSS_LIBRARY_SOURCE) $(UTILS) - $(CXX) $(CXXFLAGS) $(NSSFLAGS) -o $(NSS_LIBRARY_NAME) \ - $(NSS_SRC) $(UTILS) $(LIBS) - -$(NSS_CACHE_BIN): $(NSS_CACHE_SRC) $(UTILS_SRC) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(NSS_CACHE_BIN) $(NSS_CACHE_SRC) $(UTILS_SRC) $(LIBS) - -$(LIBNSS_CACHE_OSLOGIN_NAME): $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) - $(CXX) $(LIBNSS_SO_FLAGS) -o $(LIBNSS_CACHE_OSLOGIN_NAME) $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) - -$(LIBNSS_CACHE_OBJ): $(LIBNSS_CACHE_SRC) - $(CC) $(LIBNSSFLAGS) -c -o $(LIBNSS_CACHE_OBJ) $(LIBNSS_CACHE_SRC) - -$(LIBNSS_COMPAT_OBJ): $(LIBNSS_COMPAT_SRC) - $(CC) $(LIBNSSFLAGS) -c -o $(LIBNSS_COMPAT_OBJ) $(LIBNSS_COMPAT_SRC) - -$(PAM): $(PAM_ADMIN_MOD) $(PAM_LOGIN_MOD) - -$(PAM_LOGIN_MOD): $(PAM_LOGIN_OBJ) $(UTILS) - $(CXX) $(PAMFLAGS) -o $(PAM_LOGIN_MOD) $(PAM_LOGIN_OBJ) $(UTILS) $(PAM_LIBS) - -$(PAM_ADMIN_MOD): $(PAM_ADMIN_OBJ) $(UTILS) - $(CXX) $(PAMFLAGS) -o $(PAM_ADMIN_MOD) $(PAM_ADMIN_OBJ) $(UTILS) $(PAM_LIBS) - -$(PAM_LOGIN_OBJ): $(PAM_LOGIN_SRC) - $(CXX) $(CXXFLAGS) -c $(PAM_LOGIN_SRC) -o $(PAM_LOGIN_OBJ) - -$(PAM_ADMIN_OBJ): $(PAM_ADMIN_SRC) - $(CXX) $(CXXFLAGS) -c $(PAM_ADMIN_SRC) -o $(PAM_ADMIN_OBJ) - -$(AUTHKEYS_BIN): $(AUTHKEYS_SRC) $(UTILS_SRC) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(AUTHKEYS_BIN) $(AUTHKEYS_SRC) $(UTILS_SRC) $(LIBS) - -$(UTILS): $(UTILS_SRC) - $(CXX) $(CXXFLAGS) $(INCLUDE_FLAGS) -c $(UTILS_SRC) -o $(UTILS) - -install: $(NSS_LIBRARY_NAME) $(LIBNSS_OSLOGIN_CACHE_NAME) $(PAM_ADMIN_MOD) $(PAM_LOGIN_MOD) $(AUTHKEYS_BIN) $(NSS_CACHE_BIN) - mkdir -p $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH) - mkdir -p $(DESTDIR)$(PREFIX)/$(PAM_INSTALL_PATH) - mkdir -p $(DESTDIR)$(PREFIX)/$(AUTHKEYS_INSTALL_PATH) - mkdir -p $(DESTDIR)$(PREFIX)/$(BIN_INSTALL_PATH) - install -m 0644 $(LIBNSS_CACHE_OSLOGIN_NAME) $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH) - install -m 0644 $(NSS_LIBRARY_NAME) $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH) - install -m 0644 $(PAM_ADMIN_MOD) $(PAM_LOGIN_MOD) $(DESTDIR)$(PREFIX)/$(PAM_INSTALL_PATH) - install -m 0755 $(AUTHKEYS_BIN) $(DESTDIR)$(PREFIX)/$(AUTHKEYS_INSTALL_PATH) - install -m 0755 $(OSLOGIN_HELPER) $(DESTDIR)$(PREFIX)/$(BIN_INSTALL_PATH) - install -m 0755 $(NSS_CACHE_BIN) $(DESTDIR)$(PREFIX)/$(BIN_INSTALL_PATH) -ifdef MAKE_LINKS - ln -sf $(LIBNSS_CACHE_OSLOGIN_NAME) $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH)/$(LIBNSS_CACHE_OSLOGIN_SONAME) - ln -sf $(NSS_LIBRARY_NAME) $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH)/$(NSS_LIBRARY_SONAME) -endif -ifdef INSTALL_SELINUX - mkdir -p $(DESTDIR)$(PREFIX)/$(SELINUX_INSTALL_PATH) - install -T -m 0644 $(SELINUX_MODULE) $(DESTDIR)$(PREFIX)/$(SELINUX_INSTALL_PATH)/$(SELINUX_INSTALL_NAME) -endif - -uninstall: - rm -f $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH)/$(LIBNSS_CACHE_OSLOGIN_NAME) - rm -f $(DESTDIR)$(PREFIX)/$(NSS_INSTALL_PATH)/$(NSS_LIBRARY_NAME) - rm -f $(DESTDIR)$(PREFIX)/$(PAM_INSTALL_PATH)/$(PAM_ADMIN_MOD) - rm -f $(DESTDIR)$(PREFIX)/$(PAM_INSTALL_PATH)/$(PAM_LOGIN_MOD) - rm -f $(DESTDIR)$(PREFIX)/$(AUTHKEYS_INSTALL_PATH)/$(AUTHKEYS_BIN) - rm -f $(DESTDIR)$(PREFIX)/$(BIN_INSTALL_PATH)/$(OSLOGIN_HELPER) - rm -f $(DESTDIR)$(PREFIX)/$(BIN_INSTALL_PATH)/$(NSS_CACHE_BIN) -clean: - rm -f $(UTILS) $(NSS_LIBRARY_NAME) $(LIBNSS_CACHE_OSLOGIN_NAME) $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) $(PAM_ADMIN_OBJ) $(PAM_ADMIN_MOD) $(PAM_LOGIN_OBJ) $(PAM_LOGIN_MOD) $(AUTHKEYS_BIN) $(NSS_CACHE_BIN) +debian_deps : + sudo apt-get -y install g++ libcurl4-openssl-dev libjson-c-dev libpam-dev \ + googletest && touch $@ +.PHONY : all clean install prowbuild prowtest diff --git a/packages/google-compute-engine-oslogin/README.md b/packages/google-compute-engine-oslogin/README.md index 6f967c55..4acc31e8 100644 --- a/packages/google-compute-engine-oslogin/README.md +++ b/packages/google-compute-engine-oslogin/README.md @@ -1,76 +1,73 @@ ## OS Login Guest Environment for Google Compute Engine -This package enables Google Cloud OS Login features on Google Compute Engine -instances. +This repository contains the system components responsible for providing Google +Cloud OS Login features on Google Compute Engine instances. **Table of Contents** * [Overview](#overview) * [Components](#components) * [Authorized Keys Command](#authorized-keys-command) - * [NSS Module](#nss-module) - * [PAM Module](#pam-module) - * [Utils](#utils) -* [Utility Directories](#utility-directories) - * [bin](#bin) - * [packaging](#packaging) - * [policy](#policy) + * [NSS Modules](#nss-modules) + * [PAM Modules](#pam-modules) +* [Utilities](#Utilities) + * [Control Script](#control-script) + * [SELinux Policy](#selinux-policy) * [Source Packages](#source-packages) * [DEB](#deb) * [RPM](#rpm) -* [Version Updates](#version-updates) ## Overview -The OS Login package has the following components: +The OS Login Guest Environment consists of the following main components: -* **Authorized Keys Command** to fetch SSH keys from the user's OS Login - profile and make them available to sshd. -* **NSS Module** provides support for making OS Login user and group +* **Authorized Keys Command** which provides SSH keys from the user's OS Login + profile to sshd for authenticating users at login. +* **NSS Modules** which provide support for making OS Login user and group information available to the system, using NSS (Name Service Switch) functionality. -* **PAM Module** provides authorization and authentication support allowing - the system to use data stored in Google Cloud IAM permissions to control - both, the ability to log into an instance, and to perform operations as root - (sudo). -* **Utils** provides common code to support the components listed above. - -In addition to the main components, there are also utilities for packaging and -installing these components: - -* **bin** contains a shell script for activating/deactivating the package - components. -* **packaging** contains files used to generate `.deb` and `.rpm` packages for - the OS Login components. -* **policy** contains SELinux "type enforcement" files for configuring SELinux - on CentOS/RHEL systems. +* **PAM Modules** which provide authorization (and authentication if + two-factor support is enabled) support allowing the system to use Google + Cloud IAM permissions to control the ability to log into an instance or to + perform operations as root (via `sudo`). + +In addition to the main components, there are also the following utilities: + +* **google_oslogin_control** is a shell script for activating/deactivating the + OS Login components. +* **google_oslogin_nss_cache** is a utility for updating the local user and + group cache. +* **selinux** contains SELinux policy definition files and a compiled policy + package for configuring SELinux to support OS Login. + +The **packaging** directory also contains files used to generate `.deb` and +`.rpm` packages for the OS Login components. ## Components #### Authorized Keys Command The `google_authorized_keys` binary is designed to be used with the sshd -[AuthorizedKeysCommand](https://linux.die.net/man/5/sshd_config) option in -`sshd_config`. It does the following: +`AuthorizedKeysCommand` option in [sshd_config(5)](https://linux.die.net/man/5/sshd_config). +It does the following: -* Reads the user's profile information from the metadata server. +* Reads the user's profile information from the metadata server: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/users?username= ``` -* Checks to make sure that the user is authorized to log in. +* Checks to make sure that the user is authorized to log in: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/authorize?email=&policy=login ``` * If the check is successful, returns the SSH keys associated with the user - for use by sshd. + for use by sshd. Otherwise, exits with an error code. -#### NSS Module +#### NSS Modules -The `nss_oslogin` module is built and installed in the appropriate `lib` -directory as a shared object with the name `libnss_oslogin.so.2`. The module is -then activated by an `oslogin` entry in `/etc/nsswitch.conf`. The NSS module -supports looking up `passwd` entries from the metadata server via -`getent passwd`. +`libnss_oslogin.so` and `libnss_cache_oslogin.so` are NSS service modules which +make OS Login users and groups available for use on the local system. The module +is activated by adding `oslogin` and `cache_oslogin` entries for services in +[nsswitch.conf(5)](https://linux.die.net/man/5/nsswitch.conf). * To return a list of all users, the NSS module queries: ``` @@ -78,47 +75,59 @@ supports looking up `passwd` entries from the metadata server via ``` * To look up a user by username, the NSS module queries: ``` - http://metadata.google.internal/computeMetadata/v1/oslogin/users?username= ``` * To look up a user by UID, the NSS module queries: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/users?uid= ``` -#### PAM Module +#### PAM Modules -The `pam_module` directory contains two modules used by Linux PAM (Pluggable -Authentication Modules). +`pam_oslogin_login.so` is a PAM module which determines whether a given user is +allowed to SSH into an instance. -The first module, `pam_oslogin_login.so`, determines whether a given user is -allowed to SSH into an instance. It is activated by adding an -`account requisite` line to the PAM sshd config file and does the following: +It is activated by adding an entry for the account group to the PAM service +config for sshd as: + ``` + account requisite pam_oslogin_login.so + ``` -* Retrieves the user's profile information from the metadata server. +This module: + +* Retrieves the user's profile information from the metadata server: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/users?username= ``` -* If the user has OS Login profile information (as opposed to a local user - account), confirms whether the user has permissions to SSH into the - instance. +* If the user does not have OS Login profile information it is passed on to + the system authentication modules to be processed as a local user. +* Otherwise, the module confirms whether the user has permissions to SSH into + the instance: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/authorize?email=&policy=login ``` -* If the user is a local user account or is authorized, PAM returns a success - message and SSH can proceed. Otherwise, PAM returns a denied message and the - SSH check will fail. +* If the user is authorized, PAM returns a success message and SSH can + proceed. Otherwise, PAM returns a denied message and the SSH check will + fail. + +`pam_oslogin_admin.so` is a PAM module which determines whether a given user +should have admin (sudo) permissions on the instance. -The second module, `pam_oslogin_admin.so`, determines whether a given user -should have admin (sudo) permissions on the instance. It is activated by adding -an `account optional` line to the PAM sshd config file and does the following: +It is activated by adding an entry for the `account` group to the PAM service +config for sshd config as: + ``` + account optional pam_oslogin_admin.so + ``` + +This module: * Retrieves the user's profile information from the metadata server. ``` http://metadata.google.internal/computeMetadata/v1/oslogin/users?username= ``` -* If the user is a local user account, the module exits with success. -* If the user is an OS Login user, the module perform an authorization check - to determine if the user has admin permissions. +* If the user is not an OS Login user (a local user account), the module + returns success. +* Otherwise, the module determines if the user has admin permissions: ``` http://metadata.google.internal/computeMetadata/v1/oslogin/authorize?email=&policy=adminLogin ``` @@ -127,49 +136,40 @@ an `account optional` line to the PAM sshd config file and does the following: * If the authorization check fails for admin permissions, the file is removed from `/var/google-sudoers.d/` if it exists. -#### Utils - -`oslogin_utils` contains common functions for making HTTP calls, -interacting with the metadata server, and for parsing JSON objects. +## Utilities -## Utility Directories +#### Control Script -#### bin - -The `bin` directory contains a shell script called `google_oslogin_control` that -activates or deactivates the OS Login features. It is called in the pre and post -install scripts in the `.deb` and `.rpm` packages. The control file performs the -following tasks: +The `google_oslogin_control` shell script activates or deactivates the OS Login +features. It is invoked by the google accounts daemon. The control file performs +the following tasks: * Adds (or removes) AuthorizedKeysCommand and AuthorizedKeysCommandUser lines to (from) `sshd_config` and restarts sshd. -* Adds (or removes) `oslogin` to (from) `nsswitch.conf`. +* Adds (or removes) `oslogin` and `cache_oslogin` to (from) `nsswitch.conf`. * Adds (or removes) the `account` entries to (from) the PAM sshd config. Also adds (or removes) the `pam_mkhomedir.so` module to automatically create the home directory for an OS Login user. * Creates (or deletes) the `/var/google-sudoers.d/` directory, and a file called `google-oslogin` in `/etc/sudoers.d/` that includes the directory. -#### packaging - -The `packaging` directory contains files for creating `.deb` and `.rpm` -packages. See [Source Packages](#source-packages) for details. +#### SELinux Policy -#### policy - -The `policy` directory contains `.te` (type enforcement) files used by SELinux -to give the OS Login features the appropriate SELinux permissions. These are -compiled using `checkmodule` and `semodule_package` to create an `oslogin.pp` -that is intstalled in the appropriate SELinux directory. +The `selinux` directory contains `.te` (type enforcement) and `.fc` (file +context) files used by SELinux to give the OS Login features the appropriate +SELinux permissions. These are compiled using `checkmodule` and +`semodule_package` to create an policy package `oslogin.pp`. ## Source Packages There is currently support for creating packages for the following distros: -* Debian 8 + * Debian 9 * CentOS/RHEL 6 * CentOS/RHEL 7 +Files for these packages are in the `packaging/` directory. + #### DEB _Note: the `packaging/setup_deb.sh` script performs these steps, but is not @@ -188,9 +188,7 @@ production quality._ directory, excluding the `packaging` directory (where M.M.R is the version number). 1. In a separate directory, extract the `.orig.tar.gz` file and copy the - appropriate `debian` directory into the top level. (e.g. When working on - Debian 8, copy the `debian8` directory to a directory named `debian` within - the code directory.) + `debian` directory into the top level. 1. To build the package, run the command ``` debuild -us -uc diff --git a/packages/google-compute-engine-oslogin/bin/google_oslogin_control b/packages/google-compute-engine-oslogin/google_oslogin_control similarity index 96% rename from packages/google-compute-engine-oslogin/bin/google_oslogin_control rename to packages/google-compute-engine-oslogin/google_oslogin_control index 024d4689..bc85a8ed 100644 --- a/packages/google-compute-engine-oslogin/bin/google_oslogin_control +++ b/packages/google-compute-engine-oslogin/google_oslogin_control @@ -201,6 +201,9 @@ modify_pam_config() ( # Get location of system-remote-login. insert=$($sed -rn "/^auth\s+include\s+system-remote-login/=" "$pam_sshd_config") # TODO: find su_insert point for arch linux. + elif is_freebsd; then + # Get location of the first auth occurrence + insert=$($sed -rn '/^auth/=' "$pam_sshd_config" | head -1) fi added_config="$added_comment" @@ -223,7 +226,7 @@ modify_pam_config() ( # Insert su blocker at top of `su:account` stack. if [ -n "$su_insert" ] && ! grep -qE "$pam_account_su" "$pam_su_config"; then added_su_config="${added_comment}\n${pam_account_su}" - sed -i"" "${su_insert}i ${added_su_config}" "$pam_su_config" + $sed -i"" "${su_insert}i ${added_su_config}" "$pam_su_config" fi # Append account modules at end of `sshd:account` stack. @@ -255,6 +258,12 @@ restore_pam_config() { } modify_group_conf() { + # In FreeBSD there is no pam_group config file similar to + # /etc/security/group.conf. + if is_freebsd; then + return + fi + local group_config="${1:-${group_config}}" local group_conf_entry="sshd;*;*;Al0000-2400;adm,dip,docker,lxd,plugdev,video" @@ -264,6 +273,12 @@ modify_group_conf() { } restore_group_conf() { + # In FreeBSD there is no pam_group config file similar to + # /etc/security/group.conf. + if is_freebsd; then + return + fi + local group_config="${1:-${group_config}}" $sed -i"" "/${added_comment}/{n;d}" "$group_config" @@ -286,7 +301,7 @@ restart_service() { # Use the service helper if it exists. if command -v service > /dev/null; then - if ! service "$service" status 2>&1 | grep -q unrecognized; then + if ! service "$service" status 2>&1 | grep -Eq "unrecognized|does not exist"; then service "$service" restart return $? else diff --git a/packages/google-compute-engine-oslogin/man/nss-cache-oslogin.8 b/packages/google-compute-engine-oslogin/man/nss-cache-oslogin.8 new file mode 100644 index 00000000..aaf4f8e8 --- /dev/null +++ b/packages/google-compute-engine-oslogin/man/nss-cache-oslogin.8 @@ -0,0 +1,48 @@ +'\" t +.TH "NSS\-CACHE\-OSLOGIN" "8" "2019-06-06" "Google Cloud" + +.\" IF GNU troff set Aq to sequence aq (Apostrophe quote) +.ie \n(.g .ds Aq \(aq +.\" ELSE set Aq to ' +.el .ds Aq ' + +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l + +.SH "NAME" +nss-cache-oslogin, libnss_cache_oslogin.so.2 \- UNIX implementation for OS Login Users and Groups with local caching\&. + +.SH "SYNOPSIS" +libnss_cache_oslogin\&.so\&.2 + +.SH "DESCRIPTION" +\fBnss\-cache\-oslogin\fR is a plug\-in module for the GNU Name Service Switch +(NSS) functionality of the GNU C Library (\fBglibc\fR)\&. +This module provides UNIX name resolution from a local cache of users and groups +configured in the \fIGoogle Cloud OS Login\fR system\&. +The "passwd" and "group" services are supported by this module\&. +.PP +To use the NSS module, add "cache_oslogin" to the appropriate service lines +in nsswitch\&.conf\&. +It is recommended to place "cache_oslogin" after any system provided modules +and before the "oslogin" module, as it is a pass-through cache. + +.SH "FILES" +.IP /etc/oslogin_passwd.cache +user cache file +.IP /etc/oslogin_group.cache +group cache file + +.SH "NOTES" +Documentation for the Google Cloud OS Login service is available online at +.UR "https://cloud.google.com/compute/docs/oslogin" +.UE + +.SH "SEE ALSO" +.BR nss-oslogin (5), +.BR nsswitch.conf (5), +.BR nss (5), +.BR getent (1), +.BR initgroups (3) diff --git a/packages/google-compute-engine-oslogin/man/nss-oslogin.8 b/packages/google-compute-engine-oslogin/man/nss-oslogin.8 new file mode 100644 index 00000000..3f4c7636 --- /dev/null +++ b/packages/google-compute-engine-oslogin/man/nss-oslogin.8 @@ -0,0 +1,41 @@ +'\" t +.TH "NSS\-OSLOGIN" "8" "2019-06-06" "Google Cloud" + +.\" IF GNU troff set Aq to sequence aq (Apostrophe quote) +.ie \n(.g .ds Aq \(aq +.\" ELSE set Aq to ' +.el .ds Aq ' + +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l + +.SH "NAME" +nss-oslogin, libnss_oslogin.so.2 \- UNIX implementation for OS Login Users and Groups\&. + +.SH "SYNOPSIS" +libnss_oslogin\&.so\&.2 + +.SH "DESCRIPTION" +\fBnss\-oslogin\fR is a plug\-in module for the GNU Name Service Switch (NSS) +functionality of the GNU C Library (\fBglibc\fR). +This module provides UNIX name resolution for users and groups configured +through the \fIGoogle Cloud OS Login\fR system\&. +The "passwd", "group", and "initgroups" services are supported by this module. +.PP +To use the NSS module, add "oslogin" to the appropriate service lines +in nsswitch\&.conf\&. +It is recommended to place "oslogin" as the last entry for each service. + +.SH "NOTES" +Documentation for the Google Cloud OS Login service is available online at +.UR "https://cloud.google.com/compute/docs/oslogin" +.UE + +.SH "SEE ALSO" +.BR nss-cache-oslogin (8), +.BR nsswitch.conf (5), +.BR nss (5), +.BR getent (1), +.BR initgroups (3) diff --git a/packages/google-compute-engine-oslogin/nss_module/nss_oslogin.cc b/packages/google-compute-engine-oslogin/nss_module/nss_oslogin.cc deleted file mode 100644 index 01ca25a0..00000000 --- a/packages/google-compute-engine-oslogin/nss_module/nss_oslogin.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2017 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../compat.h" -#include "../utils/oslogin_utils.h" - -using std::string; - -using oslogin_utils::BufferManager; -using oslogin_utils::HttpGet; -using oslogin_utils::MutexLock; -using oslogin_utils::NssCache; -using oslogin_utils::ParseJsonToPasswd; -using oslogin_utils::UrlEncode; -using oslogin_utils::kMetadataServerUrl; - -// Size of the NssCache. This also determines how many users will be requested -// per HTTP call. -static const uint64_t kNssCacheSize = 2048; - -// NssCache for storing passwd entries. -static NssCache nss_cache(kNssCacheSize); - -// Protects access to nss_cache. -static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; - -extern "C" { - -// Get a passwd entry by id. -int _nss_oslogin_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, - size_t buflen, int *errnop) { - BufferManager buffer_manager(buffer, buflen); - std::stringstream url; - url << kMetadataServerUrl << "users?uid=" << uid; - string response; - long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { - *errnop = ENOENT; - return NSS_STATUS_NOTFOUND; - } - if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { - if (*errnop == EINVAL) { - openlog("nss_oslogin", LOG_PID, LOG_USER); - syslog(LOG_ERR, "Received malformed response from server: %s", - response.c_str()); - closelog(); - } - return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - } - return NSS_STATUS_SUCCESS; -} - -// Get a passwd entry by name. -int _nss_oslogin_getpwnam_r(const char *name, struct passwd *result, - char *buffer, size_t buflen, int *errnop) { - BufferManager buffer_manager(buffer, buflen); - std::stringstream url; - url << kMetadataServerUrl << "users?username=" << UrlEncode(name); - string response; - long http_code = 0; - if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty()) { - *errnop = ENOENT; - return NSS_STATUS_NOTFOUND; - } - if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { - if (*errnop == EINVAL) { - openlog("nss_oslogin", LOG_PID, LOG_USER); - syslog(LOG_ERR, "Received malformed response from server: %s", - response.c_str()); - closelog(); - } - return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; - } - return NSS_STATUS_SUCCESS; -} - -// nss_getpwent_r() is intentionally left unimplemented. This functionality is -// now covered by the nss_cache binary and nss_cache module. - -nss_status _nss_oslogin_getpwent_r() { return NSS_STATUS_NOTFOUND; } -nss_status _nss_oslogin_endpwent() { return NSS_STATUS_SUCCESS; } -nss_status _nss_oslogin_setpwent() { return NSS_STATUS_SUCCESS; } - -NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r); -NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r); -NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r); -NSS_METHOD_PROTOTYPE(__nss_compat_setpwent); -NSS_METHOD_PROTOTYPE(__nss_compat_endpwent); - -DECLARE_NSS_METHOD_TABLE(methods, - { NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, - (void*)_nss_oslogin_getpwnam_r }, - { NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, - (void*)_nss_oslogin_getpwuid_r }, - { NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, - (void*)_nss_oslogin_getpwent_r }, - { NSDB_PASSWD, "endpwent", __nss_compat_endpwent, - (void*)_nss_oslogin_endpwent }, - { NSDB_PASSWD, "setpwent", __nss_compat_setpwent, - (void*)_nss_oslogin_setpwent }, -) - -NSS_REGISTER_METHODS(methods) -} // extern "C" diff --git a/packages/google-compute-engine-oslogin/packaging/debian/changelog b/packages/google-compute-engine-oslogin/packaging/debian/changelog index edb4f8e1..bb4a1b54 100644 --- a/packages/google-compute-engine-oslogin/packaging/debian/changelog +++ b/packages/google-compute-engine-oslogin/packaging/debian/changelog @@ -1,8 +1,21 @@ +google-compute-engine-oslogin (1:20190729.00-g1) stable; urgency=medium + + * Remove unnecessary binary search logic. + + -- Google Cloud Team Mon, 29 Jul 2019 10:11:00 -0700 + +google-compute-engine-oslogin (1:20190708.00-g1) stable; urgency=medium + + * Restructure Makefile and Debian control files. + * Add man pages. + + -- Google Cloud Team Mon, 08 Jul 2019 10:20:01 -0700 + google-compute-engine-oslogin (1.5.3-1) unstable; urgency=low * Update OS Login control file for FreeBSD support. - -- Google Cloud Team Tue, 22 Nay 2019 12:00:00 -0700 + -- Google Cloud Team Wed, 22 May 2019 12:00:00 -0700 google-compute-engine-oslogin (1.5.2-1) unstable; urgency=low diff --git a/packages/google-compute-engine-oslogin/packaging/debian/rules b/packages/google-compute-engine-oslogin/packaging/debian/rules index aa0f238f..8ae94b83 100755 --- a/packages/google-compute-engine-oslogin/packaging/debian/rules +++ b/packages/google-compute-engine-oslogin/packaging/debian/rules @@ -3,6 +3,4 @@ dh $@ override_dh_auto_install: - dh_auto_install -- PAM_INSTALL_PATH=/lib/$(DEB_HOST_MULTIARCH)/security \ - BIN_INSTALL_PATH=/usr/bin NSS_INSTALL_PATH=/lib/$(DEB_HOST_MULTIARCH)/ \ - MAKE_LINKS=true + dh_auto_install -- LIBDIR=/lib/$(DEB_HOST_MULTIARCH) diff --git a/packages/google-compute-engine-oslogin/packaging/google-compute-engine-oslogin.spec b/packages/google-compute-engine-oslogin/packaging/google-compute-engine-oslogin.spec index b8b0e67e..d9b67bdd 100644 --- a/packages/google-compute-engine-oslogin/packaging/google-compute-engine-oslogin.spec +++ b/packages/google-compute-engine-oslogin/packaging/google-compute-engine-oslogin.spec @@ -18,8 +18,9 @@ %endif Name: google-compute-engine-oslogin +Epoch: 1 Version: %{_version} -Release: 1%{?dist} +Release: g1%{?dist} Summary: OS Login Functionality for Google Compute Engine License: ASL 2.0 @@ -41,8 +42,6 @@ Requires: policycoreutils-python Requires: boost-regex Requires: json-c -%define pam_install_path /%{_lib}/security - %description This package contains several libraries and changes to enable OS Login functionality for Google Compute Engine. @@ -53,26 +52,28 @@ for Google Compute Engine. %setup %build -make %{?_smp_mflags} LIBS="-lcurl -ljson-c -lboost_regex" +make %{?_smp_mflags} LDLIBS="-lcurl -ljson-c -lboost_regex" %install rm -rf %{buildroot} -make install DESTDIR=%{buildroot} NSS_INSTALL_PATH=/%{_lib} PAM_INSTALL_PATH=%{pam_install_path} INSTALL_SELINUX=true +make install DESTDIR=%{buildroot} LIBDIR=/%{_lib} INSTALL_SELINUX=y %files %doc -%attr(0755,-,-) /%{_lib}/libnss_%{name}-%{version}.so -%attr(0755,-,-) /%{_lib}/libnss_cache_%{name}-%{version}.so -%if 0%{?rhel} == 8 +/%{_lib}/libnss_oslogin-%{version}.so +/%{_lib}/libnss_cache_oslogin-%{version}.so /%{_lib}/libnss_oslogin.so.2 /%{_lib}/libnss_cache_oslogin.so.2 -%endif -%attr(0755,-,-) %{pam_install_path}/pam_oslogin_admin.so -%attr(0755,-,-) %{pam_install_path}/pam_oslogin_login.so +/%{_lib}/security/pam_oslogin_admin.so +/%{_lib}/security/pam_oslogin_login.so /usr/bin/google_authorized_keys /usr/bin/google_oslogin_control /usr/bin/google_oslogin_nss_cache /usr/share/selinux/packages/oslogin.pp +%{_mandir}/man8/nss-oslogin.8.gz +%{_mandir}/man8/libnss_oslogin.so.2.8.gz +%{_mandir}/man8/nss-cache-oslogin.8.gz +%{_mandir}/man8/libnss_cache_oslogin.so.2.8.gz %post /sbin/ldconfig diff --git a/packages/google-compute-engine-oslogin/packaging/setup_deb.sh b/packages/google-compute-engine-oslogin/packaging/setup_deb.sh index 7788a407..d9de78c0 100755 --- a/packages/google-compute-engine-oslogin/packaging/setup_deb.sh +++ b/packages/google-compute-engine-oslogin/packaging/setup_deb.sh @@ -14,7 +14,13 @@ # limitations under the License. NAME="google-compute-engine-oslogin" -VERSION="1.5.3" +VERSION="20190729.00" + +DEB=$(cut -d. -f1 /dev/null rm -rf /tmp/debpackage mkdir /tmp/debpackage -tar czvf /tmp/debpackage/${NAME}_${VERSION}.orig.tar.gz --exclude .git --exclude packaging --transform "s/^\./${NAME}-${VERSION}/" . +tar czvf /tmp/debpackage/${NAME}_${VERSION}.orig.tar.gz --exclude .git \ + --exclude packaging --transform "s/^\./${NAME}-${VERSION}/" . pushd /tmp/debpackage tar xzvf ${NAME}_${VERSION}.orig.tar.gz @@ -38,7 +44,11 @@ tar xzvf ${NAME}_${VERSION}.orig.tar.gz cd ${NAME}-${VERSION} cp -r ${working_dir}/packaging/debian ./ +echo "Building on Debian ${DEB}, modifying latest changelog entry." +sed -r -i"" "1s/^${NAME} \((.*)\) (.+;.*)/${NAME} (\1+deb${DEB}) \2/" \ + debian/changelog -debuild -us -uc +echo "Starting build" +DEB_BUILD_OPTIONS=noddebs debuild -us -uc popd diff --git a/packages/google-compute-engine-oslogin/packaging/setup_rpm.sh b/packages/google-compute-engine-oslogin/packaging/setup_rpm.sh index 957257ff..d91d1645 100755 --- a/packages/google-compute-engine-oslogin/packaging/setup_rpm.sh +++ b/packages/google-compute-engine-oslogin/packaging/setup_rpm.sh @@ -15,7 +15,7 @@ NAME="google-compute-engine-oslogin" -VERSION="1.5.3" +VERSION="20190729.00" rpm_working_dir=/tmp/rpmpackage/${NAME}-${VERSION} working_dir=${PWD} diff --git a/packages/google-compute-engine-oslogin/policy/Makefile b/packages/google-compute-engine-oslogin/selinux/Makefile similarity index 100% rename from packages/google-compute-engine-oslogin/policy/Makefile rename to packages/google-compute-engine-oslogin/selinux/Makefile diff --git a/packages/google-compute-engine-oslogin/policy/README.md b/packages/google-compute-engine-oslogin/selinux/README.md similarity index 100% rename from packages/google-compute-engine-oslogin/policy/README.md rename to packages/google-compute-engine-oslogin/selinux/README.md diff --git a/packages/google-compute-engine-oslogin/policy/oslogin.fc b/packages/google-compute-engine-oslogin/selinux/oslogin.fc similarity index 100% rename from packages/google-compute-engine-oslogin/policy/oslogin.fc rename to packages/google-compute-engine-oslogin/selinux/oslogin.fc diff --git a/packages/google-compute-engine-oslogin/policy/oslogin.pp b/packages/google-compute-engine-oslogin/selinux/oslogin.pp similarity index 100% rename from packages/google-compute-engine-oslogin/policy/oslogin.pp rename to packages/google-compute-engine-oslogin/selinux/oslogin.pp diff --git a/packages/google-compute-engine-oslogin/policy/oslogin.te b/packages/google-compute-engine-oslogin/selinux/oslogin.te similarity index 100% rename from packages/google-compute-engine-oslogin/policy/oslogin.te rename to packages/google-compute-engine-oslogin/selinux/oslogin.te diff --git a/packages/google-compute-engine-oslogin/src/Makefile b/packages/google-compute-engine-oslogin/src/Makefile new file mode 100644 index 00000000..026633af --- /dev/null +++ b/packages/google-compute-engine-oslogin/src/Makefile @@ -0,0 +1,86 @@ +SHELL = /bin/sh +TOPDIR = $(realpath ..) + +VERSION = 20190729.00 + +CPPFLAGS = -Iinclude -I/usr/include/json-c +FLAGS = -fPIC -Wall -g +CFLAGS = $(FLAGS) -Wstrict-prototypes +CXXFLAGS = $(FLAGS) + +LDFLAGS = -shared -Wl,-soname,$(SONAME) +LDLIBS = -lcurl -ljson-c +PAMLIBS = -lpam $(LDLIBS) + +# Paths which should be overrideable. + +PREFIX = /usr +LIBDIR = $(PREFIX)/lib +BINDIR = $(PREFIX)/bin +PAMDIR = $(LIBDIR)/security +MANDIR = /usr/share/man + +NSS_OSLOGIN_SONAME = libnss_oslogin.so.2 +NSS_CACHE_OSLOGIN_SONAME = libnss_cache_oslogin.so.2 + +NSS_OSLOGIN = libnss_oslogin-$(VERSION).so +NSS_CACHE_OSLOGIN = libnss_cache_oslogin-$(VERSION).so + +PAM_LOGIN = pam_oslogin_login.so +PAM_ADMIN = pam_oslogin_admin.so + +BINARIES = google_oslogin_nss_cache google_authorized_keys + +all : $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) $(PAM_LOGIN) $(PAM_ADMIN) $(BINARIES) + +clean : + rm -f $(BINARIES) + find . -type f \( -iname '*.o' -o -iname '*.so' \) -delete + +.PHONY : all clean install + +# NSS modules. + +$(NSS_OSLOGIN) : SONAME = $(NSS_OSLOGIN_SONAME) +$(NSS_OSLOGIN) : nss/nss_oslogin.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +$(NSS_CACHE_OSLOGIN) : SONAME = $(NSS_CACHE_OSLOGIN_SONAME) +$(NSS_CACHE_OSLOGIN) : nss/nss_cache_oslogin.o nss/compat/getpwent_r.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +# PAM modules + +$(PAM_LOGIN) : pam/pam_oslogin_login.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS) + +$(PAM_ADMIN) : pam/pam_oslogin_admin.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -shared $^ -o $@ $(PAMLIBS) + +# Utilities. + +google_authorized_keys : authorized_keys/authorized_keys.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) + +google_oslogin_nss_cache: cache_refresh/cache_refresh.o utils.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) + +install: all + install -d $(DESTDIR)$(LIBDIR) + install -d $(DESTDIR)$(PAMDIR) + install -d $(DESTDIR)$(BINDIR) + install -d $(DESTDIR)$(MANDIR)/man8 + install -m 0644 -t $(DESTDIR)$(LIBDIR) $(NSS_OSLOGIN) $(NSS_CACHE_OSLOGIN) + ln -sf $(NSS_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_OSLOGIN_SONAME) + ln -sf $(NSS_CACHE_OSLOGIN) $(DESTDIR)$(LIBDIR)/$(NSS_CACHE_OSLOGIN_SONAME) + install -m 0644 -t $(DESTDIR)$(PAMDIR) $(PAM_ADMIN) $(PAM_LOGIN) + install -m 0755 -t $(DESTDIR)$(BINDIR) $(BINARIES) $(TOPDIR)/google_oslogin_control + install -m 0644 -t $(DESTDIR)$(MANDIR)/man8 $(TOPDIR)/man/nss-oslogin.8 $(TOPDIR)/man/nss-cache-oslogin.8 + gzip -9 $(DESTDIR)$(MANDIR)/man8/nss-oslogin.8 + gzip -9 $(DESTDIR)$(MANDIR)/man8/nss-cache-oslogin.8 + ln -sf nss-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_OSLOGIN_SONAME).8.gz + ln -sf nss-cache-oslogin.8.gz $(DESTDIR)$(MANDIR)/man8/$(NSS_CACHE_OSLOGIN_SONAME).8.gz +ifdef INSTALL_SELINUX + install -d $(DESTDIR)/usr/share/selinux/packages + install -m 0644 -t $(DESTDIR)/usr/share/selinux/packages $(TOPDIR)/selinux/oslogin.pp +endif diff --git a/packages/google-compute-engine-oslogin/authorized_keys/authorized_keys.cc b/packages/google-compute-engine-oslogin/src/authorized_keys/authorized_keys.cc similarity index 98% rename from packages/google-compute-engine-oslogin/authorized_keys/authorized_keys.cc rename to packages/google-compute-engine-oslogin/src/authorized_keys/authorized_keys.cc index 24d1b26a..3eda59c7 100644 --- a/packages/google-compute-engine-oslogin/authorized_keys/authorized_keys.cc +++ b/packages/google-compute-engine-oslogin/src/authorized_keys/authorized_keys.cc @@ -16,7 +16,7 @@ #include #include -#include "../utils/oslogin_utils.h" +#include using std::cout; using std::endl; diff --git a/packages/google-compute-engine-oslogin/nss_cache/nss_cache.cc b/packages/google-compute-engine-oslogin/src/cache_refresh/cache_refresh.cc similarity index 98% rename from packages/google-compute-engine-oslogin/nss_cache/nss_cache.cc rename to packages/google-compute-engine-oslogin/src/cache_refresh/cache_refresh.cc index 79e1c8fe..bc4a10c1 100644 --- a/packages/google-compute-engine-oslogin/nss_cache/nss_cache.cc +++ b/packages/google-compute-engine-oslogin/src/cache_refresh/cache_refresh.cc @@ -23,8 +23,8 @@ #include -#include "../compat.h" -#include "../utils/oslogin_utils.h" +#include +#include using oslogin_utils::BufferManager; diff --git a/packages/google-compute-engine-oslogin/compat.h b/packages/google-compute-engine-oslogin/src/include/compat.h similarity index 100% rename from packages/google-compute-engine-oslogin/compat.h rename to packages/google-compute-engine-oslogin/src/include/compat.h diff --git a/packages/google-compute-engine-oslogin/libnss_cache_oslogin/nss_cache_oslogin.h b/packages/google-compute-engine-oslogin/src/include/nss_cache_oslogin.h similarity index 100% rename from packages/google-compute-engine-oslogin/libnss_cache_oslogin/nss_cache_oslogin.h rename to packages/google-compute-engine-oslogin/src/include/nss_cache_oslogin.h diff --git a/packages/google-compute-engine-oslogin/utils/oslogin_utils.h b/packages/google-compute-engine-oslogin/src/include/oslogin_utils.h similarity index 79% rename from packages/google-compute-engine-oslogin/utils/oslogin_utils.h rename to packages/google-compute-engine-oslogin/src/include/oslogin_utils.h index 6cd20246..82763bf2 100644 --- a/packages/google-compute-engine-oslogin/utils/oslogin_utils.h +++ b/packages/google-compute-engine-oslogin/src/include/oslogin_utils.h @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include +#include + #include #include @@ -29,7 +32,7 @@ namespace oslogin_utils { // Metadata server URL. static const char kMetadataServerUrl[] = - "http://metadata.google.internal/computeMetadata/v1/oslogin/"; + "http://metadata.google.internal/computeMetadata/v1/oslogin/"; // BufferManager encapsulates and manages a buffer and length. This class is not // thread safe. @@ -44,9 +47,11 @@ class BufferManager { // buffer for the string. bool AppendString(const string& value, char** buffer, int* errnop); + // Return a pointer to a buffer of size bytes. Returns NULL and sets errnop to + // ERANGE if there is not enough space left in the buffer for the request. + void* Reserve(size_t bytes, int* errnop); + private: - // Return a pointer to a buffer of size bytes. - void* Reserve(size_t bytes); // Whether there is space available in the buffer. bool CheckSpaceAvailable(size_t bytes_to_write) const; @@ -66,6 +71,12 @@ class Challenge { string status; }; +class Group { + public: + int64_t gid; + string name; +}; + // NssCache caches passwd entries for getpwent_r. This is used to prevent making // an HTTP call on every getpwent_r invocation. Stores up to cache_size entries // at a time. This class is not thread safe. @@ -98,7 +109,8 @@ class NssCache { // make an http call to the server if necessary to retrieve additional // entries. Returns whether passwd retrieval was successful. If true, the // passwd result will contain valid data. - bool NssGetpwentHelper(BufferManager* buf, struct passwd* result, int* errnop); + bool NssGetpwentHelper(BufferManager* buf, struct passwd* result, + int* errnop); // Returns the page token for requesting the next page of passwd entries. string GetPageToken() { return page_token_; } @@ -114,7 +126,7 @@ class NssCache { std::string page_token_; // Index for requesting the next passwd from the cache. - int index_; + uint32_t index_; // Whether the NssCache has reached the last page of the database. bool on_last_page_; @@ -144,8 +156,7 @@ class MutexLock { }; // Callback invoked when Curl completes a request. -size_t -OnCurlWrite(void* buf, size_t size, size_t nmemb, void* userp); +size_t OnCurlWrite(void* buf, size_t size, size_t nmemb, void* userp); // Uses Curl to issue a GET request to the given url. Returns whether the // request was successful. If successful, the result from the server will be @@ -163,17 +174,42 @@ std::string UrlEncode(const string& param); // Returns true if the given passwd contains valid fields. If pw_dir, pw_shell, // or pw_passwd are not set, this will populate these entries with default // values. -bool ValidatePasswd(struct passwd* result, BufferManager* buf, - int* errnop); +bool ValidatePasswd(struct passwd* result, BufferManager* buf, int* errnop); + +// Adds users and associated array of char* to provided buffer and store pointer +// to array in result.gr_mem. +bool AddUsersToGroup(std::vector users, struct group* result, + BufferManager* buf, int* errnop); + +// Iterates through all groups until one matching provided group is found, +// replacing gr_name with a buffermanager provided string. +bool FindGroup(struct group* grp, BufferManager* buf, int* errnop); + +// Iterates through all users for a group, storing results in a provided string +// vector. +bool GetUsersForGroup(string groupname, std::vector* users, + int* errnop); + +// Iterates through all groups for a user, storing results in a provided string +// vector. +bool GetGroupsForUser(string username, std::vector* groups, int* errnop); + +// Parses a JSON groups response, storing results in a provided Group vector. +bool ParseJsonToGroups(const string& json, std::vector* groups); + +// Parses a JSON users response, storing results in a provided string vector. +bool ParseJsonToUsers(const string& json, std::vector* users); // Parses a JSON LoginProfiles response for SSH keys. Returns a vector of valid // ssh_keys. A key is considered valid if it's expiration date is greater than // current unix time. std::vector ParseJsonToSshKeys(const string& json); +// Parses a JSON object and returns the value associated with a given key. +bool ParseJsonToKey(const string& json, const string& key, string* response); + // Parses a JSON LoginProfiles response and returns the email under the "name" // field. -bool ParseJsonToKey(const string& json, const string& key, string* email); bool ParseJsonToEmail(const string& json, string* email); // Parses a JSON LoginProfiles response and populates the passwd struct with the @@ -187,7 +223,7 @@ bool ParseJsonToPasswd(const string& response, struct passwd* result, bool ParseJsonToSuccess(const string& json); // Parses a JSON startSession response into a vector of Challenge objects. -bool ParseJsonToChallenges(const string& json, vector *challenges); +bool ParseJsonToChallenges(const string& json, vector* challenges); // Calls the startSession API. bool StartSession(const string& email, string* response); diff --git a/packages/google-compute-engine-oslogin/libnss_cache_oslogin/compat/getpwent_r.c b/packages/google-compute-engine-oslogin/src/nss/compat/getpwent_r.c similarity index 100% rename from packages/google-compute-engine-oslogin/libnss_cache_oslogin/compat/getpwent_r.c rename to packages/google-compute-engine-oslogin/src/nss/compat/getpwent_r.c diff --git a/packages/google-compute-engine-oslogin/libnss_cache_oslogin/nss_cache_oslogin.c b/packages/google-compute-engine-oslogin/src/nss/nss_cache_oslogin.c similarity index 55% rename from packages/google-compute-engine-oslogin/libnss_cache_oslogin/nss_cache_oslogin.c rename to packages/google-compute-engine-oslogin/src/nss/nss_cache_oslogin.c index 28f0e0d4..55bb78d8 100644 --- a/packages/google-compute-engine-oslogin/libnss_cache_oslogin/nss_cache_oslogin.c +++ b/packages/google-compute-engine-oslogin/src/nss/nss_cache_oslogin.c @@ -14,8 +14,8 @@ // An NSS module which adds supports for file /etc/oslogin_passwd.cache -#include "nss_cache_oslogin.h" -#include "../compat.h" +#include +#include #include @@ -60,130 +60,10 @@ static inline enum nss_status _nss_cache_oslogin_ent_bad_return_code( return ret; } -// -// Binary search routines below here -// - -int _nss_cache_oslogin_bsearch2_compare(const void *key, const void *value) { - struct nss_cache_oslogin_args *args = (struct nss_cache_oslogin_args *)key; - const char *value_text = (const char *)value; - - // Using strcmp as the generation of the index sorts without - // locale awareness. - return strcmp(args->lookup_key, value_text); -} - -enum nss_status _nss_cache_oslogin_bsearch2(struct nss_cache_oslogin_args *args, - int *errnop) { - enum nss_cache_oslogin_match (*lookup)( - FILE *, struct nss_cache_oslogin_args *) = args->lookup_function; - FILE *file = NULL; - FILE *system_file_stream = NULL; - struct stat system_file; - struct stat sorted_file; - enum nss_status ret = 100; - long offset = 0; - void *mapped_data = NULL; - - file = fopen(args->sorted_filename, "r"); - if (file == NULL) { - DEBUG("error opening %s\n", args->sorted_filename); - return NSS_STATUS_UNAVAIL; - } - - // if the sorted file is older than the system file, do not risk stale - // data and abort - // TODO(vasilios): should be a compile or runtime option - if (stat(args->system_filename, &system_file) != 0) { - DEBUG("failed to stat %s\n", args->system_filename); - fclose(file); - return NSS_STATUS_UNAVAIL; - } - if (fstat(fileno(file), &sorted_file) != 0) { - DEBUG("failed to stat %s\n", args->sorted_filename); - fclose(file); - return NSS_STATUS_UNAVAIL; - } - if (difftime(system_file.st_mtime, sorted_file.st_mtime) > 0) { - DEBUG("%s may be stale, aborting lookup\n", args->sorted_filename); - fclose(file); - return NSS_STATUS_UNAVAIL; - } - - mapped_data = - mmap(NULL, sorted_file.st_size, PROT_READ, MAP_PRIVATE, fileno(file), 0); - if (mapped_data == MAP_FAILED) { - DEBUG("mmap failed\n"); - fclose(file); - return NSS_STATUS_UNAVAIL; - } - - const char *data = (const char *)mapped_data; - while (*data != '\n') { - ++data; - } - long entry_size = data - (const char *)mapped_data + 1; - long entry_count = sorted_file.st_size / entry_size; - - void *entry = bsearch(args, mapped_data, entry_count, entry_size, - &_nss_cache_oslogin_bsearch2_compare); - if (entry != NULL) { - const char *entry_text = entry; - sscanf(entry_text + strlen(entry_text) + 1, "%ld", &offset); - } - - if (munmap(mapped_data, sorted_file.st_size) == -1) { - DEBUG("munmap failed\n"); - } - fclose(file); - - if (entry == NULL) { - return NSS_STATUS_NOTFOUND; - } - - system_file_stream = fopen(args->system_filename, "r"); - if (system_file_stream == NULL) { - DEBUG("error opening %s\n", args->system_filename); - return NSS_STATUS_UNAVAIL; - } - - if (fseek(system_file_stream, offset, SEEK_SET) != 0) { - DEBUG("fseek fail\n"); - return NSS_STATUS_UNAVAIL; - } - - switch (lookup(system_file_stream, args)) { - case NSS_CACHE_OSLOGIN_EXACT: - ret = NSS_STATUS_SUCCESS; - break; - case NSS_CACHE_OSLOGIN_ERROR: - if (errno == ERANGE) { - // let the caller retry - *errnop = errno; - ret = _nss_cache_oslogin_ent_bad_return_code(*errnop); - } - break; - default: - ret = NSS_STATUS_UNAVAIL; - break; - } - - fclose(system_file_stream); - return ret; -} - // // Routines for passwd map defined below here // -// _nss_cache_oslogin_setpwent_path() -// Helper function for testing - -extern char *_nss_cache_oslogin_setpwent_path(const char *path) { - DEBUG("%s %s\n", "Setting p_filename to", path); - return strncpy(p_filename, path, NSS_CACHE_OSLOGIN_PATH_LENGTH - 1); -} - // _nss_cache_oslogin_pwuid_wrap() // Internal wrapper for binary searches, using uid-specific calls. @@ -329,42 +209,15 @@ enum nss_status _nss_cache_oslogin_getpwent_r(struct passwd *result, enum nss_status _nss_cache_oslogin_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) { - char filename[NSS_CACHE_OSLOGIN_PATH_LENGTH]; - struct nss_cache_oslogin_args args; enum nss_status ret; - strncpy(filename, p_filename, NSS_CACHE_OSLOGIN_PATH_LENGTH - 1); - if (strlen(filename) > NSS_CACHE_OSLOGIN_PATH_LENGTH - 7) { - DEBUG("filename too long\n"); - return NSS_STATUS_UNAVAIL; - } - strncat(filename, ".ixuid", 6); - - args.sorted_filename = filename; - args.system_filename = p_filename; - args.lookup_function = _nss_cache_oslogin_pwuid_wrap; - args.lookup_value = &uid; - args.lookup_result = result; - args.buffer = buffer; - args.buflen = buflen; - char uid_text[11]; - snprintf(uid_text, sizeof(uid_text), "%d", uid); - args.lookup_key = uid_text; - args.lookup_key_length = strlen(uid_text); - - DEBUG("Binary search for uid %d\n", uid); NSS_CACHE_OSLOGIN_LOCK(); - ret = _nss_cache_oslogin_bsearch2(&args, errnop); - - if (ret == NSS_STATUS_UNAVAIL) { - DEBUG("Binary search failed, falling back to full linear search\n"); - ret = _nss_cache_oslogin_setpwent_locked(); + ret = _nss_cache_oslogin_setpwent_locked(); - if (ret == NSS_STATUS_SUCCESS) { - while ((ret = _nss_cache_oslogin_getpwent_r_locked( - result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) { - if (result->pw_uid == uid) break; - } + if (ret == NSS_STATUS_SUCCESS) { + while ((ret = _nss_cache_oslogin_getpwent_r_locked( + result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) { + if (result->pw_uid == uid) break; } } @@ -381,55 +234,18 @@ enum nss_status _nss_cache_oslogin_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) { - char *pw_name; - char filename[NSS_CACHE_OSLOGIN_PATH_LENGTH]; - struct nss_cache_oslogin_args args; enum nss_status ret; NSS_CACHE_OSLOGIN_LOCK(); + ret = _nss_cache_oslogin_setpwent_locked(); - // name is a const char, we need a non-const copy - pw_name = malloc(strlen(name) + 1); - if (pw_name == NULL) { - DEBUG("malloc error\n"); - return NSS_STATUS_UNAVAIL; - } - strncpy(pw_name, name, strlen(name) + 1); - - strncpy(filename, p_filename, NSS_CACHE_OSLOGIN_PATH_LENGTH - 1); - if (strlen(filename) > NSS_CACHE_OSLOGIN_PATH_LENGTH - 8) { - DEBUG("filename too long\n"); - free(pw_name); - return NSS_STATUS_UNAVAIL; - } - strncat(filename, ".ixname", 7); - - args.sorted_filename = filename; - args.system_filename = p_filename; - args.lookup_function = _nss_cache_oslogin_pwnam_wrap; - args.lookup_value = pw_name; - args.lookup_result = result; - args.buffer = buffer; - args.buflen = buflen; - args.lookup_key = pw_name; - args.lookup_key_length = strlen(pw_name); - - DEBUG("Binary search for user %s\n", pw_name); - ret = _nss_cache_oslogin_bsearch2(&args, errnop); - - if (ret == NSS_STATUS_UNAVAIL) { - DEBUG("Binary search failed, falling back to full linear search\n"); - ret = _nss_cache_oslogin_setpwent_locked(); - - if (ret == NSS_STATUS_SUCCESS) { - while ((ret = _nss_cache_oslogin_getpwent_r_locked( - result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) { - if (!strcmp(result->pw_name, name)) break; - } + if (ret == NSS_STATUS_SUCCESS) { + while ((ret = _nss_cache_oslogin_getpwent_r_locked( + result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) { + if (!strcmp(result->pw_name, name)) break; } } - free(pw_name); _nss_cache_oslogin_endpwent_locked(); NSS_CACHE_OSLOGIN_UNLOCK(); diff --git a/packages/google-compute-engine-oslogin/src/nss/nss_oslogin.cc b/packages/google-compute-engine-oslogin/src/nss/nss_oslogin.cc new file mode 100644 index 00000000..2a34c830 --- /dev/null +++ b/packages/google-compute-engine-oslogin/src/nss/nss_oslogin.cc @@ -0,0 +1,209 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using std::string; + +using oslogin_utils::AddUsersToGroup; +using oslogin_utils::BufferManager; +using oslogin_utils::FindGroup; +using oslogin_utils::GetGroupsForUser; +using oslogin_utils::GetUsersForGroup; +using oslogin_utils::Group; +using oslogin_utils::HttpGet; +using oslogin_utils::kMetadataServerUrl; +using oslogin_utils::MutexLock; +using oslogin_utils::NssCache; +using oslogin_utils::ParseJsonToPasswd; +using oslogin_utils::UrlEncode; + +// Size of the NssCache. This also determines how many users will be requested +// per HTTP call. +static const uint64_t kNssCacheSize = 2048; + +// NssCache for storing passwd entries. +static NssCache nss_cache(kNssCacheSize); + +// Protects access to nss_cache. +static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; + +extern "C" { + +// Get a passwd entry by id. +enum nss_status _nss_oslogin_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, + int *errnop) { + BufferManager buffer_manager(buffer, buflen); + std::stringstream url; + url << kMetadataServerUrl << "users?uid=" << uid; + string response; + long http_code = 0; + if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || + response.empty()) { + *errnop = ENOENT; + return NSS_STATUS_NOTFOUND; + } + if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { + if (*errnop == EINVAL) { + openlog("nss_oslogin", LOG_PID, LOG_USER); + syslog(LOG_ERR, "Received malformed response from server: %s", + response.c_str()); + closelog(); + } + return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } + return NSS_STATUS_SUCCESS; +} + +// Get a passwd entry by name. +enum nss_status _nss_oslogin_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, + int *errnop) { + BufferManager buffer_manager(buffer, buflen); + std::stringstream url; + url << kMetadataServerUrl << "users?username=" << UrlEncode(name); + string response; + long http_code = 0; + if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || + response.empty()) { + *errnop = ENOENT; + return NSS_STATUS_NOTFOUND; + } + if (!ParseJsonToPasswd(response, result, &buffer_manager, errnop)) { + if (*errnop == EINVAL) { + openlog("nss_oslogin", LOG_PID, LOG_USER); + syslog(LOG_ERR, "Received malformed response from server: %s", + response.c_str()); + closelog(); + } + return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + } + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_oslogin_getgrby(struct group *grp, char *buf, + size_t buflen, int *errnop) { + BufferManager buffer_manager(buf, buflen); + if (!FindGroup(grp, &buffer_manager, errnop)) + return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + + std::vector users; + if (!GetUsersForGroup(grp->gr_name, &users, errnop)) + return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + + if (!AddUsersToGroup(users, grp, &buffer_manager, errnop)) + return *errnop == ERANGE ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_oslogin_getgrgid_r(gid_t gid, struct group *grp, char *buf, + size_t buflen, int *errnop) { + grp->gr_gid = gid; + return _nss_oslogin_getgrby(grp, buf, buflen, errnop); +} + +enum nss_status _nss_oslogin_getgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, int *errnop) { + grp->gr_name = (char *)name; + return _nss_oslogin_getgrby(grp, buf, buflen, errnop); +} + +enum nss_status _nss_oslogin_initgroups_dyn(const char *user, gid_t skipgroup, + long int *start, long int *size, + gid_t **groupsp, long int limit, + int *errnop) { + std::vector grouplist; + if (!GetGroupsForUser(string(user), &grouplist, errnop)) { + return NSS_STATUS_NOTFOUND; + } + + gid_t *groups = *groupsp; + for (int i = 0; i < (int) grouplist.size(); i++) { + // Resize the buffer if needed. + if (*start == *size) { + gid_t *newgroups; + long int newsize = 2 * *size; + // Stop at limit if provided. + if (limit > 0) { + if (*size >= limit) { + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + newsize = MIN(limit, newsize); + } + newgroups = (gid_t *)realloc(groups, newsize * sizeof(gid_t *)); + if (newgroups == NULL) { + *errnop = EAGAIN; + return NSS_STATUS_TRYAGAIN; + } + *groupsp = groups = newgroups; + *size = newsize; + } + groups[(*start)++] = grouplist[i].gid; + } + return NSS_STATUS_SUCCESS; +} + +// nss_getpwent_r() is intentionally left unimplemented. This functionality is +// now covered by the nss_cache binary and nss_cache module. + +nss_status _nss_oslogin_getpwent_r() { return NSS_STATUS_NOTFOUND; } +nss_status _nss_oslogin_endpwent() { return NSS_STATUS_SUCCESS; } +nss_status _nss_oslogin_setpwent() { return NSS_STATUS_SUCCESS; } + +NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r); +NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r); +NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r); +NSS_METHOD_PROTOTYPE(__nss_compat_setpwent); +NSS_METHOD_PROTOTYPE(__nss_compat_endpwent); +NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r); +NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r); + +DECLARE_NSS_METHOD_TABLE(methods, + {NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, + (void *)_nss_oslogin_getpwnam_r}, + {NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, + (void *)_nss_oslogin_getpwuid_r}, + {NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, + (void *)_nss_oslogin_getpwent_r}, + {NSDB_PASSWD, "endpwent", __nss_compat_endpwent, + (void *)_nss_oslogin_endpwent}, + {NSDB_PASSWD, "setpwent", __nss_compat_setpwent, + (void *)_nss_oslogin_setpwent}, + {NSDB_GROUP, "getgrnam_r", __nss_compat_getgrnam_r, + (void *)_nss_oslogin_getgrnam_r}, + {NSDB_GROUP, "getgrgid_r", __nss_compat_getgrgid_r, + (void *)_nss_oslogin_getgrgid_r}, ) + +NSS_REGISTER_METHODS(methods) +} // extern "C" diff --git a/packages/google-compute-engine-oslogin/pam_module/pam_oslogin_admin.cc b/packages/google-compute-engine-oslogin/src/pam/pam_oslogin_admin.cc similarity index 98% rename from packages/google-compute-engine-oslogin/pam_module/pam_oslogin_admin.cc rename to packages/google-compute-engine-oslogin/src/pam/pam_oslogin_admin.cc index 9a774342..fe21db74 100644 --- a/packages/google-compute-engine-oslogin/pam_module/pam_oslogin_admin.cc +++ b/packages/google-compute-engine-oslogin/src/pam/pam_oslogin_admin.cc @@ -25,8 +25,8 @@ #include #include -#include "../compat.h" -#include "../utils/oslogin_utils.h" +#include +#include using std::string; diff --git a/packages/google-compute-engine-oslogin/pam_module/pam_oslogin_login.cc b/packages/google-compute-engine-oslogin/src/pam/pam_oslogin_login.cc similarity index 99% rename from packages/google-compute-engine-oslogin/pam_module/pam_oslogin_login.cc rename to packages/google-compute-engine-oslogin/src/pam/pam_oslogin_login.cc index 0c31aa84..8ddec7bd 100644 --- a/packages/google-compute-engine-oslogin/pam_module/pam_oslogin_login.cc +++ b/packages/google-compute-engine-oslogin/src/pam/pam_oslogin_login.cc @@ -26,8 +26,8 @@ #include #include -#include "../compat.h" -#include "../utils/oslogin_utils.h" +#include +#include using oslogin_utils::ContinueSession; using oslogin_utils::GetUser; diff --git a/packages/google-compute-engine-oslogin/utils/oslogin_utils.cc b/packages/google-compute-engine-oslogin/src/utils.cc similarity index 73% rename from packages/google-compute-engine-oslogin/utils/oslogin_utils.cc rename to packages/google-compute-engine-oslogin/src/utils.cc index 0ec4c8ba..c97529e2 100644 --- a/packages/google-compute-engine-oslogin/utils/oslogin_utils.cc +++ b/packages/google-compute-engine-oslogin/src/utils.cc @@ -15,18 +15,19 @@ // Requires libcurl4-openssl-dev libjson0 and libjson0-dev #include #include +#include #include #include #include #include + #include #include #include #if defined(__clang__) || __GNUC__ > 4 || \ - (__GNUC__ == 4 && (__GNUC_MINOR__ > 9 || \ - (__GNUC_MINOR__ == 9 && \ - __GNUC_PATCHLEVEL__ > 0))) + (__GNUC__ == 4 && \ + (__GNUC_MINOR__ > 9 || (__GNUC_MINOR__ == 9 && __GNUC_PATCHLEVEL__ > 0))) #include #define Regex std #else @@ -34,8 +35,8 @@ #define Regex boost #endif -#include "oslogin_utils.h" -#include "../compat.h" +#include +#include using std::string; @@ -53,11 +54,10 @@ BufferManager::BufferManager(char* buf, size_t buflen) bool BufferManager::AppendString(const string& value, char** buffer, int* errnop) { size_t bytes_to_write = value.length() + 1; - if (!CheckSpaceAvailable(bytes_to_write)) { - *errnop = ERANGE; + *buffer = static_cast(Reserve(bytes_to_write, errnop)); + if (*buffer == NULL) { return false; } - *buffer = static_cast(Reserve(bytes_to_write)); strncpy(*buffer, value.c_str(), bytes_to_write); return true; } @@ -69,11 +69,10 @@ bool BufferManager::CheckSpaceAvailable(size_t bytes_to_write) const { return true; } -void* BufferManager::Reserve(size_t bytes) { - if (buflen_ < bytes) { - std::cerr << "Attempted to reserve more bytes than the buffer can hold!" - << "\n"; - abort(); +void* BufferManager::Reserve(size_t bytes, int* errnop) { + if (!CheckSpaceAvailable(bytes)) { + *errnop = ERANGE; + return NULL; } void* result = buf_; buf_ += bytes; @@ -95,7 +94,7 @@ void NssCache::Reset() { } bool NssCache::HasNextPasswd() { - return index_ < passwd_cache_.size() && !passwd_cache_[index_].empty(); + return (index_ < passwd_cache_.size()) && !passwd_cache_[index_].empty(); } bool NssCache::GetNextPasswd(BufferManager* buf, passwd* result, int* errnop) { @@ -172,7 +171,7 @@ bool NssCache::NssGetpwentHelper(BufferManager* buf, struct passwd* result, response.empty() || !LoadJsonArrayToCache(response)) { // It is possible this to be true after LoadJsonArrayToCache(), so we // must check it again. - if(!OnLastPage()) { + if (!OnLastPage()) { *errnop = ENOENT; } return false; @@ -268,8 +267,7 @@ string UrlEncode(const string& param) { return encoded_param; } -bool ValidatePasswd(struct passwd* result, BufferManager* buf, - int* errnop) { +bool ValidatePasswd(struct passwd* result, BufferManager* buf, int* errnop) { // OS Login disallows uids less than 1000. if (result->pw_uid < 1000) { *errnop = EINVAL; @@ -307,6 +305,72 @@ bool ValidatePasswd(struct passwd* result, BufferManager* buf, return true; } +bool ParseJsonToUsers(const string& json, std::vector* result) { + json_object* root = NULL; + root = json_tokener_parse(json.c_str()); + if (root == NULL) { + return false; + } + + json_object* users = NULL; + if (!json_object_object_get_ex(root, "usernames", &users)) { + return false; + } + if (json_object_get_type(users) != json_type_array) { + return false; + } + for (int idx = 0; idx < json_object_array_length(users); idx++) { + json_object* user = json_object_array_get_idx(users, idx); + const char* username = json_object_get_string(user); + result->push_back(string(username)); + } + return true; +} + +bool ParseJsonToGroups(const string& json, std::vector* result) { + json_object* root = NULL; + root = json_tokener_parse(json.c_str()); + if (root == NULL) { + return false; + } + + json_object* groups = NULL; + if (!json_object_object_get_ex(root, "posixGroups", &groups)) { + return false; + } + if (json_object_get_type(groups) != json_type_array) { + return false; + } + for (int idx = 0; idx < json_object_array_length(groups); idx++) { + json_object* group = json_object_array_get_idx(groups, idx); + + json_object* gid; + if (!json_object_object_get_ex(group, "gid", &gid)) { + return false; + } + + json_object* name; + if (!json_object_object_get_ex(group, "name", &name)) { + return false; + } + + Group g; + g.gid = json_object_get_int64(gid); + // get_int64 will confusingly return 0 if the string can't be converted to + // an integer. We can't rely on type check as it may be a string in the API. + if (g.gid == 0) { + return false; + } + g.name = json_object_get_string(name); + if (g.name == "") { + return false; + } + + result->push_back(g); + } + return true; +} + std::vector ParseJsonToSshKeys(const string& json) { std::vector result; json_object* root = NULL; @@ -333,17 +397,14 @@ std::vector ParseJsonToSshKeys(const string& json) { if (json_object_get_type(ssh_public_keys) != json_type_object) { return result; } - json_object_object_foreach(ssh_public_keys, key, val) { - json_object* iter; - if (!json_object_object_get_ex(ssh_public_keys, key, &iter)) { - return result; - } - if (json_object_get_type(iter) != json_type_object) { + json_object_object_foreach(ssh_public_keys, key, obj) { + (void)(key); + if (json_object_get_type(obj) != json_type_object) { continue; } string key_to_add = ""; bool expired = false; - json_object_object_foreach(iter, key, val) { + json_object_object_foreach(obj, key, val) { string string_key(key); int val_type = json_object_get_type(val); if (string_key == "key") { @@ -470,6 +531,31 @@ bool ParseJsonToPasswd(const string& json, struct passwd* result, return ValidatePasswd(result, buf, errnop); } +bool AddUsersToGroup(std::vector users, struct group* result, + BufferManager* buf, int* errnop) { + if (users.size() < 1) { + return true; + } + + // Get some space for the char* array for number of users + 1 for NULL cap. + char** bufp; + if (!(bufp = + (char**)buf->Reserve(sizeof(char*) * (users.size() + 1), errnop))) { + return false; + } + result->gr_mem = bufp; + + for (int i = 0; i < (int) users.size(); i++) { + if (!buf->AppendString(users[i], bufp, errnop)) { + result->gr_mem = NULL; + return false; + } + } + *bufp = NULL; // End the array with a null pointer. + + return true; +} + bool ParseJsonToEmail(const string& json, string* email) { json_object* root = NULL; root = json_tokener_parse(json.c_str()); @@ -513,7 +599,7 @@ bool ParseJsonToKey(const string& json, const string& key, string* response) { const char* c_response; root = json_tokener_parse(json.c_str()); - if (root==NULL) { + if (root == NULL) { return false; } @@ -530,7 +616,7 @@ bool ParseJsonToKey(const string& json, const string& key, string* response) { } bool ParseJsonToChallenges(const string& json, - std::vector *challenges) { + std::vector* challenges) { json_object* root = NULL; root = json_tokener_parse(json.c_str()); @@ -538,7 +624,7 @@ bool ParseJsonToChallenges(const string& json, return false; } - json_object *jsonChallenges = NULL; + json_object* jsonChallenges = NULL; if (!json_object_object_get_ex(root, "challenges", &jsonChallenges)) { return false; } @@ -568,13 +654,135 @@ bool ParseJsonToChallenges(const string& json, return true; } +bool FindGroup(struct group* result, BufferManager* buf, int* errnop) { + if (result->gr_name == NULL && result->gr_gid == 0) { + return false; + } + std::stringstream url; + std::vector groups; + + string response; + long http_code; + string pageToken = ""; + + do { + url.str(""); + url << kMetadataServerUrl << "groups"; + if (pageToken != "") url << "?pageToken=" << pageToken; + + response.clear(); + http_code = 0; + if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || + response.empty()) { + *errnop = EAGAIN; + return false; + } + + if (!ParseJsonToKey(response, "nextPageToken", &pageToken)) { + pageToken = ""; + } + + groups.clear(); + if (!ParseJsonToGroups(response, &groups) || groups.empty()) { + *errnop = ENOENT; + return false; + } + + // Check for a match. + for (int i = 0; i < (int) groups.size(); i++) { + Group el = groups[i]; + if ((result->gr_name != NULL) && (string(result->gr_name) == el.name)) { + // Set the name even though it matches because the final string must + // be stored in the provided buffer. + if (!buf->AppendString(el.name, &result->gr_name, errnop)) { + return false; + } + result->gr_gid = el.gid; + return true; + } + if ((result->gr_gid != 0) && (result->gr_gid == el.gid)) { + if (!buf->AppendString(el.name, &result->gr_name, errnop)) { + return false; + } + return true; + } + } + } while (pageToken != ""); + // Not found. + *errnop = ENOENT; + return false; +} + +bool GetGroupsForUser(string username, std::vector* groups, + int* errnop) { + std::stringstream url; + + string response; + long http_code; + string pageToken = ""; + + do { + url.str(""); + url << kMetadataServerUrl << "groups?username=" << username; + if (pageToken != "") url << "?pageToken=" << pageToken; + + response.clear(); + http_code = 0; + if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || + response.empty()) { + *errnop = EAGAIN; + return false; + } + + if (!ParseJsonToKey(response, "pageToken", &pageToken)) { + pageToken = ""; + } + + if (!ParseJsonToGroups(response, groups)) { + *errnop = ENOENT; + return false; + } + } while (pageToken != ""); + return true; +} + +bool GetUsersForGroup(string groupname, std::vector* users, + int* errnop) { + string response; + long http_code; + string pageToken = ""; + std::stringstream url; + + do { + url.str(""); + url << kMetadataServerUrl << "users?groupname=" << groupname; + if (pageToken != "") url << "?pageToken=" << pageToken; + + response.clear(); + http_code = 0; + if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || + response.empty()) { + *errnop = EAGAIN; + return false; + } + if (!ParseJsonToKey(response, "nextPageToken", &pageToken)) { + pageToken = ""; + } + if (!ParseJsonToUsers(response, users)) { + *errnop = EINVAL; + return false; + } + } while (pageToken != ""); + return true; +} + bool GetUser(const string& username, string* response) { std::stringstream url; url << kMetadataServerUrl << "users?username=" << UrlEncode(username); long http_code = 0; - if (!HttpGet(url.str(), response, &http_code) || response->empty() - || http_code != 200) { + if (!HttpGet(url.str(), response, &http_code) || response->empty() || + http_code != 200) { return false; } return true; @@ -601,8 +809,8 @@ bool StartSession(const string& email, string* response) { url << kMetadataServerUrl << "authenticate/sessions/start"; long http_code = 0; - if (!HttpPost(url.str(), data, response, &http_code) || response->empty() - || http_code != 200) { + if (!HttpPost(url.str(), data, response, &http_code) || response->empty() || + http_code != 200) { ret = false; } @@ -627,8 +835,7 @@ bool ContinueSession(bool alt, const string& email, const string& user_token, json_object_object_add(jobj, "action", json_object_new_string("START_ALTERNATE")); } else { - json_object_object_add(jobj, "action", - json_object_new_string("RESPOND")); + json_object_object_add(jobj, "action", json_object_new_string("RESPOND")); } // AUTHZEN type and START_ALTERNATE action don't provide credentials. @@ -643,11 +850,11 @@ bool ContinueSession(bool alt, const string& email, const string& user_token, data = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN); std::stringstream url; - url << kMetadataServerUrl << "authenticate/sessions/" - << session_id << "/continue"; + url << kMetadataServerUrl << "authenticate/sessions/" << session_id + << "/continue"; long http_code = 0; - if (!HttpPost(url.str(), data, response, &http_code) || response->empty() - || http_code != 200) { + if (!HttpPost(url.str(), data, response, &http_code) || response->empty() || + http_code != 200) { ret = false; } diff --git a/packages/google-compute-engine-oslogin/test/Makefile b/packages/google-compute-engine-oslogin/test/Makefile new file mode 100644 index 00000000..83d23fd8 --- /dev/null +++ b/packages/google-compute-engine-oslogin/test/Makefile @@ -0,0 +1,34 @@ +TOPDIR = $(realpath ..) + +CPPFLAGS += -I$(TOPDIR)/src/include -I/usr/include/json-c +CXXFLAGS += -g -Wall -Wextra -std=c++11 +LDLIBS = -lcurl -ljson-c -lpthread + +all : test_runner non_network_tests + +clean : + rm -f test_runner *.o + +gtest-all.o : $(GTEST_DIR)/src/gtest-all.cc + $(CXX) $(CXXFLAGS) -isystem $(GTEST)/include -I$(GTEST) $(CPPFLAGS) -c $^ + +test_runner : oslogin_utils_test.o $(TOPDIR)/src/utils.o gtest-all.o + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ $(LDLIBS) + +non_network_tests : test_runner + ./test_runner --gtest_filter=*-FindGroupTest.*:GetUsersForGroupTest.* + +network_tests : test_runner ping reset + ./test_runner --gtest_filter=FindGroupTest.*:GetUsersForGroupTest.* + +# run as $ make tests GTESTARGS="--gtest_filter=FindGroupTest.*" +tests : test_runner + ./test_runner ${GTESTARGS} + +ping : + nc -vzw2 metadata.google.internal 80 >/dev/null 2>&1 + +reset : + curl -Ss http://metadata.google.internal/reset >/dev/null 2>&1 + +.PHONY : all clean tests ping reset gtest prowtest diff --git a/packages/google-compute-engine-oslogin/utils/oslogin_utils_test.cc b/packages/google-compute-engine-oslogin/test/oslogin_utils_test.cc similarity index 72% rename from packages/google-compute-engine-oslogin/utils/oslogin_utils_test.cc rename to packages/google-compute-engine-oslogin/test/oslogin_utils_test.cc index ecc4c116..6c9c1df9 100644 --- a/packages/google-compute-engine-oslogin/utils/oslogin_utils_test.cc +++ b/packages/google-compute-engine-oslogin/test/oslogin_utils_test.cc @@ -13,10 +13,9 @@ // limitations under the License. // Requires libgtest-dev and gtest compiled and installed. -#include "oslogin_utils.h" - #include #include +#include #include #include @@ -25,11 +24,10 @@ using std::vector; namespace oslogin_utils { - // Test that the buffer can successfully append multiple strings. TEST(BufferManagerTest, TestAppendString) { size_t buflen = 20; - char* buffer = (char*)malloc(buflen *sizeof(char)); + char* buffer = (char*)malloc(buflen * sizeof(char)); ASSERT_STRNE(buffer, NULL); char* first_string; @@ -38,7 +36,7 @@ TEST(BufferManagerTest, TestAppendString) { oslogin_utils::BufferManager buffer_manager(buffer, buflen); buffer_manager.AppendString("test1", &first_string, &test_errno); buffer_manager.AppendString("test2", &second_string, &test_errno); - EXPECT_EQ(test_errno, 0); + ASSERT_EQ(test_errno, 0); ASSERT_STREQ(first_string, "test1"); ASSERT_STREQ(second_string, "test2"); ASSERT_STREQ(buffer, "test1"); @@ -57,7 +55,7 @@ TEST(BufferManagerTest, TestAppendStringTooLarge) { oslogin_utils::BufferManager buffer_manager(buffer, buflen); ASSERT_FALSE( buffer_manager.AppendString("test1", &first_string, &test_errno)); - EXPECT_EQ(test_errno, ERANGE); + ASSERT_EQ(test_errno, ERANGE); } // Test successfully loading and retrieving an array of JSON posix accounts. @@ -87,9 +85,9 @@ TEST(NssCacheTest, TestLoadJsonArray) { // Verify that the first user was stored. ASSERT_TRUE(nss_cache.HasNextPasswd()); ASSERT_TRUE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, 0); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1337); + ASSERT_EQ(test_errno, 0); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1337); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -97,9 +95,9 @@ TEST(NssCacheTest, TestLoadJsonArray) { // Verify that the second user was stored. ASSERT_TRUE(nss_cache.HasNextPasswd()); ASSERT_TRUE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, 0); - EXPECT_EQ(result.pw_uid, 1338); - EXPECT_EQ(result.pw_gid, 1338); + ASSERT_EQ(test_errno, 0); + ASSERT_EQ(result.pw_uid, 1338); + ASSERT_EQ(result.pw_gid, 1338); ASSERT_STREQ(result.pw_name, "bar"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/bar"); @@ -107,7 +105,7 @@ TEST(NssCacheTest, TestLoadJsonArray) { // Verify that there are no more users stored. ASSERT_FALSE(nss_cache.HasNextPasswd()); ASSERT_FALSE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, ENOENT); + ASSERT_EQ(test_errno, ENOENT); } // Test successfully loading and retrieving a partial array. @@ -132,9 +130,9 @@ TEST(NssCacheTest, TestLoadJsonPartialArray) { // Verify that the first user was stored. ASSERT_TRUE(nss_cache.HasNextPasswd()); ASSERT_TRUE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, 0); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1337); + ASSERT_EQ(test_errno, 0); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1337); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -144,14 +142,13 @@ TEST(NssCacheTest, TestLoadJsonPartialArray) { // Verify that there are no more users stored. ASSERT_FALSE(nss_cache.HasNextPasswd()); ASSERT_FALSE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, ENOENT); + ASSERT_EQ(test_errno, ENOENT); } // Test successfully loading and retrieving the final response. TEST(NssCacheTest, TestLoadJsonFinalResponse) { NssCache nss_cache(2); - string response = - "{\"nextPageToken\": \"0\"}"; + string response = "{\"nextPageToken\": \"0\"}"; ASSERT_FALSE(nss_cache.LoadJsonArrayToCache(response)); ASSERT_EQ(nss_cache.GetPageToken(), ""); @@ -167,10 +164,9 @@ TEST(NssCacheTest, TestLoadJsonFinalResponse) { ASSERT_FALSE(nss_cache.HasNextPasswd()); ASSERT_TRUE(nss_cache.OnLastPage()); ASSERT_FALSE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); - EXPECT_EQ(test_errno, ENOENT); + ASSERT_EQ(test_errno, ENOENT); } - // Tests that resetting, and checking HasNextPasswd does not crash. TEST(NssCacheTest, ResetNullPtrTest) { NssCache nss_cache(2); @@ -192,8 +188,8 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdSucceeds) { struct passwd result; int test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1338); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1338); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -213,8 +209,8 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdSucceedsWithHighUid) { struct passwd result; int test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(result.pw_uid, 4294967295); - EXPECT_EQ(result.pw_gid, 4294967295); + ASSERT_EQ(result.pw_uid, 4294967295); + ASSERT_EQ(result.pw_gid, 4294967295); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -233,8 +229,8 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdSucceedsWithStringUid) { struct passwd result; int test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1338); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1338); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -253,8 +249,8 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdNoLoginProfilesSucceeds) { struct passwd result; int test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1337); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1337); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -274,7 +270,7 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdFailsWithERANGE) { struct passwd result; int test_errno = 0; ASSERT_FALSE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(test_errno, ERANGE); + ASSERT_EQ(test_errno, ERANGE); } // Test parsing malformed JSON responses. @@ -297,13 +293,13 @@ TEST(ParseJsonPasswdTest, ParseJsonToPasswdFailsWithEINVAL) { struct passwd result; int test_errno = 0; ASSERT_FALSE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(test_errno, EINVAL); + ASSERT_EQ(test_errno, EINVAL); // Reset errno. test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user2, &result, &buf, &test_errno)); - EXPECT_EQ(test_errno, 0); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1337); + ASSERT_EQ(test_errno, 0); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1337); } // Test parsing a partially filled response. Validate should fill empty fields @@ -321,8 +317,8 @@ TEST(ParseJsonPasswdTest, ValidatePartialJsonResponse) { struct passwd result; int test_errno = 0; ASSERT_TRUE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(result.pw_uid, 1337); - EXPECT_EQ(result.pw_gid, 1337); + ASSERT_EQ(result.pw_uid, 1337); + ASSERT_EQ(result.pw_gid, 1337); ASSERT_STREQ(result.pw_name, "foo"); ASSERT_STREQ(result.pw_shell, "/bin/bash"); ASSERT_STREQ(result.pw_dir, "/home/foo"); @@ -343,7 +339,139 @@ TEST(ParseJsonPasswdTest, ValidateInvalidJsonResponse) { struct passwd result; int test_errno = 0; ASSERT_FALSE(ParseJsonToPasswd(test_user, &result, &buf, &test_errno)); - EXPECT_EQ(test_errno, EINVAL); + ASSERT_EQ(test_errno, EINVAL); +} + +// Test parsing a valid JSON response from the metadata server. +TEST(ParseJsonToGroupsTest, ParseJsonToGroupsSucceeds) { + string test_group = "{\"posixGroups\":[{\"name\":\"demo\",\"gid\":123452}]}"; + + std::vector groups; + ASSERT_TRUE(ParseJsonToGroups(test_group, &groups)); + ASSERT_EQ(groups[0].gid, 123452); + ASSERT_EQ(groups[0].name, "demo"); +} + +// Test parsing a valid JSON response from the metadata server with gid > 2^31. +TEST(ParseJsonToGroupsTest, ParseJsonToGroupsSucceedsWithHighGid) { + string test_group = + "{\"posixGroups\":[{\"name\":\"demo\",\"gid\":4294967295}]}"; + + std::vector groups; + ASSERT_TRUE(ParseJsonToGroups(test_group, &groups)); + ASSERT_EQ(groups[0].gid, 4294967295); + ASSERT_EQ(groups[0].name, "demo"); +} + +TEST(ParseJsonToGroupsTest, ParseJsonToGroupsSucceedsWithStringGid) { + string test_group = + "{\"posixGroups\":[{\"name\":\"demo\",\"gid\":\"123452\"}]}"; + + std::vector groups; + ASSERT_TRUE(ParseJsonToGroups(test_group, &groups)); + ASSERT_EQ(groups[0].gid, 123452); + ASSERT_EQ(groups[0].name, "demo"); +} + +// Test parsing malformed JSON responses. +TEST(ParseJsonToGroupsTest, ParseJsonToGroupsFails) { + string test_badgid = + "{\"posixGroups\":[{\"name\":\"demo\",\"gid\":\"this-should-be-int\"}]}"; + string test_nogid = "{\"posixGroups\":[{\"name\":\"demo\"}]}"; + string test_noname = "{\"posixGroups\":[{\"gid\":123452}]}"; + + std::vector groups; + ASSERT_FALSE(ParseJsonToGroups(test_badgid, &groups)); + ASSERT_FALSE(ParseJsonToGroups(test_nogid, &groups)); + ASSERT_FALSE(ParseJsonToGroups(test_noname, &groups)); +} + +// Test parsing a valid JSON response from the metadata server. +TEST(ParseJsonToUsersTest, ParseJsonToUsersSucceeds) { + string test_group_users = + "{\"usernames\":[\"user0001\",\"user0002\",\"user0003\",\"user0004\"," + "\"user0005\"]}"; + + std::vector users; + ASSERT_TRUE(ParseJsonToUsers(test_group_users, &users)); + ASSERT_FALSE(users.empty()); + ASSERT_EQ(users.size(), 5); + + ASSERT_EQ(users[0], "user0001"); + ASSERT_EQ(users[1], "user0002"); + ASSERT_EQ(users[2], "user0003"); + ASSERT_EQ(users[3], "user0004"); + ASSERT_EQ(users[4], "user0005"); +} + +// Test parsing a valid JSON response from the metadata server. +TEST(ParseJsonToUsersTest, ParseJsonToUsersEmptyGroupSucceeds) { + string test_group_users = "{\"usernames\":[]}"; + + std::vector users; + ASSERT_TRUE(ParseJsonToUsers(test_group_users, &users)); + ASSERT_TRUE(users.empty()); +} + +// Test parsing malformed JSON responses. +TEST(ParseJsonToUsersTest, ParseJsonToUsersFails) { + string test_group_users = + "{\"badstuff\":[\"user0001\",\"user0002\",\"user0003\",\"user0004\"," + "\"user0005\"]}"; + + std::vector users; + ASSERT_FALSE(ParseJsonToUsers(test_group_users, &users)); +} + +TEST(GetUsersForGroupTest, GetUsersForGroupSucceeds) { + string response; + long http_code; + ASSERT_TRUE( + HttpGet("http://metadata.google.internal/reset", &response, &http_code)); + + std::vector users; + int errnop = 0; + + ASSERT_TRUE(GetUsersForGroup("demo", &users, &errnop)); + ASSERT_FALSE(users.empty()); + ASSERT_EQ(users[0], "user000173_grande_focustest_org"); + ASSERT_EQ(errnop, 0); +} + +TEST(FindGroupTest, FindGroupByGidSucceeds) { + string response; + long http_code; + ASSERT_TRUE( + HttpGet("http://metadata.google.internal/reset", &response, &http_code)); + + size_t buflen = 200 * sizeof(char); + char* buffer = (char*)malloc(buflen); + ASSERT_STRNE(buffer, NULL); + BufferManager buf(buffer, buflen); + int errnop = 0; + + struct group grp = {}; + grp.gr_gid = 123452; + ASSERT_TRUE(FindGroup(&grp, &buf, &errnop)); + ASSERT_EQ(errnop, 0); +} + +TEST(FindGroupTest, FindGroupByNameSucceeds) { + string response; + long http_code; + ASSERT_TRUE( + HttpGet("http://metadata.google.internal/reset", &response, &http_code)); + + size_t buflen = 200 * sizeof(char); + char* buffer = (char*)malloc(buflen); + ASSERT_STRNE(buffer, NULL); + BufferManager buf(buffer, buflen); + int errnop; + + const char* match = "demo"; + struct group grp = {}; + grp.gr_name = (char*)match; + ASSERT_TRUE(FindGroup(&grp, &buf, &errnop)); } TEST(ParseJsonEmailTest, SuccessfullyParsesEmail) { @@ -372,10 +500,9 @@ TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysSucceeds) { char* buffer = (char*)malloc(buflen * sizeof(char)); ASSERT_STRNE(buffer, NULL); BufferManager buf(buffer, buflen); - int test_errno = 0; std::vector result = ParseJsonToSshKeys(test_user); - EXPECT_EQ(result.size(), 1); - EXPECT_EQ(result[0], "test_key"); + ASSERT_EQ(result.size(), 1); + ASSERT_EQ(result[0], "test_key"); } TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysMultipleKeys) { @@ -388,11 +515,10 @@ TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysMultipleKeys) { char* buffer = (char*)malloc(buflen * sizeof(char)); ASSERT_STRNE(buffer, NULL); BufferManager buf(buffer, buflen); - int test_errno = 0; std::vector result = ParseJsonToSshKeys(test_user); - EXPECT_EQ(result.size(), 2); - EXPECT_EQ(result[0], "test_key"); - EXPECT_EQ(result[1], "test_key2"); + ASSERT_EQ(result.size(), 2); + ASSERT_EQ(result[0], "test_key"); + ASSERT_EQ(result[1], "test_key2"); } TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysFiltersExpiredKeys) { @@ -405,10 +531,9 @@ TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysFiltersExpiredKeys) { char* buffer = (char*)malloc(buflen * sizeof(char)); ASSERT_STRNE(buffer, NULL); BufferManager buf(buffer, buflen); - int test_errno = 0; std::vector result = ParseJsonToSshKeys(test_user); - EXPECT_EQ(result.size(), 1); - EXPECT_EQ(result[0], "test_key"); + ASSERT_EQ(result.size(), 1); + ASSERT_EQ(result[0], "test_key"); } TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysFiltersMalformedExpiration) { @@ -421,10 +546,9 @@ TEST(ParseJsonSshKeyTest, ParseJsonToSshKeysFiltersMalformedExpiration) { char* buffer = (char*)malloc(buflen * sizeof(char)); ASSERT_STRNE(buffer, NULL); BufferManager buf(buffer, buflen); - int test_errno = 0; std::vector result = ParseJsonToSshKeys(test_user); - EXPECT_EQ(result.size(), 1); - EXPECT_EQ(result[0], "test_key"); + ASSERT_EQ(result.size(), 1); + ASSERT_EQ(result[0], "test_key"); } TEST(ParseJsonAuthorizeSuccess, SuccessfullyAuthorized) { @@ -433,16 +557,8 @@ TEST(ParseJsonAuthorizeSuccess, SuccessfullyAuthorized) { } TEST(ValidateUserNameTest, ValidateValidUserNames) { - string cases[] = { - "user", - "_", - ".", - ".abc_", - "_abc-", - "ABC", - "A_.-", - "ausernamethirtytwocharacterslong" - }; + string cases[] = {"user", "_", ".", ".abc_", + "_abc-", "ABC", "A_.-", "ausernamethirtytwocharacterslong"}; for (auto test_user : cases) { ASSERT_TRUE(ValidateUserName(test_user)); } @@ -482,29 +598,30 @@ TEST(ParseJsonKeyTest, TestMissingKey) { } TEST(ParseJsonChallengesTest, TestChallenges) { - string challenges_json = "{\"status\":\"CHALLENGE_REQUIRED\",\"sessionId\":" + string challenges_json = + "{\"status\":\"CHALLENGE_REQUIRED\",\"sessionId\":" "\"testSessionId\",\"challenges\":[{\"challengeId\":1,\"challengeType\":" "\"TOTP\",\"status\":\"READY\"}, {\"challengeId\":2,\"challengeType\":" "\"AUTHZEN\",\"status\":\"PROPOSED\"}]}"; vector challenges; ASSERT_TRUE(ParseJsonToChallenges(challenges_json, &challenges)); - EXPECT_EQ(challenges.size(), 2); - EXPECT_EQ(challenges[0].id, 1); - EXPECT_EQ(challenges[0].type, "TOTP"); + ASSERT_EQ(challenges.size(), 2); + ASSERT_EQ(challenges[0].id, 1); + ASSERT_EQ(challenges[0].type, "TOTP"); } TEST(ParseJsonChallengesTest, TestMalformedChallenges) { - string challenges_json = "{\"status\":\"CHALLENGE_REQUIRED\",\"sessionId\":" + string challenges_json = + "{\"status\":\"CHALLENGE_REQUIRED\",\"sessionId\":" "\"testSessionId\",\"challenges\":[{\"challengeId\":1,\"challengeType\":" "\"TOTP\",\"status\":\"READY\"}, {\"challengeId\":2,\"challengeType\":" "\"AUTHZEN\"}]}"; vector challenges; ASSERT_FALSE(ParseJsonToChallenges(challenges_json, &challenges)); - EXPECT_EQ(challenges.size(), 1); + ASSERT_EQ(challenges.size(), 1); } } // namespace oslogin_utils - -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/packages/google-compute-engine-oslogin/utils/run_tests.sh b/packages/google-compute-engine-oslogin/utils/run_tests.sh deleted file mode 100755 index 83adcdc4..00000000 --- a/packages/google-compute-engine-oslogin/utils/run_tests.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# Copyright 2017 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Unit tests require gtest to be installed. -g++ -o test_runner oslogin_utils_test.cc oslogin_utils.cc -I/usr/include/json-c -lcurl -ljson-c -lgtest -lpthread -./test_runner -rm ./test_runner diff --git a/packages/google-compute-engine/packaging/debian/changelog b/packages/google-compute-engine/packaging/debian/changelog index 42a62e1c..a1b56733 100644 --- a/packages/google-compute-engine/packaging/debian/changelog +++ b/packages/google-compute-engine/packaging/debian/changelog @@ -1,8 +1,23 @@ +google-compute-engine (1:20190729.00-g1) stable; urgency=medium + + * Suport Google Private Access over IPv6. + * Switch to v1 guest attributes URL. + + -- Google Cloud Team Mon, 29 Jul 2019 10:07:29 -0700 + +google-compute-engine (1:20190708.00-g1) stable; urgency=medium + + * Drop unnecessary build and package dependencies. + * Log to journal and console directly from systemd service files. + * Update Debian build dependencies. + + -- Google Cloud Team Mon, 08 Jul 2019 10:20:15 -0700 + google-compute-engine (2.8.16-1) stable; urgency=low * Fix instance setup in Python 3 environments. - -- Google Cloud Team Tue, 22 May 2019 12:00:00 -0700 + -- Google Cloud Team Wed, 22 May 2019 12:00:00 -0700 google-compute-engine (2.8.15-1) stable; urgency=low diff --git a/packages/google-compute-engine/packaging/debian/compat b/packages/google-compute-engine/packaging/debian/compat index ec635144..f599e28b 100644 --- a/packages/google-compute-engine/packaging/debian/compat +++ b/packages/google-compute-engine/packaging/debian/compat @@ -1 +1 @@ -9 +10 diff --git a/packages/google-compute-engine/packaging/debian/control b/packages/google-compute-engine/packaging/debian/control index 3f647fe4..4b670f02 100644 --- a/packages/google-compute-engine/packaging/debian/control +++ b/packages/google-compute-engine/packaging/debian/control @@ -2,18 +2,18 @@ Source: google-compute-engine Section: admin Priority: optional Maintainer: Google Cloud Team -Build-Depends: debhelper (>= 9), - dh-systemd (>= 1.5) +Build-Depends: debhelper (>= 10) Standards-Version: 3.9.8 Homepage: https://github.com/GoogleCloudPlatform/compute-image-packages Package: google-compute-engine Architecture: all Depends: google-compute-engine-oslogin, + python-google-compute-engine, + python3-google-compute-engine, ${misc:Depends}, - rsyslog | system-log-daemon, systemd -Recommends: rsyslog +Recommends: rsyslog | system-log-daemon Provides: irqbalance Conflicts: google-compute-engine-jessie, google-compute-engine-init-jessie, diff --git a/packages/google-compute-engine/packaging/debian/google-compute-engine.links b/packages/google-compute-engine/packaging/debian/google-compute-engine.links new file mode 100644 index 00000000..a8ebeff9 --- /dev/null +++ b/packages/google-compute-engine/packaging/debian/google-compute-engine.links @@ -0,0 +1 @@ +usr/bin/google_set_hostname etc/dhcp/dhclient-exit-hooks.d/google_set_hostname diff --git a/packages/google-compute-engine/packaging/debian/install b/packages/google-compute-engine/packaging/debian/install index f95892be..b436f83f 100644 --- a/packages/google-compute-engine/packaging/debian/install +++ b/packages/google-compute-engine/packaging/debian/install @@ -1,5 +1,4 @@ etc/apt/apt.conf.d/* -etc/dhcp/dhclient-exit-hooks.d/* etc/modprobe.d/* etc/rsyslog.d/* etc/sysctl.d/* diff --git a/packages/google-compute-engine/packaging/debian/rules b/packages/google-compute-engine/packaging/debian/rules old mode 100644 new mode 100755 diff --git a/packages/google-compute-engine/packaging/google-compute-engine-el6.spec b/packages/google-compute-engine/packaging/google-compute-engine-el6.spec index a4c4084f..5555b204 100644 --- a/packages/google-compute-engine/packaging/google-compute-engine-el6.spec +++ b/packages/google-compute-engine/packaging/google-compute-engine-el6.spec @@ -13,15 +13,16 @@ # limitations under the License. Name: google-compute-engine +Epoch: 1 Version: %{_version} -Release: 1.el6 +Release: g1.el6 Summary: Google Compute Engine guest environment. License: ASL 2.0 Url: https://github.com/GoogleCloudPlatform/compute-image-packages Source0: %{name}_%{version}.orig.tar.gz Requires: curl Requires: google-compute-engine-oslogin -Requires: python-google-compute-engine = %{version} +Requires: python-google-compute-engine = 1:%{version} Requires: rsyslog # Old packages. Obsoletes: google-compute-engine-init @@ -44,6 +45,8 @@ specific to the Google Compute Engine cloud environment. cp -a src/{etc,usr} %{buildroot} install -d %{buildroot}/lib/ cp -a src/lib/udev %{buildroot}/lib +mkdir -p %{buildroot}/etc/dhcp +ln -sf /usr/bin/google_set_hostname %{buildroot}/etc/dhcp/dhclient-exit-hooks %files %defattr(0644,root,root,0755) diff --git a/packages/google-compute-engine/packaging/google-compute-engine.spec b/packages/google-compute-engine/packaging/google-compute-engine.spec index aba148d8..1ec37dca 100644 --- a/packages/google-compute-engine/packaging/google-compute-engine.spec +++ b/packages/google-compute-engine/packaging/google-compute-engine.spec @@ -18,8 +18,9 @@ %endif Name: google-compute-engine +Epoch: 1 Version: %{_version} -Release: 1%{?dist} +Release: g1%{?dist} Summary: Google Compute Engine guest environment. License: ASL 2.0 Url: https://github.com/GoogleCloudPlatform/compute-image-packages @@ -27,9 +28,9 @@ Source0: %{name}_%{version}.orig.tar.gz Requires: curl Requires: google-compute-engine-oslogin %if 0%{?rhel} == 8 -Requires: python3-google-compute-engine = %{version} +Requires: python3-google-compute-engine = 1:%{version} %else -Requires: python-google-compute-engine = %{version} +Requires: python-google-compute-engine = 1:%{version} %endif Requires: rsyslog diff --git a/packages/google-compute-engine/packaging/setup_deb.sh b/packages/google-compute-engine/packaging/setup_deb.sh index b03d2974..d4c84e9c 100755 --- a/packages/google-compute-engine/packaging/setup_deb.sh +++ b/packages/google-compute-engine/packaging/setup_deb.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="google-compute-engine" -VERSION="2.8.16" +VERSION="20190729.00" working_dir=${PWD} if [[ $(basename "$working_dir") != $NAME ]]; then diff --git a/packages/google-compute-engine/packaging/setup_rpm.sh b/packages/google-compute-engine/packaging/setup_rpm.sh index 8d26b4e5..119caaab 100755 --- a/packages/google-compute-engine/packaging/setup_rpm.sh +++ b/packages/google-compute-engine/packaging/setup_rpm.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="google-compute-engine" -VERSION="2.8.16" +VERSION="20190729.00" rpm_working_dir=/tmp/rpmpackage/${NAME}-${VERSION} working_dir=${PWD} diff --git a/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks b/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks deleted file mode 120000 index 577ef537..00000000 --- a/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/google_set_hostname \ No newline at end of file diff --git a/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks.d/google_set_hostname b/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks.d/google_set_hostname deleted file mode 120000 index 577ef537..00000000 --- a/packages/google-compute-engine/src/etc/dhcp/dhclient-exit-hooks.d/google_set_hostname +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/google_set_hostname \ No newline at end of file diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-accounts-daemon.service b/packages/google-compute-engine/src/lib/systemd/system/google-accounts-daemon.service index 9158be52..db627aaa 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-accounts-daemon.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-accounts-daemon.service @@ -1,13 +1,13 @@ [Unit] Description=Google Compute Engine Accounts Daemon After=network.target google-instance-setup.service google-network-daemon.service -Requires=network.target [Service] Type=simple ExecStart=/usr/bin/google_accounts_daemon OOMScoreAdjust=-999 Restart=always +StandardOutput=journal+console [Install] WantedBy=multi-user.target diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-clock-skew-daemon.service b/packages/google-compute-engine/src/lib/systemd/system/google-clock-skew-daemon.service index 71369f53..9339116c 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-clock-skew-daemon.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-clock-skew-daemon.service @@ -1,11 +1,11 @@ [Unit] Description=Google Compute Engine Clock Skew Daemon After=network.target google-instance-setup.service google-network-daemon.service -Requires=network.target [Service] Type=simple ExecStart=/usr/bin/google_clock_skew_daemon +StandardOutput=journal+console [Install] WantedBy=multi-user.target diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-instance-setup.service b/packages/google-compute-engine/src/lib/systemd/system/google-instance-setup.service index afa639af..ee987b2e 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-instance-setup.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-instance-setup.service @@ -1,12 +1,12 @@ [Unit] Description=Google Compute Engine Instance Setup -After=local-fs.target network-online.target network.target rsyslog.service +After=network-online.target network.target rsyslog.service Before=sshd.service -Wants=local-fs.target network-online.target network.target [Service] Type=oneshot ExecStart=/usr/bin/google_instance_setup +StandardOutput=journal+console [Install] WantedBy=sshd.service diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-network-daemon.service b/packages/google-compute-engine/src/lib/systemd/system/google-network-daemon.service index 74bf82e1..71745d43 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-network-daemon.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-network-daemon.service @@ -1,14 +1,13 @@ [Unit] Description=Google Compute Engine Network Daemon -After=local-fs.target network-online.target network.target rsyslog.service +After=network-online.target network.target After=google-instance-setup.service -Wants=local-fs.target network-online.target network.target -Requires=network.target PartOf=network.service [Service] Type=simple ExecStart=/usr/bin/google_network_daemon +StandardOutput=journal+console [Install] WantedBy=multi-user.target diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-shutdown-scripts.service b/packages/google-compute-engine/src/lib/systemd/system/google-shutdown-scripts.service index 069405ec..ae23d76f 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-shutdown-scripts.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-shutdown-scripts.service @@ -1,8 +1,7 @@ [Unit] Description=Google Compute Engine Shutdown Scripts -After=local-fs.target network-online.target network.target rsyslog.service systemd-resolved.service +After=network-online.target network.target rsyslog.service After=google-instance-setup.service google-network-daemon.service -Wants=local-fs.target network-online.target network.target [Service] ExecStart=/bin/true @@ -10,6 +9,7 @@ ExecStop=/usr/bin/google_metadata_script_runner --script-type shutdown Type=oneshot RemainAfterExit=true TimeoutStopSec=0 +StandardOutput=journal+console [Install] WantedBy=multi-user.target diff --git a/packages/google-compute-engine/src/lib/systemd/system/google-startup-scripts.service b/packages/google-compute-engine/src/lib/systemd/system/google-startup-scripts.service index 73ee8dbf..233298a8 100644 --- a/packages/google-compute-engine/src/lib/systemd/system/google-startup-scripts.service +++ b/packages/google-compute-engine/src/lib/systemd/system/google-startup-scripts.service @@ -1,13 +1,13 @@ [Unit] Description=Google Compute Engine Startup Scripts -After=local-fs.target network-online.target network.target rsyslog.service +After=network-online.target network.target rsyslog.service After=google-instance-setup.service google-network-daemon.service -Wants=local-fs.target network-online.target network.target [Service] ExecStart=/usr/bin/google_metadata_script_runner --script-type startup KillMode=process Type=oneshot +StandardOutput=journal+console [Install] WantedBy=multi-user.target diff --git a/packages/google-compute-engine/src/usr/lib/systemd/journald.conf.d/20-google.conf b/packages/google-compute-engine/src/usr/lib/systemd/journald.conf.d/20-google.conf deleted file mode 100644 index 6c620d56..00000000 --- a/packages/google-compute-engine/src/usr/lib/systemd/journald.conf.d/20-google.conf +++ /dev/null @@ -1,5 +0,0 @@ -# Google Compute Engine default console logging. -# This is not currently included in any packages, but may in the future. - -[Journal] -ForwardToConsole=yes diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/tests/utils_test.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/tests/utils_test.py index 6ff9f9cb..7f92795a 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/tests/utils_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/tests/utils_test.py @@ -26,6 +26,31 @@ def setUp(self): self.mock_logger = mock.Mock() self.mock_setup = mock.create_autospec(utils.Utils) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + @mock.patch('google_compute_engine.distro_lib.helpers.CallEnableRouteAdvertisements') + def testEnableIpv6(self, mock_call_enable_ra, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + mocks.attach_mock(mock_call_enable_ra, 'enable_ra') + + utils.Utils.EnableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.enable_ra(['A', 'B'], mock.ANY), + mock.call.dhclient(['A', 'B'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + def testDisableIpv6(self, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + + utils.Utils.DisableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.dhclient(['A', 'B'], mock.ANY, None, release_lease=True), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') def testEnableNetworkInterfaces(self, mock_call): mocks = mock.Mock() diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/utils.py index 9bdccb8c..7872d1b3 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_8/utils.py @@ -23,8 +23,27 @@ class Utils(utils.Utils): """Utilities used by Linux guest services on Debian 8.""" - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallEnableRouteAdvertisements(interfaces, logger) + helpers.CallDhclientIpv6(interfaces, logger) + + def DisableIpv6(self, interfaces, logger): + """Disable Ipv6 by giving up the DHCP lease using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallDhclientIpv6(interfaces, logger, None, release_lease=True) + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/tests/utils_test.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/tests/utils_test.py index 2717f008..48407534 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/tests/utils_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/tests/utils_test.py @@ -26,6 +26,31 @@ def setUp(self): self.mock_logger = mock.Mock() self.mock_setup = mock.create_autospec(utils.Utils) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + @mock.patch('google_compute_engine.distro_lib.helpers.CallEnableRouteAdvertisements') + def testEnableIpv6(self, mock_call_enable_ra, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + mocks.attach_mock(mock_call_enable_ra, 'enable_ra') + + utils.Utils.EnableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.enable_ra(['A', 'B'], mock.ANY), + mock.call.dhclient(['A', 'B'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + def testDisableIpv6(self, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + + utils.Utils.DisableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.dhclient(['A', 'B'], mock.ANY, None, release_lease=True), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') def testEnableNetworkInterfaces(self, mock_call): mocks = mock.Mock() diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/utils.py index 452e02f9..92552142 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/debian_9/utils.py @@ -23,8 +23,27 @@ class Utils(utils.Utils): """Utilities used by Linux guest services on Debian 9.""" - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallEnableRouteAdvertisements(interfaces, logger) + helpers.CallDhclientIpv6(interfaces, logger) + + def DisableIpv6(self, interfaces, logger): + """Disable Ipv6 by giving up the DHCP lease using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallDhclientIpv6(interfaces, logger, None, release_lease=True) + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/tests/utils_test.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/tests/utils_test.py index 539b059d..1dcbc604 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/tests/utils_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/tests/utils_test.py @@ -29,6 +29,31 @@ def setUp(self): def tearDown(self): pass + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + @mock.patch('google_compute_engine.distro_lib.helpers.CallEnableRouteAdvertisements') + def testEnableIpv6(self, mock_call_enable_ra, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + mocks.attach_mock(mock_call_enable_ra, 'enable_ra') + + utils.Utils.EnableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.enable_ra(['A', 'B'], mock.ANY), + mock.call.dhclient(['A', 'B'], mock.ANY, dhclient_script=None), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + def testDisableIpv6(self, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + + utils.Utils.DisableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.dhclient(['A', 'B'], mock.ANY, None, release_lease=True), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') def testEnableNetworkInterfaces(self, mock_call): mocks = mock.Mock() diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/utils.py index 20b8e2f6..9f9bb98a 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_6/utils.py @@ -21,10 +21,30 @@ class Utils(utils.Utils): - """Utilities used by Linux guest services on Debian 8.""" + """Utilities used by Linux guest services on EL 6.""" - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallEnableRouteAdvertisements(interfaces, logger) + helpers.CallDhclientIpv6( + interfaces, logger, dhclient_script=dhclient_script) + + def DisableIpv6(self, interfaces, logger): + """Disable Ipv6 by giving up the DHCP lease using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallDhclientIpv6(interfaces, logger, None, release_lease=True) + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/tests/utils_test.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/tests/utils_test.py index d726c3d2..61260b6f 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/tests/utils_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/tests/utils_test.py @@ -96,6 +96,31 @@ def testDisableNetworkManager(self, mock_exists): ] self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + @mock.patch('google_compute_engine.distro_lib.helpers.CallEnableRouteAdvertisements') + def testEnableIpv6(self, mock_call_enable_ra, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + mocks.attach_mock(mock_call_enable_ra, 'enable_ra') + + utils.Utils.EnableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.enable_ra(['A', 'B'], mock.ANY), + mock.call.dhclient(['A', 'B'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclientIpv6') + def testDisableIpv6(self, mock_call_dhclient): + mocks = mock.Mock() + mocks.attach_mock(mock_call_dhclient, 'dhclient') + + utils.Utils.DisableIpv6(self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [ + mock.call.dhclient(['A', 'B'], mock.ANY, None, release_lease=True), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.el_7.utils.os.path.exists') @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') def testEnableNetworkInterfaces(self, mock_call, mock_exists): diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/utils.py index b11a98f9..e0e318bf 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/el_7/utils.py @@ -30,8 +30,27 @@ class Utils(utils.Utils): network_path = constants.LOCALBASE + '/etc/sysconfig/network-scripts' - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallEnableRouteAdvertisements(interfaces, logger) + helpers.CallDhclientIpv6(interfaces, logger) + + def DisableIpv6(self, interfaces, logger): + """Disable Ipv6 by giving up the DHCP lease using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallDhclientIpv6(interfaces, logger, None, release_lease=True) + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/freebsd_11/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/freebsd_11/utils.py index 4a9e7062..2734c8fb 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/freebsd_11/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/freebsd_11/utils.py @@ -23,8 +23,17 @@ class Utils(utils.Utils): """Utilities used by Linux guest services on FreeBSD 11.""" - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + pass + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/helpers.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/helpers.py index 93902b33..b7d16a10 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/helpers.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/helpers.py @@ -42,6 +42,54 @@ def CallDhclient( logger.warning('Could not enable interfaces %s.', interfaces) +def CallDhclientIpv6(interfaces, logger, dhclient_script=None, + release_lease=False): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + release_lease: Release the IPv6 lease. + """ + logger.info('Calling Dhclient for IPv6 configuration ' + 'on the Ethernet interfaces %s.', interfaces) + + timeout_command = ['timeout', '5'] + dhclient_command = ['dhclient'] + + if release_lease: + try: + subprocess.check_call( + timeout_command + dhclient_command + [ + '-6', '-r', '-v'] + interfaces) + except subprocess.CalledProcessError: + logger.warning('Could not release IPv6 lease on interface %s.', + interfaces) + return + + if dhclient_script and os.path.exists(dhclient_script): + dhclient_command += ['-sf', dhclient_script] + + try: + subprocess.check_call( + timeout_command + dhclient_command + ['-1', '-6', '-v'] + interfaces) + except subprocess.CalledProcessError: + logger.warning('Could not enable IPv6 on interface %s.', interfaces) + + +def CallEnableRouteAdvertisements(interfaces, logger): + """Enable route advertisements. + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + """ + for interface in interfaces: + accept_ra = ( + 'net.ipv6.conf.{interface}.accept_ra_rt_info_max_plen'.format( + interface=interface)) + CallSysctl(logger, accept_ra, 128) + def CallHwclock(logger): """Sync clock using hwclock. @@ -75,3 +123,20 @@ def CallNtpdate(logger): logger.warning('Failed to sync system time with ntp server.') else: logger.info('Synced system time with ntp server.') + +def CallSysctl(logger, name, value): + """Write a variable using sysctl. + + Args: + logger: logger object, used to write to SysLog and serial port. + name: string name of the sysctl variable. + value: value of the sysctl variable. + """ + logger.info('Configuring sysctl %s.', name) + + sysctl_command = [ + 'sysctl', '-w', '{name}={value}'.format(name=name, value=value)] + try: + subprocess.check_call(sysctl_command) + except subprocess.CalledProcessError: + logger.warning('Unable to configure sysctl %s.', name) diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_11/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_11/utils.py index 6d1d0f2a..623505ce 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_11/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_11/utils.py @@ -27,8 +27,17 @@ class Utils(utils.Utils): """Utilities used by Linux guest services on SUSE 11.""" - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + pass + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_12/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_12/utils.py index 380df7bd..b4a3d00a 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_12/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/sles_12/utils.py @@ -29,8 +29,17 @@ class Utils(utils.Utils): network_path = constants.LOCALBASE + '/etc/sysconfig/network' - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Configure the network interfaces for IPv6 using dhclient. + + Args: + interface: string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + pass + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/tests/helpers_test.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/tests/helpers_test.py index 7533b200..6afca577 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/tests/helpers_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/tests/helpers_test.py @@ -65,6 +65,92 @@ def testCallDhclient(self, mock_call, mock_exists): self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.distro_lib.helpers.os.path.exists') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallDhclientIpv6NonExistentScript(self, mock_call, mock_exists): + mock_logger = mock.Mock() + + mock_exists.side_effect = [False] + helpers.CallDhclientIpv6(['a', 'b'], mock_logger, 'test_script') + mock_call.assert_has_calls( + [ + mock.call.call( + ['timeout', '5', 'dhclient', '-1', '-6', '-v', 'a', 'b']), + ]) + + @mock.patch('google_compute_engine.distro_lib.helpers.os.path.exists') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallDhclientIpv6(self, mock_call, mock_exists): + mock_logger = mock.Mock() + mock_exists.side_effect = [True] + mock_call.side_effect = [ + None, + None, + subprocess.CalledProcessError(1, 'Test'), + None, + None, + subprocess.CalledProcessError(1, 'Test'), + ] + + helpers.CallDhclientIpv6(['a', 'b'], mock_logger, 'test_script') + helpers.CallDhclientIpv6(['c', 'd'], mock_logger, None) + helpers.CallDhclientIpv6(['e', 'f'], mock_logger, None) + helpers.CallDhclientIpv6( + ['g', 'h'], mock_logger, 'test_script', release_lease=True) + helpers.CallDhclientIpv6(['i', 'j'], mock_logger, None, release_lease=True) + helpers.CallDhclientIpv6(['k', 'l'], mock_logger, None, release_lease=True) + + expected_calls = [ + mock.call.call( + [ + 'timeout', '5','dhclient', '-sf', 'test_script', '-1', '-6', + '-v', 'a', 'b', + ]), + mock.call.call( + [ + 'timeout', '5', 'dhclient', '-1', '-6', '-v', 'c', 'd', + ]), + mock.call.call( + [ + 'timeout', '5', 'dhclient', '-1', '-6', '-v', 'e', 'f', + ]), + mock.call.call( + [ + 'timeout', '5', 'dhclient', '-6', '-r', '-v', 'g', 'h', + ]), + mock.call.call( + [ + 'timeout', '5', 'dhclient', '-6', '-r', '-v', 'i', 'j', + ]), + mock.call.call( + [ + 'timeout', '5', 'dhclient', '-6', '-r', '-v', 'k', 'l', + ]), + ] + + self.assertEqual(mock_call.mock_calls, expected_calls) + mock_logger.assert_has_calls( + [ + mock.call.warning(mock.ANY, ['e', 'f']), + ]) + mock_logger.assert_has_calls( + [ + mock.call.warning(mock.ANY, ['k', 'l']), + ]) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testEnableRouteAdvertisements(self, mock_call): + mock_logger = mock.Mock() + interfaces = ['foo', 'bar', 'baz'] + helpers.CallEnableRouteAdvertisements(interfaces, mock_logger) + mock_call.assert_has_calls([ + mock.call( + [ + 'sysctl', '-w', + 'net.ipv6.conf.%s.accept_ra_rt_info_max_plen=128' % interface, + ]) + for interface in interfaces]) + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') def testCallHwclock(self, mock_call): command = ['/sbin/hwclock', '--hctosys'] @@ -135,3 +221,20 @@ def testCallNtpdateError(self, mock_call, mock_check_call): mock_check_call.assert_called_once_with(command_ntpdate, shell=True) expected_calls = [mock.call.warning(mock.ANY)] self.assertEqual(mock_logger.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallSysctl(self, mock_call): + command = ['sysctl', '-w'] + mock_logger = mock.Mock() + expected_log_calls = [] + for name in ['foo', 'bar', 'baz']: + for value in ['foo', 'bar', 'baz']: + params = ['{name}={value}'.format(name=name, value=value)] + helpers.CallSysctl(mock_logger, name, value) + mock_call.assert_called_with(command + params) + expected_log_calls.append(mock.call.info(mock.ANY, name)) + self.assertEqual(mock_logger.mock_calls, expected_log_calls) + + mock_call.side_effect = subprocess.CalledProcessError(1, 'Test') + helpers.CallSysctl(mock_logger, 'fail', 1) + mock_logger.assert_has_calls([mock.call.warning(mock.ANY, 'fail')]) diff --git a/packages/python-google-compute-engine/google_compute_engine/distro_lib/utils.py b/packages/python-google-compute-engine/google_compute_engine/distro_lib/utils.py index 75b656e5..df1d45cd 100644 --- a/packages/python-google-compute-engine/google_compute_engine/distro_lib/utils.py +++ b/packages/python-google-compute-engine/google_compute_engine/distro_lib/utils.py @@ -27,8 +27,17 @@ def __init__(self, debug=False): """ self.debug = debug - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): + def EnableIpv6(self, interfaces, logger, dhclient_script=None): + """Enable IPv6 on the list of network interfaces. + + Args: + interfaces: list of string, the output device names for enabling IPv6. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + pass + + def EnableNetworkInterfaces(self, interfaces, logger, dhclient_script=None): """Enable the list of network interfaces. Args: diff --git a/packages/python-google-compute-engine/google_compute_engine/instance_setup/instance_setup.py b/packages/python-google-compute-engine/google_compute_engine/instance_setup/instance_setup.py index d65f6423..cb1a2a6d 100755 --- a/packages/python-google-compute-engine/google_compute_engine/instance_setup/instance_setup.py +++ b/packages/python-google-compute-engine/google_compute_engine/instance_setup/instance_setup.py @@ -38,7 +38,7 @@ def get_method(self): return 'PUT' -GUEST_ATTRIBUTES_URL = ('http://metadata.google.internal/computeMetadata/v1beta1/' +GUEST_ATTRIBUTES_URL = ('http://metadata.google.internal/computeMetadata/v1/' 'instance/guest-attributes') HOSTKEY_NAMESPACE = 'hostkeys' diff --git a/packages/python-google-compute-engine/google_compute_engine/instance_setup/tests/instance_setup_test.py b/packages/python-google-compute-engine/google_compute_engine/instance_setup/tests/instance_setup_test.py index 4cc33cfb..a4182199 100644 --- a/packages/python-google-compute-engine/google_compute_engine/instance_setup/tests/instance_setup_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/instance_setup/tests/instance_setup_test.py @@ -336,7 +336,7 @@ def testWriteHostKeyToGuestAttributes(self, mock_put, mock_urlopen): key_type = 'ssh-rsa' key_value = 'asdfasdf' encoded_key_value = key_value.encode('utf-8') - expected_url = ('http://metadata.google.internal/computeMetadata/v1beta1/' + expected_url = ('http://metadata.google.internal/computeMetadata/v1/' 'instance/guest-attributes/hostkeys/%s' % key_type) headers = {'Metadata-Flavor': 'Google'} diff --git a/packages/python-google-compute-engine/google_compute_engine/networking/network_daemon.py b/packages/python-google-compute-engine/google_compute_engine/networking/network_daemon.py index 95b190b4..e152464e 100644 --- a/packages/python-google-compute-engine/google_compute_engine/networking/network_daemon.py +++ b/packages/python-google-compute-engine/google_compute_engine/networking/network_daemon.py @@ -90,6 +90,11 @@ def HandleNetworkInterfaces(self, result): network_interfaces = self._ExtractInterfaceMetadata(result) if self.network_setup_enabled: + default_interface = network_interfaces[0] + if default_interface.ipv6: + self.network_setup.EnableIpv6([default_interface.name]) + else: + self.network_setup.DisableIpv6([default_interface.name]) self.network_setup.EnableNetworkInterfaces( [interface.name for interface in network_interfaces[1:]]) @@ -119,7 +124,9 @@ def _ExtractInterfaceMetadata(self, metadata): if self.target_instance_ips: ip_addresses.extend(network_interface.get('targetInstanceIps', [])) interfaces.append(NetworkDaemon.NetworkInterface( - interface, ip_addresses, network_interface.get('ip', []))) + interface, forwarded_ips=ip_addresses, + ip=network_interface.get('ip', None), + ipv6='dhcpv6Refresh' in network_interface.keys())) else: message = 'Network interface not found for MAC address: %s.' self.logger.warning(message, mac_address) @@ -128,10 +135,11 @@ def _ExtractInterfaceMetadata(self, metadata): class NetworkInterface(object): """Network interface information extracted from metadata.""" - def __init__(self, name, forwarded_ips=None, ip=None): + def __init__(self, name, forwarded_ips=None, ip=None, ipv6=False): self.name = name self.forwarded_ips = forwarded_ips self.ip = ip + self.ipv6 = ipv6 def main(): diff --git a/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/network_setup.py b/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/network_setup.py index 91667223..b92bed1b 100755 --- a/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/network_setup.py +++ b/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/network_setup.py @@ -42,6 +42,45 @@ def __init__(self, dhclient_script=None, dhcp_command=None, debug=False): self.logger = logger.Logger( name='network-setup', debug=debug, facility=facility) self.distro_utils = distro_utils.Utils(debug=debug) + self.ipv6_initialized = False + self.ipv6_interfaces = set() + + def EnableIpv6(self, interfaces): + """Enable IPv6 on the list of network interfaces. + + Args: + interfaces: list of string, the output device names for enabling IPv6. + """ + if not interfaces or self.ipv6_interfaces == set(interfaces): + return + + self.logger.info('Enabling IPv6 on Ethernet interface: %s.', interfaces) + self.ipv6_interfaces = self.ipv6_interfaces.union(set(interfaces)) + self.ipv6_initialized = True + + # Distro-specific setup for enabling IPv6 on network interfaces. + self.distro_utils.EnableIpv6( + interfaces, self.logger, dhclient_script=self.dhclient_script) + + def DisableIpv6(self, interfaces): + """Disable IPv6 on the list of network interfaces. + + Args: + interfaces: list of string, the output device names for disabling IPv6. + """ + # Allow to run once during Initialization and after that only when an + # interface is found in the ipv6_interfaces set. + if not interfaces or ( + self.ipv6_initialized and not self.ipv6_interfaces.intersection( + set(interfaces))): + return + + self.logger.info('Disabling IPv6 on Ethernet interface: %s.', interfaces) + self.ipv6_interfaces.difference_update(interfaces) + self.ipv6_initialized = True + + # Distro-specific setup for disabling IPv6 on network interfaces. + self.distro_utils.DisableIpv6(interfaces, self.logger) def EnableNetworkInterfaces(self, interfaces): """Enable the list of network interfaces. diff --git a/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/tests/network_setup_test.py b/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/tests/network_setup_test.py index 644510aa..10fe2c5f 100644 --- a/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/tests/network_setup_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/networking/network_setup/tests/network_setup_test.py @@ -27,61 +27,154 @@ class NetworkSetupTest(unittest.TestCase): def setUp(self): self.mock_logger = mock.Mock() self.mock_distro_utils = mock.Mock() - self.mock_setup = mock.create_autospec(network_setup.NetworkSetup) - self.mock_setup.logger = self.mock_logger - self.mock_setup.distro_utils = self.mock_distro_utils - self.mock_setup.dhclient_script = '/bin/script' - self.mock_setup.dhcp_command = '' - - @mock.patch('google_compute_engine.networking.network_setup.network_setup.logger') - def testNetworkSetup(self, mock_logger): - mock_logger_instance = mock.Mock() - mock_logger.Logger.return_value = mock_logger_instance + self.dhclient_script = '/bin/script' + self.dhcp_command = '' + self.setup = network_setup.NetworkSetup( + dhclient_script=self.dhclient_script, dhcp_command=self.dhcp_command, + debug=False) + self.setup.distro_utils = self.mock_distro_utils + self.setup.logger = self.mock_logger + + @mock.patch('google_compute_engine.networking.network_setup.network_setup.subprocess.check_call') + def testEnableIpv6(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + mocks.attach_mock(self.mock_logger, 'logger') + mocks.attach_mock(self.mock_distro_utils.EnableIpv6, 'enable') + mock_call.side_effect = [None, subprocess.CalledProcessError(1, 'Test')] + + # Return immediately with no interfaces. + network_setup.NetworkSetup.EnableIpv6(self.setup, None) + network_setup.NetworkSetup.EnableIpv6(self.setup, []) + # Enable interfaces. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A', 'B']) + self.assertEqual(self.setup.ipv6_interfaces, set(['A', 'B'])) + # Add a new interface. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A', 'B', 'C']) + self.assertEqual(self.setup.ipv6_interfaces, set(['A', 'B', 'C'])) + # Interfaces are already enabled, do nothing. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A', 'B', 'C']) + self.assertEqual(self.setup.ipv6_interfaces, set(['A', 'B', 'C'])) + expected_calls = [ + mock.call.logger.info(mock.ANY, ['A', 'B']), + mock.call.enable(['A', 'B'], mock.ANY, dhclient_script='/bin/script'), + mock.call.logger.info(mock.ANY, ['A', 'B', 'C']), + mock.call.enable( + ['A', 'B', 'C'], mock.ANY, dhclient_script='/bin/script'), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.networking.network_setup.network_setup.subprocess.check_call') + def testDisableIpv6(self, mock_call): mocks = mock.Mock() - mocks.attach_mock(mock_logger, 'logger') - with mock.patch.object( - network_setup.NetworkSetup, 'EnableNetworkInterfaces'): + mocks.attach_mock(mock_call, 'call') + mocks.attach_mock(self.mock_logger, 'logger') + mocks.attach_mock(self.mock_distro_utils.EnableIpv6, 'enable') + mocks.attach_mock(self.mock_distro_utils.DisableIpv6, 'disable') + expected_calls = [] - network_setup.NetworkSetup(['A', 'B'], debug=True) - expected_calls = [ - mock.call.logger.Logger(name=mock.ANY, debug=True, facility=mock.ANY), - ] - self.assertEqual(mocks.mock_calls, expected_calls) + # Clean run, run disable once e.g. at boot. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A']) + self.assertEqual(self.setup.ipv6_interfaces, set([])) + # No more disables allowed, have to follow the contract of Enable and then + # Disable. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A']) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A']), + mock.call.disable(['A'], mock.ANY), + ]) + # Enable interfaces. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A', 'B', 'C']) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A', 'B', 'C']), + mock.call.enable( + ['A', 'B', 'C'], mock.ANY, dhclient_script='/bin/script'), + ]) + # Remove interface. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A']) + self.assertEqual(self.setup.ipv6_interfaces, set(['B', 'C'])) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A']), + mock.call.disable(['A'], mock.ANY), + ]) + + # Add it back. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A']) + self.assertEqual(self.setup.ipv6_interfaces, set(['A', 'B', 'C'])) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A']), + mock.call.enable(['A'], mock.ANY, dhclient_script='/bin/script'), + ]) + + # Remove list. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A', 'B']) + self.assertEqual(self.setup.ipv6_interfaces, set(['C'])) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A', 'B']), + mock.call.disable(['A', 'B'], mock.ANY), + ]) + + # Try removing again, these are no ops. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A']) + network_setup.NetworkSetup.DisableIpv6(self.setup, ['A', 'B']) + + # Remove the last element. + network_setup.NetworkSetup.DisableIpv6(self.setup, ['C']) + self.assertEqual(self.setup.ipv6_interfaces, set([])) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['C']), + mock.call.disable(['C'], mock.ANY), + ]) + + # Empty list, allow adds back again. + network_setup.NetworkSetup.EnableIpv6(self.setup, ['A']) + self.assertEqual(self.setup.ipv6_interfaces, set(['A'])) + expected_calls.extend( + [ + mock.call.logger.info(mock.ANY, ['A']), + mock.call.enable(['A'], mock.ANY, dhclient_script='/bin/script'), + ]) + self.assertEqual(mocks.mock_calls, expected_calls) @mock.patch('google_compute_engine.networking.network_setup.network_setup.subprocess.check_call') def testEnableNetworkInterfaces(self, mock_call): mocks = mock.Mock() mocks.attach_mock(mock_call, 'call') mocks.attach_mock(self.mock_logger, 'logger') - mocks.attach_mock( - self.mock_setup.distro_utils.EnableNetworkInterfaces, 'enable') + mocks.attach_mock(self.mock_distro_utils.EnableNetworkInterfaces, 'enable') mock_call.side_effect = [None, subprocess.CalledProcessError(1, 'Test')] - # Return immediately with fewer than two interfaces. - network_setup.NetworkSetup.EnableNetworkInterfaces(self.mock_setup, None) - network_setup.NetworkSetup.EnableNetworkInterfaces(self.mock_setup, []) + # Return immediately with no interfaces. + network_setup.NetworkSetup.EnableNetworkInterfaces(self.setup, None) + network_setup.NetworkSetup.EnableNetworkInterfaces(self.setup, []) # Enable interfaces. network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B']) - self.assertEqual(self.mock_setup.interfaces, set(['A', 'B'])) + self.setup, ['A', 'B']) + self.assertEqual(self.setup.interfaces, set(['A', 'B'])) # Add a new interface. network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B', 'C']) - self.assertEqual(self.mock_setup.interfaces, set(['A', 'B', 'C'])) + self.setup, ['A', 'B', 'C']) + self.assertEqual(self.setup.interfaces, set(['A', 'B', 'C'])) # Interfaces are already enabled. network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B', 'C']) - self.assertEqual(self.mock_setup.interfaces, set(['A', 'B', 'C'])) + self.setup, ['A', 'B', 'C']) + self.assertEqual(self.setup.interfaces, set(['A', 'B', 'C'])) # Run a user supplied command successfully. - self.mock_setup.dhcp_command = 'success' + self.setup.dhcp_command = 'success' network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['D', 'E']) - self.assertEqual(self.mock_setup.interfaces, set(['D', 'E'])) + self.setup, ['D', 'E']) + self.assertEqual(self.setup.interfaces, set(['D', 'E'])) # Run a user supplied command and logger error messages. - self.mock_setup.dhcp_command = 'failure' + self.setup.dhcp_command = 'failure' network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['F', 'G']) - self.assertEqual(self.mock_setup.interfaces, set(['F', 'G'])) + self.setup, ['F', 'G']) + self.assertEqual(self.setup.interfaces, set(['F', 'G'])) expected_calls = [ mock.call.logger.info(mock.ANY, ['A', 'B']), mock.call.enable(['A', 'B'], mock.ANY, dhclient_script='/bin/script'), diff --git a/packages/python-google-compute-engine/google_compute_engine/networking/tests/network_daemon_test.py b/packages/python-google-compute-engine/google_compute_engine/networking/tests/network_daemon_test.py index acf0371e..9216ea37 100644 --- a/packages/python-google-compute-engine/google_compute_engine/networking/tests/network_daemon_test.py +++ b/packages/python-google-compute-engine/google_compute_engine/networking/tests/network_daemon_test.py @@ -139,8 +139,36 @@ def testHandleNetworkInterfaces(self): self.mock_setup.ip_forwarding_enabled = True self.mock_setup.network_setup_enabled = True self.mock_setup._ExtractInterfaceMetadata.return_value = [ - network_daemon.NetworkDaemon.NetworkInterface('a'), - network_daemon.NetworkDaemon.NetworkInterface('b'), + network_daemon.NetworkDaemon.NetworkInterface( + 'eth0', forwarded_ips=['a'], ip='1.1.1.1', ipv6=False), + network_daemon.NetworkDaemon.NetworkInterface('eth1'), + ] + result = mock.Mock() + + network_daemon.NetworkDaemon.HandleNetworkInterfaces( + self.mock_setup, result) + expected_calls = [ + mock.call.setup._ExtractInterfaceMetadata(result), + mock.call.network_setup.DisableIpv6(['eth0']), + mock.call.network_setup.EnableNetworkInterfaces(['eth1']), + mock.call.forwarding.HandleForwardedIps( + 'eth0', ['a'], '1.1.1.1'), + mock.call.forwarding.HandleForwardedIps('eth1', None, None), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + def testHandleNetworkInterfacesIpv6(self): + mocks = mock.Mock() + mocks.attach_mock(self.mock_ip_forwarding, 'forwarding') + mocks.attach_mock(self.mock_network_setup, 'network_setup') + mocks.attach_mock(self.mock_setup, 'setup') + self.mock_setup.ip_aliases = None + self.mock_setup.target_instance_ips = None + self.mock_setup.ip_forwarding_enabled = True + self.mock_setup.network_setup_enabled = True + self.mock_setup._ExtractInterfaceMetadata.return_value = [ + network_daemon.NetworkDaemon.NetworkInterface( + 'eth0', forwarded_ips=['a'], ip='1.1.1.1', ipv6=True), ] result = mock.Mock() @@ -148,9 +176,36 @@ def testHandleNetworkInterfaces(self): self.mock_setup, result) expected_calls = [ mock.call.setup._ExtractInterfaceMetadata(result), - mock.call.network_setup.EnableNetworkInterfaces(['b']), - mock.call.forwarding.HandleForwardedIps('a', None, None), - mock.call.forwarding.HandleForwardedIps('b', None, None), + mock.call.network_setup.EnableIpv6(['eth0']), + mock.call.network_setup.EnableNetworkInterfaces([]), + mock.call.forwarding.HandleForwardedIps( + 'eth0', ['a'], '1.1.1.1'), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + def testHandleNetworkInterfacesIpv6Disabled(self): + mocks = mock.Mock() + mocks.attach_mock(self.mock_ip_forwarding, 'forwarding') + mocks.attach_mock(self.mock_network_setup, 'network_setup') + mocks.attach_mock(self.mock_setup, 'setup') + self.mock_setup.ip_aliases = None + self.mock_setup.target_instance_ips = None + self.mock_setup.ip_forwarding_enabled = True + self.mock_setup.network_setup_enabled = True + self.mock_setup._ExtractInterfaceMetadata.return_value = [ + network_daemon.NetworkDaemon.NetworkInterface( + 'eth0', forwarded_ips=['a'], ip='1.1.1.1', ipv6=False), + ] + result = mock.Mock() + + network_daemon.NetworkDaemon.HandleNetworkInterfaces( + self.mock_setup, result) + expected_calls = [ + mock.call.setup._ExtractInterfaceMetadata(result), + mock.call.network_setup.DisableIpv6(['eth0']), + mock.call.network_setup.EnableNetworkInterfaces([]), + mock.call.forwarding.HandleForwardedIps( + 'eth0', ['a'], '1.1.1.1'), ] self.assertEqual(mocks.mock_calls, expected_calls) @@ -187,12 +242,15 @@ def testExtractInterfaceMetadata(self): { 'mac': '1', 'forwardedIps': ['a'], + 'dhcpv6Refresh': 1, }, { 'mac': '2', 'forwardedIps': ['b'], 'ipAliases': ['banana'], 'targetInstanceIps': ['baklava'], + 'ip': '2.2.2.2', + 'dhcpv6Refresh': 2, }, { 'mac': '3', @@ -209,11 +267,13 @@ def testExtractInterfaceMetadata(self): }, ] expected_interfaces = [ - network_daemon.NetworkDaemon.NetworkInterface('eth0', ['a']), network_daemon.NetworkDaemon.NetworkInterface( - 'eth1', ['b', 'banana', 'baklava']), + 'eth0', forwarded_ips=['a'], ip=None, ipv6=True), + network_daemon.NetworkDaemon.NetworkInterface( + 'eth1', forwarded_ips=['b', 'banana', 'baklava'], ip='2.2.2.2', + ipv6=True), network_daemon.NetworkDaemon.NetworkInterface( - 'eth2', ['cherry', 'cake']), + 'eth2', forwarded_ips=['cherry', 'cake'], ip=None), ] actual_interfaces = network_daemon.NetworkDaemon._ExtractInterfaceMetadata( @@ -221,6 +281,8 @@ def testExtractInterfaceMetadata(self): for actual, expected in zip(actual_interfaces, expected_interfaces): self.assertEqual(actual.name, expected.name) self.assertEqual(actual.forwarded_ips, expected.forwarded_ips) + self.assertEqual(actual.ip, expected.ip) + self.assertEqual(actual.ipv6, expected.ipv6) def testExtractInterfaceMetadataWithoutOptions(self): self.mock_setup.ip_aliases = None @@ -233,12 +295,15 @@ def testExtractInterfaceMetadataWithoutOptions(self): { 'mac': '1', 'forwardedIps': ['a'], + 'dhcpv6Refresh': 1, }, { 'mac': '2', 'forwardedIps': ['b'], 'ipAliases': ['banana'], 'targetInstanceIps': ['baklava'], + 'ip': '2.2.2.2', + 'dhcpv6Refresh': 2, }, { 'mac': '3', @@ -247,9 +312,12 @@ def testExtractInterfaceMetadataWithoutOptions(self): }, ] expected_interfaces = [ - network_daemon.NetworkDaemon.NetworkInterface('eth0', ['a']), - network_daemon.NetworkDaemon.NetworkInterface('eth1', ['b']), - network_daemon.NetworkDaemon.NetworkInterface('eth2', []), + network_daemon.NetworkDaemon.NetworkInterface( + 'eth0', forwarded_ips=['a'], ip=None, ipv6=True), + network_daemon.NetworkDaemon.NetworkInterface( + 'eth1', forwarded_ips=['b'], ip='2.2.2.2', ipv6=True), + network_daemon.NetworkDaemon.NetworkInterface( + 'eth2', forwarded_ips=[], ip=None, ipv6=False), ] actual_interfaces = network_daemon.NetworkDaemon._ExtractInterfaceMetadata( @@ -257,3 +325,5 @@ def testExtractInterfaceMetadataWithoutOptions(self): for actual, expected in zip(actual_interfaces, expected_interfaces): self.assertEqual(actual.name, expected.name) self.assertEqual(actual.forwarded_ips, expected.forwarded_ips) + self.assertEqual(actual.ip, expected.ip) + self.assertEqual(actual.ipv6, expected.ipv6) diff --git a/packages/python-google-compute-engine/packaging/debian/changelog b/packages/python-google-compute-engine/packaging/debian/changelog index 55936210..e919d55c 100644 --- a/packages/python-google-compute-engine/packaging/debian/changelog +++ b/packages/python-google-compute-engine/packaging/debian/changelog @@ -1,8 +1,22 @@ +python-google-compute-engine (1:20190729.00-g1) stable; urgency=medium + + * Support for Google Private Access over IPv6. + + -- Google Cloud Team Mon, 29 Jul 2019 10:09:24 -0700 + +python-google-compute-engine (1:20190708.00-g1) stable; urgency=medium + + * Move Debian entry point scripts to python3. + * Update Debian build dependencies. + * Drop unnecessary build and package dependencies. + + -- Google Cloud Team Mon, 08 Jul 2019 10:20:44 -0700 + python-google-compute-engine (2.8.16-1) stable; urgency=low * Fix guest attributes flow in Python 3. - -- Google Cloud Team Tue, 22 May 2019 12:00:00 -0700 + -- Google Cloud Team Wed, 22 May 2019 12:00:00 -0700 python-google-compute-engine (2.8.15-1) stable; urgency=low diff --git a/packages/python-google-compute-engine/packaging/debian/compat b/packages/python-google-compute-engine/packaging/debian/compat index ec635144..f599e28b 100644 --- a/packages/python-google-compute-engine/packaging/debian/compat +++ b/packages/python-google-compute-engine/packaging/debian/compat @@ -1 +1 @@ -9 +10 diff --git a/packages/python-google-compute-engine/packaging/debian/control b/packages/python-google-compute-engine/packaging/debian/control index e54871af..d6491d08 100644 --- a/packages/python-google-compute-engine/packaging/debian/control +++ b/packages/python-google-compute-engine/packaging/debian/control @@ -2,18 +2,17 @@ Source: python-google-compute-engine Section: admin Priority: optional Maintainer: Google Cloud Team -Build-Depends: debhelper (>= 9), +Build-Depends: debhelper (>= 10), dh-python, python-all, + python-boto, + python-mock, + python-pytest, python-setuptools, python3-all, - python3-setuptools, - python-pytest, + python3-distro, python3-pytest, - python-mock, - python-boto, - python3-boto, - python3-distro + python3-setuptools Standards-Version: 3.9.8 Homepage: https://github.com/GoogleCloudPlatform/compute-image-packages @@ -26,17 +25,14 @@ Conflicts: google-compute-engine-jessie, google-compute-daemon, google-startup-scripts Description: Google Compute Engine python library for Python 2.x. + Python libraries used for interacting with Google Compute Engine instance + metadata to provide platform integration. Package: python3-google-compute-engine Section: python Architecture: all -Depends: ${misc:Depends}, - python3-distro | python3-distro-info, - python3-boto, - python3-pkg-resources, - python3:any -Conflicts: google-compute-engine-jessie, - google-compute-engine-stretch, - google-compute-daemon, - google-startup-scripts +Depends: ${python3:Depends}, ${misc:Depends}, + python3-distro, python3-pkg-resources, python3:any Description: Google Compute Engine python library for Python 3.x. + Python libraries used for interacting with Google Compute Engine instance + metadata to provide platform integration. diff --git a/packages/python-google-compute-engine/packaging/debian/rules b/packages/python-google-compute-engine/packaging/debian/rules index 553f920f..b7999dda 100755 --- a/packages/python-google-compute-engine/packaging/debian/rules +++ b/packages/python-google-compute-engine/packaging/debian/rules @@ -14,13 +14,5 @@ override_dh_clean: override_dh_auto_install: dh_auto_install - mv debian/python3-google-compute-engine/usr/bin/google_instance_setup \ - debian/python3-google-compute-engine/usr/bin/google_instance_setup3 - mv debian/python3-google-compute-engine/usr/bin/google_accounts_daemon \ - debian/python3-google-compute-engine/usr/bin/google_accounts_daemon3 - mv debian/python3-google-compute-engine/usr/bin/google_network_daemon \ - debian/python3-google-compute-engine/usr/bin/google_network_daemon3 - mv debian/python3-google-compute-engine/usr/bin/google_clock_skew_daemon \ - debian/python3-google-compute-engine/usr/bin/google_clock_skew_daemon3 - mv debian/python3-google-compute-engine/usr/bin/google_metadata_script_runner \ - debian/python3-google-compute-engine/usr/bin/google_metadata_script_runner3 + # Remove python2.7 entry points. + rm -Rf debian/python-google-compute-engine/usr/bin diff --git a/packages/python-google-compute-engine/packaging/python-google-compute-engine.spec b/packages/python-google-compute-engine/packaging/python-google-compute-engine.spec index f36bed32..1984cec4 100644 --- a/packages/python-google-compute-engine/packaging/python-google-compute-engine.spec +++ b/packages/python-google-compute-engine/packaging/python-google-compute-engine.spec @@ -18,8 +18,9 @@ %endif Name: python-google-compute-engine +Epoch: 1 Version: %{_version} -Release: 1%{?dist} +Release: g1%{?dist} Summary: Google Compute Engine python library License: ASL 2.0 Url: https://github.com/GoogleCloudPlatform/compute-image-packages diff --git a/packages/python-google-compute-engine/packaging/python3-google-compute-engine.spec b/packages/python-google-compute-engine/packaging/python3-google-compute-engine.spec index 6bf3291a..69257d95 100644 --- a/packages/python-google-compute-engine/packaging/python3-google-compute-engine.spec +++ b/packages/python-google-compute-engine/packaging/python3-google-compute-engine.spec @@ -13,8 +13,9 @@ # limitations under the License. Name: python3-google-compute-engine +Epoch: 1 Version: %{_version} -Release: 1%{?dist} +Release: g1%{?dist} Summary: Google Compute Engine python3 library License: ASL 2.0 Url: https://github.com/GoogleCloudPlatform/compute-image-packages diff --git a/packages/python-google-compute-engine/packaging/setup_deb.sh b/packages/python-google-compute-engine/packaging/setup_deb.sh index 6ce67224..573dba4a 100755 --- a/packages/python-google-compute-engine/packaging/setup_deb.sh +++ b/packages/python-google-compute-engine/packaging/setup_deb.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="python-google-compute-engine" -VERSION="2.8.16" +VERSION="20190729.00" working_dir=${PWD} if [[ $(basename "$working_dir") != $NAME ]]; then @@ -24,7 +24,7 @@ fi # Build dependencies. sudo apt-get -y install python-all python-setuptools python3-all \ - python3-setuptools python-pytest python3-pytest python-mock + python3-setuptools python-pytest python3-pytest python-mock python-boto # DEB creation tools. sudo apt-get -y install debhelper devscripts build-essential diff --git a/packages/python-google-compute-engine/packaging/setup_rpm.sh b/packages/python-google-compute-engine/packaging/setup_rpm.sh index c6c6d759..e12be10f 100755 --- a/packages/python-google-compute-engine/packaging/setup_rpm.sh +++ b/packages/python-google-compute-engine/packaging/setup_rpm.sh @@ -14,7 +14,7 @@ # limitations under the License. NAME="python-google-compute-engine" -VERSION="2.8.16" +VERSION="20190729.00" rpm_working_dir=/tmp/rpmpackage/${NAME}-${VERSION} working_dir=${PWD} diff --git a/packages/python-google-compute-engine/setup.py b/packages/python-google-compute-engine/setup.py index 87bf2dbd..a4fee36d 100755 --- a/packages/python-google-compute-engine/setup.py +++ b/packages/python-google-compute-engine/setup.py @@ -37,7 +37,7 @@ name='google-compute-engine', packages=setuptools.find_packages(), url='https://github.com/GoogleCloudPlatform/compute-image-packages', - version='2.8.16', + version='20190729.00', # Entry points create scripts in /usr/bin that call a function. entry_points={ 'console_scripts': [