pax_global_header00006660000000000000000000000064142547013530014516gustar00rootroot0000000000000052 comment=792fce795218633bcbde505fb3457a0b24f26d37 guest-agent-20220622.00/000077500000000000000000000000001425470135300144205ustar00rootroot00000000000000guest-agent-20220622.00/90-google-guest-agent.preset000066400000000000000000000001571425470135300215720ustar00rootroot00000000000000enable google-guest-agent.service enable google-shutdown-scripts.service enable google-startup-scripts.service guest-agent-20220622.00/CONTRIBUTING.md000066400000000000000000000061001425470135300166460ustar00rootroot00000000000000# How to become a contributor and submit your own code ## Contributor License Agreements We'd love to accept your sample apps and patches! Before we can take them, we have to jump a couple of legal hurdles. Please fill out either the individual or corporate Contributor License Agreement (CLA). * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA] (https://developers.google.com/open-source/cla/individual). * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA] (https://developers.google.com/open-source/cla/corporate). Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. ## Contributing a patch 1. Submit an issue describing your proposed change to the repo in question. 1. The repo owner will respond to your issue promptly. 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 1. Fork the desired repo, develop and test your code changes. 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. Refer to the [Google Cloud Platform Samples Style Guide] (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the recommended coding standards for this organization. 1. Ensure that your code has an appropriate set of unit tests which all pass. 1. Submit a pull request. ## Contributing a new sample App 1. Submit an issue to the `GoogleCloudPlatform/Template` repo describing your proposed sample app. 1. The Template repo owner will respond to your enhancement issue promptly. Instructional value is the top priority when evaluating new app proposals for this collection of repos. 1. If your proposal is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 1. Create your own repo for your app following this naming convention: * {product}-{app-name}-{language} * products: appengine, compute, storage, bigquery, prediction, cloudsql * example: appengine-guestbook-python * For multi-product apps, concatenate the primary products, like this: compute-appengine-demo-suite-python. * For multi-language apps, concatenate the primary languages like this: appengine-sockets-python-java-go. 1. Clone the `README.md`, `CONTRIB.md` and `LICENSE` files from the GoogleCloudPlatform/Template repo. 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. Refer to the [Google Cloud Platform Samples Style Guide] (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the recommended coding standards for this organization. 1. Ensure that your code has an appropriate set of unit tests which all pass. 1. Submit a request to fork your repo in GoogleCloudPlatform organization via your proposal issue. guest-agent-20220622.00/LICENSE000066400000000000000000000261151425470135300154320ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 Google Inc. 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. guest-agent-20220622.00/OWNERS000066400000000000000000000003431425470135300153600ustar00rootroot00000000000000# This file enables automatic assignment of PR reviewers. # See the OWNERS docs at https://go.k8s.io/owners approvers: - adjackura - hopkiw - zmarano - bkatyl reviewers: - adjackura - hopkiw - zmarano - bkatyl guest-agent-20220622.00/README.md000066400000000000000000000235741425470135300157120ustar00rootroot00000000000000## Guest Agent for Google Compute Engine This repository contains the source code and packaging artifacts for the Google guest agent and metadata script runner binaries. These components are installed on Windows and Linux GCE VMs in order to enable GCE platform features. **Table of Contents** * [Overview](#overview) * [Features](#features) * [Account Management](#account-management) * [Clock Skew](#clock-skew) * [OS Login](#os-login) * [Network](#network) * [Windows Failover Cluster Support](#windows-failover-cluster-support) * [Instance Setup](#instance-setup) * [Metadata Scripts](#metadata-scripts) * [Configuration](#configuration) * [Packaging](#packaging) ## Overview The repository contains these components: * **google-guest-agent** daemon which handles all of the areas outlined below in "features" * **google-metadata-script-runner** binary to run user-provided scripts at VM startup and shutdown. ## Features The guest agent functionality can be separated into various areas of responsibility. Historically, on Linux these were managed by separate independent processes, but today they are all managed by the guest agent. The `Daemons` section of the instance configs file on Linux refers to these areas of responsibility. This allows a user to easily modify or disable functionality. Behaviors for each area of responsibility are detailed below. #### Account management On Windows, the agent handles [creating user accounts and setting/resetting passwords.](https://cloud.google.com/compute/docs/instances/windows/creating-passwords-for-windows-instances) On Linux: If OS Login is not used, the guest agent will be responsible for provisioning and deprovisioning user accounts. The agent creates local user accounts and maintains the authorized SSH keys file for each. User account creation is based on [adding and remove SSH Keys](https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys) stored in metadata. The guest agent has the following behaviors: * Administrator permissions are managed with a `google-sudoers` Linux group. Members of this group are granted `sudo` permissions on the VM. * All users provisioned by the account daemon are added to the `google-sudoers` group. * The daemon stores a file in the guest to record which user accounts are managed by Google. * User accounts not managed by Google are not touched by the accounts daemon. * The authorized keys file for a Google managed user is deleted when all SSH keys for the user are removed from metadata. #### OS Login (Linux only) If the user has [configured OS Login via metadata](https://cloud.google.com/compute/docs/instances/managing-instance-access), the guest agent will be responsible for configuring the OS to use OS Login, otherwise called 'enabling' OS Login. This consists of: * Adding a Google config block to the SSHD configuration file and restarting SSHD. * Adding OS Login entries to the nsswitch.conf file. * Adding OS Login entries to the PAM configuration file for SSHD. If the user disables OS login via metadata, the configuration changes will be removed. #### Clock Skew (Linux only) The guest agent is responsible for syncing the software clock with the hypervisor clock after a stop/start event or after a migration. Preventing clock skew may result in `system time has changed` messages in VM logs. #### Network The guest agent uses network interface metadata to manage the network interfaces in the guest by performing the following tasks: * Enabled all associated network interfaces on boot. * Setup or remove IP routes in the guest for IP forwarding and IP aliases * Only IPv4 IP addresses are currently supported. * Routes are set on the primary ethernet interface. * Google routes are configured, by default, with the routing protocol ID `66`. This ID is a namespace for daemon configured IP addresses. It can be changed with the config file, see below. #### Windows Failover Cluster Support (Windows only) The agent can monitor the active node in the Windows Failover Cluster and coordinate with GCP Internal Load Balancer to forward all cluster traffic to the expected node. The following fields on instance metadata or instance\_configs.cfg can control the behavior: * `enable-wsfc`: If set to true, all IP forwarding info will be ignored and agent will start responding to the health check port. Default false. * `wsfc-agent-port`: The port which the agent will respond to health checks. Default 59998. * `wsfc-addrs`: A comma separated list of IP address. This is an advanced setting to enable user have both normal forwarding IPs and cluster IPs on the same instance. If set, agent will only skip-auto configuring IPs in the list. Default empty. #### Instance Setup (Linux only) The guest agent will perform some actions once each time on startup: * Optimize for local SSD. * Enable multi-queue on all the virtionet devices. The guest agent will perform some actions one time only, on the first VM boot: * Generate SSH host keys. * Create the `boto` config for using Google Cloud Storage. ## Metadata Scripts Metadata scripts implement support for running user provided [startup scripts](https://cloud.google.com/compute/docs/startupscript) and [shutdown scripts](https://cloud.google.com/compute/docs/shutdownscript). The guest support for metadata scripts is implemented in Python with the following design details. * Metadata scripts are executed in a shell. * If multiple metadata keys are specified (e.g. `startup-script` and `startup-script-url`) both are executed. * If multiple metadata keys are specified (e.g. `startup-script` and `startup-script-url`) a URL is executed first. * The exit status of a metadata script is logged after completed execution. ## Configuration Users of Google provided images may configure the guest environment behaviors using a configuration file. To make configuration changes on Windows, follow [these instructions](https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#configure-windows-features) To make configuration changes on Linux, add settings to `/etc/default/instance_configs.cfg`. If you are attempting to change the behavior of a running instance, restart the guest agent after modifying. Linux distributions looking to include their own defaults can specify settings in `/etc/default/instance_configs.cfg.distro`. These settings will not override `/etc/default/instance_configs.cfg`. This enables distribution settings that do not override user configuration during package update. The following are valid user configuration options. Section | Option | Value ----------------- | ---------------------- | ----- Accounts | deprovision\_remove | `true` makes deprovisioning a user destructive. Accounts | groups | Comma separated list of groups for newly provisioned users. Accounts | useradd\_cmd | Command string to create a new user. Accounts | userdel\_cmd | Command string to delete a user. Accounts | usermod\_cmd | Command string to modify a user's groups. Accounts | gpasswd\_add\_cmd | Command string to add a user to a group. Accounts | gpasswd\_remove\_cmd | Command string to remove a user from a group. Accounts | groupadd\_cmd | Command string to create a new group. Daemons | accounts\_daemon | `false` disables the accounts daemon. Daemons | clock\_skew\_daemon | `false` disables the clock skew daemon. Daemons | network\_daemon | `false` disables the network daemon. InstanceSetup | host\_key\_types | Comma separated list of host key types to generate. InstanceSetup | optimize\_local\_ssd | `false` prevents optimizing for local SSD. InstanceSetup | network\_enabled | `false` skips instance setup functions that require metadata. InstanceSetup | set\_boto\_config | `false` skips setting up a `boto` config. InstanceSetup | set\_host\_keys | `false` skips generating host keys on first boot. InstanceSetup | set\_multiqueue | `false` skips multiqueue driver support. IpForwarding | ethernet\_proto\_id | Protocol ID string for daemon added routes. IpForwarding | ip\_aliases | `false` disables setting up alias IP routes. IpForwarding | target\_instance\_ips | `false` disables internal IP address load balancing. MetadataScripts | default\_shell | String with the default shell to execute scripts. MetadataScripts | run\_dir | String base directory where metadata scripts are executed. MetadataScripts | startup | `false` disables startup script execution. MetadataScripts | shutdown | `false` disables shutdown script execution. NetworkInterfaces | setup | `false` skips network interface setup. NetworkInterfaces | ip\_forwarding | `false` skips IP forwarding. NetworkInterfaces | dhcp\_command | String path for alternate dhcp executable used to enable network interfaces. Setting `network_enabled` to `false` will disable generating host keys and the `boto` config in the guest. ## Packaging The guest agent and metadata script runner are packaged in DEB, RPM or Googet format packages which are published to Google Cloud repositories and preinstalled on Google managed GCE Images. Packaging scripts for each platform are stored in the packaging/ directory. We build the following packages for the Windows guest environment: google-compute-engine-windows - contains the guest agent executable. google-compute-engine-metadata-scripts - contains files to run startup and shutdown scripts. We build the following packages for the Linux guest environment: google-guest-agent - contains the guest agent and metadata script runner executables, as well as service files for both. guest-agent-20220622.00/THIRD_PARTY_LICENSES/000077500000000000000000000000001425470135300175365ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/000077500000000000000000000000001425470135300226745ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/000077500000000000000000000000001425470135300233015ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/LICENSE000066400000000000000000000261361425470135300243160ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/logging/000077500000000000000000000000001425470135300247275ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/logging/LICENSE000066400000000000000000000261361425470135300257440ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/storage/000077500000000000000000000000001425470135300247455ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/cloud.google.com/go/storage/LICENSE000066400000000000000000000261361425470135300257620ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/000077500000000000000000000000001425470135300215755ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/000077500000000000000000000000001425470135300255055ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agent/000077500000000000000000000000001425470135300277305ustar00rootroot00000000000000google_guest_agent/000077500000000000000000000000001425470135300335125ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agentLICENSE000066400000000000000000000261151425470135300345240ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agent/google_guest_agent Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 Google Inc. 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-logging-go/000077500000000000000000000000001425470135300306635ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-logging-go/logger/000077500000000000000000000000001425470135300321425ustar00rootroot00000000000000LICENSE000066400000000000000000000261351425470135300330770ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-logging-go/logger Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/go-ini/000077500000000000000000000000001425470135300227575ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/go-ini/ini/000077500000000000000000000000001425470135300235365ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/go-ini/ini/LICENSE000066400000000000000000000240151425470135300245450ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2014 Unknwon 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/000077500000000000000000000000001425470135300230445ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/groupcache/000077500000000000000000000000001425470135300251645ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/groupcache/lru/000077500000000000000000000000001425470135300257665ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/groupcache/lru/LICENSE000066400000000000000000000240411425470135300267740ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/protobuf/000077500000000000000000000000001425470135300247045ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/golang/protobuf/LICENSE000066400000000000000000000027101425470135300257110ustar00rootroot00000000000000Copyright 2010 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/google/000077500000000000000000000000001425470135300230515ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/google/go-cmp/000077500000000000000000000000001425470135300242335ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/google/go-cmp/cmp/000077500000000000000000000000001425470135300250125ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/google/go-cmp/cmp/LICENSE000066400000000000000000000027071425470135300260250ustar00rootroot00000000000000Copyright (c) 2017 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/googleapis/000077500000000000000000000000001425470135300237265ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/000077500000000000000000000000001425470135300251105ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/v2/000077500000000000000000000000001425470135300254375ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/v2/LICENSE000066400000000000000000000026771425470135300264600ustar00rootroot00000000000000Copyright 2016, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/kardianos/000077500000000000000000000000001425470135300235505ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/kardianos/service/000077500000000000000000000000001425470135300252105ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/kardianos/service/LICENSE000066400000000000000000000015461425470135300262230ustar00rootroot00000000000000Copyright (c) 2015 Daniel Theophanes This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/tarm/000077500000000000000000000000001425470135300225405ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/tarm/serial/000077500000000000000000000000001425470135300240175ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/github.com/tarm/serial/LICENSE000066400000000000000000000027071425470135300250320ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/go.opencensus.io/000077500000000000000000000000001425470135300227325ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/go.opencensus.io/LICENSE000066400000000000000000000261351425470135300237460ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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.guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/000077500000000000000000000000001425470135300215735ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/000077500000000000000000000000001425470135300220425ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/net/000077500000000000000000000000001425470135300226305ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/net/LICENSE000066400000000000000000000027071425470135300236430ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/oauth2/000077500000000000000000000000001425470135300232445ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/oauth2/LICENSE000066400000000000000000000027071425470135300242570ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/sync/000077500000000000000000000000001425470135300230165ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/sync/semaphore/000077500000000000000000000000001425470135300250015ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/sync/semaphore/LICENSE000066400000000000000000000027071425470135300260140ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/sys/000077500000000000000000000000001425470135300226605ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/sys/LICENSE000066400000000000000000000027071425470135300236730ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/text/000077500000000000000000000000001425470135300230265ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/golang.org/x/text/LICENSE000066400000000000000000000027071425470135300240410ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/000077500000000000000000000000001425470135300230465ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/api/000077500000000000000000000000001425470135300236175ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/api/LICENSE000066400000000000000000000027031425470135300246260ustar00rootroot00000000000000Copyright (c) 2011 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/genproto/000077500000000000000000000000001425470135300247035ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/genproto/LICENSE000066400000000000000000000261361425470135300257200ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/grpc/000077500000000000000000000000001425470135300240015ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/grpc/LICENSE000066400000000000000000000261361425470135300250160ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/protobuf/000077500000000000000000000000001425470135300247065ustar00rootroot00000000000000guest-agent-20220622.00/THIRD_PARTY_LICENSES/google.golang.org/protobuf/LICENSE000066400000000000000000000027071425470135300257210ustar00rootroot00000000000000Copyright (c) 2018 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. guest-agent-20220622.00/go.mod000066400000000000000000000007711425470135300155330ustar00rootroot00000000000000module github.com/GoogleCloudPlatform/guest-agent go 1.16 require ( cloud.google.com/go/storage v1.22.1 github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20210621170517-facc9c0d81b9 github.com/go-ini/ini v1.66.6 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da github.com/golang/protobuf v1.5.2 github.com/kardianos/service v1.2.1 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c google.golang.org/grpc v1.47.0 ) guest-agent-20220622.00/go.sum000066400000000000000000002017221425470135300155570ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.1.0/go.mod h1:g4RsfUkOvV3Vi5yRujQETpqwCN0F+faPZ2/ykNYfBJc= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0 h1:XdQIN5mdPTSBVwSIVDuY5e8ZzVAccsHvD3qTEz4zIps= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/logging v1.0.0 h1:kaunpnoEh9L4hu6JUsBa8Y20LBfKnCuDhKUgdZp7oK8= cloud.google.com/go/logging v1.0.0/go.mod h1:V1cc3ogwobYzQq5f2R7DS/GvRIrI4FKj01Gs5glwAls= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.1.1/go.mod h1:nbQkUX8zrWh07WKekXr/Phd0q/ERj4IOJnkE+v56Qys= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.22.1 h1:F6IlQJZrZM++apn9V5/VfS3gbTUYg98PS3EMQAzqtfg= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20210621170517-facc9c0d81b9 h1:Qq6KJSPMb1wCih/rqzQs3Ni2ShuCYc4q51wTx8lpy6Q= github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20210621170517-facc9c0d81b9/go.mod h1:3F/urXs15KEI7RBGoOsK9/jCCJPBKHxyZH/Nzc7uldo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.66.6 h1:h6k2Bb0HWS/BXXHCXj4QHjxPmlIU4NK+7MuLp9SD+4k= github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk= github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20190912063710-ac5d2bfcbfe0/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190917162342-3b4f30a44f3b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191017205301-920acffc3e65/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.11.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335 h1:2D0OT6tPVdrQTOnVe1VQjfJPTED6EZ7fdJ/f6Db6OsY= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= guest-agent-20220622.00/google-guest-agent.conf000066400000000000000000000001641425470135300207650ustar00rootroot00000000000000description "GCE Guest Agent" start on stopped rc RUNLEVEL=[2345] oom -16 respawn exec /usr/bin/google_guest_agent guest-agent-20220622.00/google-guest-agent.service000066400000000000000000000015701425470135300215020ustar00rootroot00000000000000[Unit] Description=Google Compute Engine Guest Agent # Start before sshd in order to regenerate SSH host keys. Before=sshd.service # Start after network is online and restart when network service is restarted. # Debian/Ubuntu 16.04: networking.service # SLES/EL7: network.service (SLES via wicked.service) # EL8: NetworkManager.service # COS/Ubuntu 18.04+: systemd-networkd.service After=network-online.target syslog.service After=network.service networking.service NetworkManager.service systemd-networkd.service Wants=network-online.target PartOf=network.service networking.service NetworkManager.service systemd-networkd.service [Service] Type=notify ExecStart=/usr/bin/google_guest_agent OOMScoreAdjust=-999 Restart=always [Install] WantedBy=sshd.service WantedBy=multi-user.target WantedBy=network.service networking.service NetworkManager.service systemd-networkd.service guest-agent-20220622.00/google-shutdown-scripts.conf000066400000000000000000000002061425470135300220770ustar00rootroot00000000000000# Runs a shutdown script from metadata. start on starting rc RUNLEVEL=[06] task exec /usr/bin/google_metadata_script_runner shutdown guest-agent-20220622.00/google-shutdown-scripts.service000066400000000000000000000006351425470135300226200ustar00rootroot00000000000000[Unit] Description=Google Compute Engine Shutdown Scripts Wants=network-online.target rsyslog.service After=network-online.target rsyslog.service [Service] Type=oneshot ExecStart=/bin/true RemainAfterExit=true # This service does nothing on start, and runs shutdown scripts on stop. ExecStop=/usr/bin/google_metadata_script_runner shutdown TimeoutStopSec=0 KillMode=process [Install] WantedBy=multi-user.target guest-agent-20220622.00/google-startup-scripts.conf000066400000000000000000000002141425470135300217250ustar00rootroot00000000000000# Runs a startup script from metadata. start on started google-guest-agent and startup exec /usr/bin/google_metadata_script_runner startup guest-agent-20220622.00/google-startup-scripts.service000066400000000000000000000006111425470135300224410ustar00rootroot00000000000000[Unit] Description=Google Compute Engine Startup Scripts Wants=network-online.target rsyslog.service After=network-online.target rsyslog.service google-guest-agent.service Before=apt-daily.service [Service] Type=oneshot ExecStart=/usr/bin/google_metadata_script_runner startup #TimeoutStartSec is ignored for Type=oneshot service units. KillMode=process [Install] WantedBy=multi-user.target guest-agent-20220622.00/google_authorized_keys/000077500000000000000000000000001425470135300211655ustar00rootroot00000000000000guest-agent-20220622.00/google_authorized_keys/main.go000066400000000000000000000125551425470135300224500ustar00rootroot00000000000000// Copyright 2022 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. // GoogleAuthorizedKeys obtains SSH keys from metadata. package main import ( "context" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "os" "runtime" "strconv" "strings" "time" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( programName = "GoogleAuthorizedKeysCommand" version string metadataURL = "http://169.254.169.254/computeMetadata/v1/" metadataRecursive = "/?recursive=true&alt=json" defaultTimeout = 2 * time.Second ) func logFormat(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func logFormatWindows(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") // 2006/01/02 15:04:05 GCEMetadataScripts This is a log message. return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func getMetadata(key string, recurse bool) ([]byte, error) { client := &http.Client{ Timeout: defaultTimeout, } url := metadataURL + key if recurse { url += metadataRecursive } req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Add("Metadata-Flavor", "Google") var res *http.Response // Retry forever, increase sleep between retries (up to 5 times) in order // to wait for slow network initialization. var rt time.Duration for i := 1; ; i++ { res, err = client.Do(req) if err == nil { break } if i < 6 { rt = time.Duration(3*i) * time.Second } logger.Errorf("error connecting to metadata server, retrying in %s, error: %v", rt, err) time.Sleep(rt) } defer res.Body.Close() md, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } return md, nil } func parseSSHKeys(username string, keys []string) []string { var keyList []string for _, key := range keys { keySplit := strings.SplitN(key, ":", 2) if len(keySplit) != 2 { continue } user, keyVal, err := utils.GetUserKey(key) if err != nil { continue } if user == username { keyList = append(keyList, keyVal) } } return keyList } func getUserKeys(username string, instanceAttributes *attributes, projectAttributes *attributes) []string { var userKeyList []string instanceKeyList := parseSSHKeys(username, instanceAttributes.SSHKeys) userKeyList = append(userKeyList, instanceKeyList...) if !instanceAttributes.BlockProjectSSHKeys { projectKeyList := parseSSHKeys(username, projectAttributes.SSHKeys) userKeyList = append(userKeyList, projectKeyList...) } return userKeyList } func checkWinSSHEnabled(instanceAttributes *attributes, projectAttributes *attributes) bool { if instanceAttributes.EnableWindowsSSH != nil { return bool(*instanceAttributes.EnableWindowsSSH) } else if projectAttributes.EnableWindowsSSH != nil { return bool(*projectAttributes.EnableWindowsSSH) } return false } type attributes struct { EnableWindowsSSH *bool BlockProjectSSHKeys bool SSHKeys []string } func getMetadataAttributes(metadataKey string) (*attributes, error) { var a attributes type jsonAttributes struct { EnableWindowsSSH string `json:"enable-windows-ssh"` BlockProjectSSHKeys string `json:"block-project-ssh-keys"` SSHKeys string `json:"ssh-keys"` } var ja jsonAttributes metadata, err := getMetadata(metadataKey, true) if err != nil { return nil, err } if err := json.Unmarshal(metadata, &ja); err != nil { return nil, err } value, err := strconv.ParseBool(ja.BlockProjectSSHKeys) if err == nil { a.BlockProjectSSHKeys = value } value, err = strconv.ParseBool(ja.EnableWindowsSSH) if err == nil { a.EnableWindowsSSH = &value } if ja.SSHKeys != "" { a.SSHKeys = strings.Split(ja.SSHKeys, "\n") } return &a, nil } func main() { ctx := context.Background() username := os.Args[1] opts := logger.LogOpts{ LoggerName: programName, FormatFunction: logFormat, } if runtime.GOOS == "windows" { opts.Writers = []io.Writer{&utils.SerialPort{Port: "COM1"}, os.Stderr} opts.FormatFunction = logFormatWindows } else { opts.Writers = []io.Writer{os.Stderr} } logger.Init(ctx, opts) instanceAttributes, err := getMetadataAttributes("instance/attributes/") if err != nil { logger.Errorf("Cannot read instance metadata attributes: %v", err) os.Exit(1) } projectAttributes, err := getMetadataAttributes("project/attributes/") if err != nil { logger.Errorf("Cannot read project metadata attributes: %v", err) os.Exit(1) } if runtime.GOOS == "windows" && !checkWinSSHEnabled(instanceAttributes, projectAttributes) { logger.Errorf("Windows SSH not enabled with 'enable-windows-ssh' metadata key.") os.Exit(1) } userKeyList := getUserKeys(username, instanceAttributes, projectAttributes) fmt.Printf(strings.Join(userKeyList, "\n")) } guest-agent-20220622.00/google_authorized_keys/main_test.go000066400000000000000000000156121425470135300235040ustar00rootroot00000000000000// Copyright 2022 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. package main import ( "fmt" "net/http" "net/http/httptest" "reflect" "strconv" "strings" "testing" "time" ) func stringSliceEqual(a, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } func boolToStr(b *bool) string { if b == nil { return "" } return strconv.FormatBool(*b) } var t = true var f = false var truebool *bool = &t var falsebool *bool = &f func TestParseSSHKeys(t *testing.T) { keys := []string{ "# Here is some random data in the file.", "usera:ssh-rsa AAAA1234USERA", "userb:ssh-rsa AAAA1234USERB", `usera:ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2095-04-23T12:34:56+0000"}`, `usera:ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2020-04-23T12:34:56+0000"}`, } expected := []string{ "ssh-rsa AAAA1234USERA", `ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2095-04-23T12:34:56+0000"}`, } user := "usera" if got, want := parseSSHKeys(user, keys), expected; !stringSliceEqual(got, want) { t.Errorf("ParseSSHKeys(%s,%s) incorrect return: got %v, want %v", user, keys, got, want) } } func TestCheckWinSSHEnabled(t *testing.T) { tests := []struct { instanceEnable *bool projectEnable *bool expected bool }{ { instanceEnable: truebool, projectEnable: nil, expected: true, }, { instanceEnable: falsebool, projectEnable: nil, expected: false, }, { instanceEnable: falsebool, projectEnable: truebool, expected: false, }, { instanceEnable: nil, projectEnable: truebool, expected: true, }, { instanceEnable: nil, projectEnable: falsebool, expected: false, }, { instanceEnable: nil, projectEnable: nil, expected: false, }, } for _, tt := range tests { instanceAttributes := attributes{EnableWindowsSSH: tt.instanceEnable} projectAttributes := attributes{EnableWindowsSSH: tt.projectEnable} if got, want := checkWinSSHEnabled(&instanceAttributes, &projectAttributes), tt.expected; got != want { t.Errorf("checkWinSSHEnabled(%s, %s) incorrect return: got %v, want %v", boolToStr(tt.instanceEnable), boolToStr(tt.projectEnable), got, want) } } } func TestGetUserKeysNew(t *testing.T) { tests := []struct { userName string instanceMetadata attributes projectMetadata attributes expectedKeys []string }{ { userName: "name", instanceMetadata: attributes{BlockProjectSSHKeys: false, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, }, projectMetadata: attributes{ SSHKeys: []string{"name:ssh-rsa [KEY] project1", "othername:ssh-rsa [KEY] project2"}, }, expectedKeys: []string{"ssh-rsa [KEY] instance1", "ssh-rsa [KEY] project1"}, }, { userName: "name", instanceMetadata: attributes{BlockProjectSSHKeys: true, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, }, projectMetadata: attributes{ SSHKeys: []string{"name:ssh-rsa [KEY] project1", "othername:ssh-rsa [KEY] project2"}, }, expectedKeys: []string{"ssh-rsa [KEY] instance1"}, }, { userName: "name", instanceMetadata: attributes{BlockProjectSSHKeys: false, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, }, projectMetadata: attributes{ SSHKeys: nil, }, expectedKeys: []string{"ssh-rsa [KEY] instance1"}, }, { userName: "name", instanceMetadata: attributes{BlockProjectSSHKeys: false, SSHKeys: nil, }, projectMetadata: attributes{ SSHKeys: []string{"name:ssh-rsa [KEY] project1", "othername:ssh-rsa [KEY] project2"}, }, expectedKeys: []string{"ssh-rsa [KEY] project1"}, }, } for count, tt := range tests { if got, want := getUserKeys(tt.userName, &tt.instanceMetadata, &tt.projectMetadata), tt.expectedKeys; !stringSliceEqual(got, want) { t.Errorf("getUserKeys[%d] incorrect return: got %v, want %v", count, got, want) } } } func TestGetMetadataAttributes(t *testing.T) { tests := []struct { metadata string att *attributes expectErr bool }{ { metadata: `{"enable-windows-ssh":"true","ssh-keys":"name:ssh-rsa [KEY] instance1\nothername:ssh-rsa [KEY] instance2","block-project-ssh-keys":"false","other-metadata":"foo"}`, att: &attributes{EnableWindowsSSH: truebool, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, BlockProjectSSHKeys: false}, expectErr: false, }, { metadata: `{"enable-windows-ssh":"true","ssh-keys":"name:ssh-rsa [KEY] instance1\nothername:ssh-rsa [KEY] instance2","block-project-ssh-keys":"true","other-metadata":"foo"}`, att: &attributes{EnableWindowsSSH: truebool, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, BlockProjectSSHKeys: true}, expectErr: false, }, { metadata: `{"ssh-keys":"name:ssh-rsa [KEY] instance1\nothername:ssh-rsa [KEY] instance2","block-project-ssh-keys":"false","other-metadata":"foo"}`, att: &attributes{EnableWindowsSSH: nil, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, BlockProjectSSHKeys: false}, expectErr: false, }, { metadata: `{"enable-windows-ssh":"false","ssh-keys":"name:ssh-rsa [KEY] instance1\nothername:ssh-rsa [KEY] instance2","other-metadata":"foo"}`, att: &attributes{EnableWindowsSSH: falsebool, SSHKeys: []string{"name:ssh-rsa [KEY] instance1", "othername:ssh-rsa [KEY] instance2"}, BlockProjectSSHKeys: false}, expectErr: false, }, { metadata: `BADJSON`, att: nil, expectErr: true, }, } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Get test number from request path tnum, _ := strconv.Atoi(strings.Split(r.URL.Path, "/")[2]) fmt.Fprintf(w, tests[tnum].metadata) })) defer ts.Close() metadataURL = ts.URL defaultTimeout = 1 * time.Second for count, tt := range tests { want := tt.att hasErr := false reqStr := fmt.Sprintf("/attributes/%d", count) got, err := getMetadataAttributes(reqStr) if err != nil { hasErr = true } if !reflect.DeepEqual(got, want) || hasErr != tt.expectErr { t.Errorf("Failed: Got: %v, Want: %v, Error: %v", got, want, err) } } } guest-agent-20220622.00/google_guest_agent/000077500000000000000000000000001425470135300202615ustar00rootroot00000000000000guest-agent-20220622.00/google_guest_agent/accounts_unix.go000066400000000000000000000027631425470135300235020ustar00rootroot00000000000000// Copyright 2019 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. // +build !windows package main import ( "fmt" "os" "os/user" "syscall" ) func getUID(path string) string { if dir, err := os.Stat(path); err == nil { if stat, ok := dir.Sys().(*syscall.Stat_t); ok { return fmt.Sprintf("%d", stat.Uid) } } return "" } func createUser(username, uid string) error { useradd := config.Section("Accounts").Key("useradd_cmd").MustString("useradd -m -s /bin/bash -p * {user}") if uid != "" { useradd = fmt.Sprintf("%s -u %s", useradd, uid) } return runCmd(createUserGroupCmd(useradd, username, "")) } func addUserToGroup(user, group string) error { gpasswdadd := config.Section("Accounts").Key("gpasswd_add_cmd").MustString("gpasswd -a {user} {group}") return runCmd(createUserGroupCmd(gpasswdadd, user, group)) } func userExists(name string) (bool, error) { if _, err := user.Lookup(name); err != nil { return false, err } return true, nil } guest-agent-20220622.00/google_guest_agent/accounts_windows.go000066400000000000000000000121371425470135300242050ustar00rootroot00000000000000// 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. package main import ( "fmt" "syscall" "unsafe" "golang.org/x/sys/windows" ) var ( netAPI32 = windows.NewLazySystemDLL("netapi32.dll") procNetUserAdd = netAPI32.NewProc("NetUserAdd") procNetUserGetInfo = netAPI32.NewProc("NetUserGetInfo") procNetUserSetInfo = netAPI32.NewProc("NetUserSetInfo") procNetLocalGroupAddMembers = netAPI32.NewProc("NetLocalGroupAddMembers") ) type ( USER_INFO_0 struct { Usri0_name LPWSTR } USER_INFO_1 struct { Usri1_name LPWSTR Usri1_password LPWSTR Usri1_password_age DWORD Usri1_priv DWORD Usri1_home_dir LPWSTR Usri1_comment LPWSTR Usri1_flags DWORD Usri1_script_path LPWSTR } LOCALGROUP_MEMBERS_INFO_0 struct { Lgrmi0_sid *syscall.SID } USER_INFO_1003 struct { Usri1003_password LPWSTR } ) const ( USER_PRIV_GUEST = 0 USER_PRIV_USER = 1 USER_PRIV_ADMIN = 2 UF_SCRIPT = 0x0001 UF_ACCOUNTDISABLE = 0x0002 UF_HOMEDIR_REQUIRED = 0x0008 UF_LOCKOUT = 0x0010 UF_PASSWD_NOTREQD = 0x0020 UF_PASSWD_CANT_CHANGE = 0x0040 UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x0080 UF_TEMP_DUPLICATE_ACCOUNT = 0x0100 UF_NORMAL_ACCOUNT = 0x0200 UF_INTERDOMAIN_TRUST_ACCOUNT = 0x0800 UF_WORKSTATION_TRUST_ACCOUNT = 0x1000 UF_SERVER_TRUST_ACCOUNT = 0x2000 UF_DONT_EXPIRE_PASSWD = 0x10000 UF_MNS_LOGON_ACCOUNT = 0x20000 UF_SMARTCARD_REQUIRED = 0x40000 UF_TRUSTED_FOR_DELEGATION = 0x80000 UF_NOT_DELEGATED = 0x100000 UF_USE_DES_KEY_ONLY = 0x200000 UF_DONT_REQUIRE_PREAUTH = 0x400000 UF_PASSWORD_EXPIRED = 0x800000 UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000 UF_NO_AUTH_DATA_REQUIRED = 0x2000000 UF_PARTIAL_SECRETS_ACCOUNT = 0x4000000 UF_USE_AES_KEYS = 0x8000000 ) func resetPwd(username, pwd string) error { uPtr, err := syscall.UTF16PtrFromString(username) if err != nil { return fmt.Errorf("error encoding username to UTF16: %v", err) } pPtr, err := syscall.UTF16PtrFromString(pwd) if err != nil { return fmt.Errorf("error encoding password to UTF16: %v", err) } ret, _, _ := procNetUserSetInfo.Call( uintptr(0), uintptr(unsafe.Pointer(uPtr)), uintptr(1003), uintptr(unsafe.Pointer(&USER_INFO_1003{pPtr})), uintptr(0)) if ret != 0 { return fmt.Errorf("nonzero return code from NetUserSetInfo: %s", syscall.Errno(ret)) } return nil } func addUserToGroup(username, group string) error { gPtr, err := syscall.UTF16PtrFromString(group) if err != nil { return fmt.Errorf("error encoding group to UTF16: %v", err) } sid, _, _, err := syscall.LookupSID("", username) if err != nil { return err } sArray := []LOCALGROUP_MEMBERS_INFO_0{{sid}} ret, _, _ := procNetLocalGroupAddMembers.Call( uintptr(0), uintptr(unsafe.Pointer(gPtr)), uintptr(0), uintptr(unsafe.Pointer(&sArray[0])), uintptr(1), ) // Ignore ERROR_MEMBER_IN_ALIAS (1378). if ret != 0 && ret != 1378 { return fmt.Errorf("nonzero return code from NetLocalGroupAddMembers: %s", syscall.Errno(ret)) } return nil } func createUser(username, pwd string) error { uPtr, err := syscall.UTF16PtrFromString(username) if err != nil { return fmt.Errorf("error encoding username to UTF16: %v", err) } pPtr, err := syscall.UTF16PtrFromString(pwd) if err != nil { return fmt.Errorf("error encoding password to UTF16: %v", err) } uInfo1 := USER_INFO_1{ Usri1_name: uPtr, Usri1_password: pPtr, Usri1_priv: USER_PRIV_USER, Usri1_flags: UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD, } ret, _, _ := procNetUserAdd.Call( uintptr(0), uintptr(1), uintptr(unsafe.Pointer(&uInfo1)), uintptr(0), ) if ret != 0 { return fmt.Errorf("nonzero return code from NetUserAdd: %s", syscall.Errno(ret)) } return nil } func userExists(name string) (bool, error) { uPtr, err := syscall.UTF16PtrFromString(name) if err != nil { return false, fmt.Errorf("error encoding username to UTF16: %v", err) } ret, _, _ := procNetUserGetInfo.Call( uintptr(0), uintptr(unsafe.Pointer(uPtr)), uintptr(1), uintptr(unsafe.Pointer(&USER_INFO_0{})), ) if ret != 0 { return false, fmt.Errorf("nonzero return code from NetUserGetInfo: %s", syscall.Errno(ret)) } return true, nil } func getUID(path string) string { return "" } guest-agent-20220622.00/google_guest_agent/addresses.go000066400000000000000000000454161425470135300225770ustar00rootroot00000000000000// Copyright 2022 Google LLC // // 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. package main import ( "errors" "fmt" "net" "os" "os/exec" "reflect" "runtime" "strings" "time" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( addressKey = regKeyBase + `\ForwardedIps` oldWSFCAddresses string oldWSFCEnable bool interfacesEnabled bool interfaces []net.Interface defaultProtoID = "66" ) type addressMgr struct{} func (a *addressMgr) parseWSFCAddresses() string { wsfcAddresses := config.Section("wsfc").Key("addresses").String() if wsfcAddresses != "" { return wsfcAddresses } if newMetadata.Instance.Attributes.WSFCAddresses != "" { return newMetadata.Instance.Attributes.WSFCAddresses } if newMetadata.Project.Attributes.WSFCAddresses != "" { return newMetadata.Project.Attributes.WSFCAddresses } return "" } func (a *addressMgr) parseWSFCEnable() bool { wsfcEnable, err := config.Section("wsfc").Key("enable").Bool() if err == nil { return wsfcEnable } if newMetadata.Instance.Attributes.EnableWSFC != nil { return *newMetadata.Instance.Attributes.EnableWSFC } if newMetadata.Project.Attributes.EnableWSFC != nil { return *newMetadata.Project.Attributes.EnableWSFC } return false } func getForwardsFromRegistry(mac string) ([]string, error) { regFwdIPs, err := readRegMultiString(addressKey, mac) if err == errRegNotExist { // The old agent stored MAC addresses without the ':', // check for those and clean them up. oldName := strings.Replace(mac, ":", "", -1) regFwdIPs, err = readRegMultiString(addressKey, oldName) if err == nil { deleteRegKey(addressKey, oldName) } } else if err != nil { return nil, err } return regFwdIPs, nil } func compareRoutes(configuredRoutes, desiredRoutes []string) (toAdd, toRm []string) { for _, desiredRoute := range desiredRoutes { if !utils.ContainsString(desiredRoute, configuredRoutes) { toAdd = append(toAdd, desiredRoute) } } for _, configuredRoute := range configuredRoutes { if !utils.ContainsString(configuredRoute, desiredRoutes) { toRm = append(toRm, configuredRoute) } } return toAdd, toRm } var badMAC []string func getInterfaceByMAC(mac string) (net.Interface, error) { hwaddr, err := net.ParseMAC(mac) if err != nil { return net.Interface{}, err } for _, iface := range interfaces { if iface.HardwareAddr.String() == hwaddr.String() { return iface, nil } } return net.Interface{}, fmt.Errorf("no interface found with MAC %s", mac) } // https://www.ietf.org/rfc/rfc1354.txt // Only fields that we currently care about. type ipForwardEntry struct { ipForwardDest net.IP ipForwardMask net.IPMask ipForwardNextHop net.IP ipForwardIfIndex int32 ipForwardMetric1 int32 } // TODO: getLocalRoutes and getIPForwardEntries should be merged. func getLocalRoutes(ifname string) ([]string, error) { if runtime.GOOS == "windows" { return nil, errors.New("getLocalRoutes unimplemented on Windows") } protoID := config.Section("IpForwarding").Key("ethernet_proto_id").MustString(defaultProtoID) args := fmt.Sprintf("route list table local type local scope host dev %s proto %s", ifname, protoID) out := runCmdOutput(exec.Command("ip", strings.Split(args, " ")...)) if out.ExitCode() != 0 { return nil, error(out) } var res []string for _, line := range strings.Split(out.Stdout(), "\n") { line = strings.TrimPrefix(line, "local ") line = strings.TrimSpace(line) if line != "" { res = append(res, line) } } // and again for IPv6 routes, without 'scope host' which is IPv4 only args = fmt.Sprintf("-6 route list table local type local dev %s proto %s", ifname, protoID) out = runCmdOutput(exec.Command("ip", strings.Split(args, " ")...)) if out.ExitCode() != 0 { return nil, error(out) } for _, line := range strings.Split(out.Stdout(), "\n") { line = strings.TrimPrefix(line, "local ") line = strings.Split(line, " ")[0] line = strings.TrimSpace(line) if line != "" { res = append(res, line) } } return res, nil } // TODO: addLocalRoute and addRoute should be merged with the addition of ipForwardType to ipForwardEntry. func addLocalRoute(ip, ifname string) error { if runtime.GOOS == "windows" { return errors.New("addLocalRoute unimplemented on Windows") } // TODO: Subnet size should be parsed from alias IP entries. if !strings.Contains(ip, "/") { ip = ip + "/32" } protoID := config.Section("IpForwarding").Key("ethernet_proto_id").MustString(defaultProtoID) args := fmt.Sprintf("route add to local %s scope host dev %s proto %s", ip, ifname, protoID) return runCmd(exec.Command("ip", strings.Split(args, " ")...)) } // TODO: removeLocalRoute should be changed to removeIPForwardEntry and match getIPForwardEntries. func removeLocalRoute(ip, ifname string) error { if runtime.GOOS == "windows" { return errors.New("removeLocalRoute unimplemented on Windows") } // TODO: Subnet size should be parsed from alias IP entries. if !strings.Contains(ip, "/") { ip = ip + "/32" } protoID := config.Section("IpForwarding").Key("ethernet_proto_id").MustString(defaultProtoID) args := fmt.Sprintf("route delete to local %s scope host dev %s proto %s", ip, ifname, protoID) return runCmd(exec.Command("ip", strings.Split(args, " ")...)) } // Filter out forwarded ips based on WSFC (Windows Failover Cluster Settings). // If only EnableWSFC is set, all ips in the ForwardedIps and TargetInstanceIps will be ignored. // If WSFCAddresses is set (with or without EnableWSFC), only ips in the list will be filtered out. // TODO return a filtered list rather than modifying the metadata object. liamh@15-11-19 func (a *addressMgr) applyWSFCFilter() { wsfcAddresses := a.parseWSFCAddresses() var wsfcAddrs []string for _, wsfcAddr := range strings.Split(wsfcAddresses, ",") { if wsfcAddr == "" { continue } if net.ParseIP(wsfcAddr) == nil { logger.Errorf("Address for WSFC is not in valid form %s", wsfcAddr) continue } wsfcAddrs = append(wsfcAddrs, wsfcAddr) } if len(wsfcAddrs) != 0 { interfaces := newMetadata.Instance.NetworkInterfaces for idx := range interfaces { var filteredForwardedIps []string for _, ip := range interfaces[idx].ForwardedIps { if !utils.ContainsString(ip, wsfcAddrs) { filteredForwardedIps = append(filteredForwardedIps, ip) } } interfaces[idx].ForwardedIps = filteredForwardedIps var filteredTargetInstanceIps []string for _, ip := range interfaces[idx].TargetInstanceIps { if !utils.ContainsString(ip, wsfcAddrs) { filteredTargetInstanceIps = append(filteredTargetInstanceIps, ip) } } interfaces[idx].TargetInstanceIps = filteredTargetInstanceIps } } else { wsfcEnable := a.parseWSFCEnable() if wsfcEnable { for idx := range newMetadata.Instance.NetworkInterfaces { newMetadata.Instance.NetworkInterfaces[idx].ForwardedIps = nil newMetadata.Instance.NetworkInterfaces[idx].TargetInstanceIps = nil } } } } func (a *addressMgr) diff() bool { wsfcAddresses := a.parseWSFCAddresses() wsfcEnable := a.parseWSFCEnable() diff := !reflect.DeepEqual(newMetadata.Instance.NetworkInterfaces, oldMetadata.Instance.NetworkInterfaces) || wsfcEnable != oldWSFCEnable || wsfcAddresses != oldWSFCAddresses oldWSFCAddresses = wsfcAddresses oldWSFCEnable = wsfcEnable return diff } func (a *addressMgr) timeout() bool { return false } func (a *addressMgr) disabled(os string) (disabled bool) { disabled, err := config.Section("addressManager").Key("disable").Bool() if err == nil { // This is the windows config key. On windows, finding a key in // the config file takes priority over metadata. return disabled } if newMetadata.Instance.Attributes.DisableAddressManager != nil { return *newMetadata.Instance.Attributes.DisableAddressManager } if newMetadata.Project.Attributes.DisableAddressManager != nil { return *newMetadata.Project.Attributes.DisableAddressManager } // This is the linux config key, defaulting to true. On Linux, the // config file has lower priority since we ship a file with defaults. return !config.Section("Daemons").Key("network_daemon").MustBool(true) } func (a *addressMgr) set() error { if runtime.GOOS == "windows" { a.applyWSFCFilter() } var err error interfaces, err = net.Interfaces() if err != nil { return fmt.Errorf("error populating interfaces: %v", err) } if config.Section("NetworkInterfaces").Key("setup").MustBool(true) { if runtime.GOOS != "windows" { logger.Debugf("Configure IPv6") if err := configureIPv6(); err != nil { // Continue through IPv6 configuration errors. logger.Errorf("Error configuring IPv6: %v", err) } } if runtime.GOOS != "windows" && !interfacesEnabled { logger.Debugf("Enable network interfaces") if err := enableNetworkInterfaces(); err != nil { return err } interfacesEnabled = true } } if !config.Section("NetworkInterfaces").Key("ip_forwarding").MustBool(true) { return nil } logger.Debugf("Add routes for aliases, forwarded IP and target-instance IPs") // Add routes for IP aliases, forwarded and target-instance IPs. for _, ni := range newMetadata.Instance.NetworkInterfaces { iface, err := getInterfaceByMAC(ni.Mac) if err != nil { if !utils.ContainsString(ni.Mac, badMAC) { logger.Errorf("Error getting interface: %s", err) badMAC = append(badMAC, ni.Mac) } continue } wantIPs := ni.ForwardedIps wantIPs = append(wantIPs, ni.ForwardedIpv6s...) if config.Section("IpForwarding").Key("target_instance_ips").MustBool(true) { wantIPs = append(wantIPs, ni.TargetInstanceIps...) } // IP Aliases are not supported on windows. if runtime.GOOS != "windows" && config.Section("IpForwarding").Key("ip_aliases").MustBool(true) { wantIPs = append(wantIPs, ni.IPAliases...) } var forwardedIPs []string var configuredIPs []string if runtime.GOOS == "windows" { addrs, err := iface.Addrs() if err != nil { logger.Errorf("Error getting addresses for interface %s: %s", iface.Name, err) } for _, addr := range addrs { configuredIPs = append(configuredIPs, strings.TrimSuffix(addr.String(), "/32")) } regFwdIPs, err := getForwardsFromRegistry(ni.Mac) if err != nil { logger.Errorf("Error getting forwards from registry: %s", err) continue } for _, ip := range configuredIPs { // Only add to `forwardedIPs` if it is recorded in the registry. if utils.ContainsString(ip, regFwdIPs) { forwardedIPs = append(forwardedIPs, ip) } } } else { forwardedIPs, err = getLocalRoutes(iface.Name) if err != nil { logger.Errorf("Error getting routes: %v", err) continue } } // Trims any '/32' suffix for consistency. trimSuffix := func(entries []string) []string { var res []string for _, entry := range entries { res = append(res, strings.TrimSuffix(entry, "/32")) } return res } forwardedIPs = trimSuffix(forwardedIPs) wantIPs = trimSuffix(wantIPs) toAdd, toRm := compareRoutes(forwardedIPs, wantIPs) if len(toAdd) != 0 || len(toRm) != 0 { var msg string msg = fmt.Sprintf("Changing forwarded IPs for %s from %q to %q by", ni.Mac, forwardedIPs, wantIPs) if len(toAdd) != 0 { msg += fmt.Sprintf(" adding %q", toAdd) } if len(toRm) != 0 { if len(toAdd) != 0 { msg += " and" } msg += fmt.Sprintf(" removing %q", toRm) } logger.Infof(msg) } var registryEntries []string for _, ip := range wantIPs { // If the IP is not in toAdd, add to registry list and continue. if !utils.ContainsString(ip, toAdd) { registryEntries = append(registryEntries, ip) continue } var err error if runtime.GOOS == "windows" { // Don't addAddress if this is already configured. if !utils.ContainsString(ip, configuredIPs) { err = addAddress(net.ParseIP(ip), net.IPv4Mask(255, 255, 255, 255), uint32(iface.Index)) } } else { err = addLocalRoute(ip, iface.Name) } if err == nil { registryEntries = append(registryEntries, ip) } else { logger.Errorf("error adding route: %v", err) } } for _, ip := range toRm { var err error if runtime.GOOS == "windows" { if !utils.ContainsString(ip, configuredIPs) { continue } err = removeAddress(net.ParseIP(ip), uint32(iface.Index)) } else { err = removeLocalRoute(ip, iface.Name) } if err != nil { logger.Errorf("error removing route: %v", err) // Add IPs we fail to remove to registry to maintain accurate record. registryEntries = append(registryEntries, ip) } } if runtime.GOOS == "windows" { if err := writeRegMultiString(addressKey, ni.Mac, registryEntries); err != nil { logger.Errorf("error writing registry: %s", err) } } } return nil } // Enables or disables IPv6 on network interfaces. func configureIPv6() error { var newNi, oldNi networkInterfaces if len(newMetadata.Instance.NetworkInterfaces) == 0 { return fmt.Errorf("no interfaces found in metadata") } newNi = newMetadata.Instance.NetworkInterfaces[0] if len(oldMetadata.Instance.NetworkInterfaces) > 0 { oldNi = oldMetadata.Instance.NetworkInterfaces[0] } iface, err := getInterfaceByMAC(newNi.Mac) if err != nil { return err } switch { case oldNi.DHCPv6Refresh != "" && newNi.DHCPv6Refresh == "", newNi.DHCPv6Refresh == "" && len(oldMetadata.Instance.NetworkInterfaces) == 0: // disable // uses empty old interface slice to indicate this is first-run. // Before obtaining or releasing an IPv6 lease, we wait for // 'tentative' IPs as part of SLAAC. We wait up to 5 seconds // for this condition to automatically resolve. tentative := exec.Command("ip", "-6", "-o", "a", "s", "dev", iface.Name, "scope", "link", "tentative") for i := 0; i < 5; i++ { res := runCmdOutput(tentative) if res.ExitCode() == 0 && res.Stdout() == "" { break } time.Sleep(1 * time.Second) } if err := runCmd(exec.Command("dhclient", "-r", "-6", "-1", "-v", iface.Name)); err != nil { return err } case oldNi.DHCPv6Refresh == "" && newNi.DHCPv6Refresh != "": // enable tentative := exec.Command("ip", "-6", "-o", "a", "s", "dev", iface.Name, "scope", "link", "tentative") for i := 0; i < 5; i++ { res := runCmdOutput(tentative) if res.ExitCode() == 0 && res.Stdout() == "" { break } time.Sleep(1 * time.Second) } val := fmt.Sprintf("net.ipv6.conf.%s.accept_ra_rt_info_max_plen=128", iface.Name) if err := runCmd(exec.Command("sysctl", val)); err != nil { return err } if err := runCmd(exec.Command("dhclient", "-1", "-6", "-v", iface.Name)); err != nil { return err } } return nil } // enableNetworkInterfaces runs `dhclient eth1 eth2 ... ethN` // and `dhclient -6 eth1 eth2 ... ethN`. // On RHEL7, it also calls disableNM for each interface. // On SLES, it calls enableSLESInterfaces instead of dhclient. func enableNetworkInterfaces() error { if len(newMetadata.Instance.NetworkInterfaces) < 2 { return nil } var googleInterfaces []string // The primary (first) interface is managed by the OS, we only handle // secondary interfaces in this code. for _, ni := range newMetadata.Instance.NetworkInterfaces[1:] { iface, err := getInterfaceByMAC(ni.Mac) if err != nil { if !utils.ContainsString(ni.Mac, badMAC) { logger.Errorf("Error getting interface: %s", err) badMAC = append(badMAC, ni.Mac) } continue } googleInterfaces = append(googleInterfaces, iface.Name) } var googleIpv6Interfaces []string for _, ni := range newMetadata.Instance.NetworkInterfaces[1:] { if ni.DHCPv6Refresh == "" { // This interface is not IPv6 enabled continue } iface, err := getInterfaceByMAC(ni.Mac) if err != nil { if !utils.ContainsString(ni.Mac, badMAC) { logger.Errorf("Error getting interface: %s", err) badMAC = append(badMAC, ni.Mac) } continue } googleIpv6Interfaces = append(googleIpv6Interfaces, iface.Name) } switch { case osRelease.os == "sles": return enableSLESInterfaces(googleInterfaces) case (osRelease.os == "rhel" || osRelease.os == "centos") && osRelease.version.major >= 7: for _, iface := range googleInterfaces { err := disableNM(iface) if err != nil { return err } } fallthrough default: dhcpCommand := config.Section("NetworkInterfaces").Key("dhcp_command").String() if dhcpCommand != "" { return runCmd(exec.Command(dhcpCommand)) } // Try IPv4 first as it's higher priority. if err := runCmd(exec.Command("dhclient", googleInterfaces...)); err != nil { return err } if len(googleIpv6Interfaces) == 0 { return nil } for _, iface := range googleIpv6Interfaces { // Enable kernel to accept to route advertisements. val := fmt.Sprintf("net.ipv6.conf.%s.accept_ra_rt_info_max_plen=128", iface) if err := runCmd(exec.Command("sysctl", val)); err != nil { return err } } var dhclientArgs6 []string dhclientArgs6 = append([]string{"-6"}, googleIpv6Interfaces...) return runCmd(exec.Command("dhclient", dhclientArgs6...)) } } // enableSLESInterfaces writes one ifcfg file for each interface, then // runs `wicked ifup eth1 eth2 ... ethN` func enableSLESInterfaces(interfaces []string) error { var err error var priority = 10100 for _, iface := range interfaces { logger.Debugf("write enabling ifcfg-%s config", iface) var ifcfg *os.File ifcfg, err = os.Create("/etc/sysconfig/network/ifcfg-" + iface) if err != nil { return err } defer closer(ifcfg) contents := []string{ googleComment, "STARTMODE=hotplug", // NOTE: 'dhcp' is the dhcp4+dhcp6 option. "BOOTPROTO=dhcp", fmt.Sprintf("DHCLIENT_ROUTE_PRIORITY=%d", priority), } _, err = ifcfg.WriteString(strings.Join(contents, "\n")) if err != nil { return err } priority += 100 } args := append([]string{"ifup", "--timeout", "1"}, interfaces...) return runCmd(exec.Command("/usr/sbin/wicked", args...)) } // disableNM writes an ifcfg file with DHCP and NetworkManager disabled. func disableNM(iface string) error { logger.Debugf("write disabling ifcfg-%s config", iface) filename := "/etc/sysconfig/network-scripts/ifcfg-" + iface ifcfg, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) if err == nil { defer closer(ifcfg) contents := []string{ googleComment, fmt.Sprintf("DEVICE=%s", iface), "BOOTPROTO=none", "DEFROUTE=no", "IPV6INIT=no", "NM_CONTROLLED=no", "NOZEROCONF=yes", } _, err = ifcfg.WriteString(strings.Join(contents, "\n")) return err } if os.IsExist(err) { return nil } return err } guest-agent-20220622.00/google_guest_agent/addresses_integ_test.go000066400000000000000000000054011425470135300250120ustar00rootroot00000000000000// Copyright 2021 Google LLC // // 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. // +build integration package main import ( "context" "fmt" "strings" "testing" ) const testIp = "192.168.0.0" func TestAddAndRemoveLocalRoute(t *testing.T) { metdata, err := getMetadata(context.Context(), false) if err != nil { t.Fatalf("failed to get metadata, err %v", err) } iface, err := getInterfaceByMAC(metdata.Instance.NetworkInterfaces[0].Mac) if err != nil { t.Fatalf("failed to get interface from mac, err %v", err) } // test add local route if err := removeLocalRoute(testIp, iface.Name); err != nil { t.Fatalf("failed to remove local route, err %v", err) } if err := addLocalRoute(testIp, iface.Name); err != nil { t.Fatalf("add test local route should not failed, err %v", err) } res, err := getLocalRoutes(iface.Name) if err != nil { t.Fatalf("get local route should not failed, err %v", err) } exist := false for _, route := range res { if strings.Contains(route, fmt.Sprintf("local %s/24", testIp)) { exist = true } } if !exist { t.Fatalf("route %s is not added", testIp) } // test remove local route if err := removeLocalRoute(testIp, iface.Name); err != nil { t.Fatalf("add test local route should not failed") } res, err := getLocalRoutes(iface.Name) if err != nil { t.Fatalf("ip route list should not failed, err %s", res.err) } for _, route := range res { if strings.Contains(route, fmt.Sprintf("local %s/24", testIp)) { t.Fatalf("route %s should be removed but exist", testIp) } } } func TestGetLocalRoute(t *testing.T) { metdata, err := getMetadata(context.Context(), false) if err != nil { t.Fatalf("failed to get metadata, err %v", err) } iface, err := getInterfaceByMAC(metdata.Instance.NetworkInterfaces[0].Mac) if err != nil { t.Fatalf("failed to get interface from mac, err %v", err) } if err := addLocalRoute(testIp, iface.Name); err != nil { t.Fatalf("add test local route should not failed, err %v", err) } routes, err := getLocalRoutes(iface.Name) if err != nil { t.Fatalf("get local routes should not failed, err %v", err) } if len(routes) != 1 { t.Fatal("find unexpected local route %s.", routes[0]) } if routes[0] != testIp { t.Fatal("find unexpected local route %s.", routes[0]) } } guest-agent-20220622.00/google_guest_agent/addresses_test.go000066400000000000000000000210501425470135300236220ustar00rootroot00000000000000// 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. package main import ( "encoding/json" "reflect" "testing" "github.com/go-ini/ini" ) func TestCompareRoutes(t *testing.T) { var tests = []struct { forwarded, metadata, wantAdd, wantRm []string }{ // These should return toAdd: // In Md, not present {nil, []string{"1.2.3.4"}, []string{"1.2.3.4"}, nil}, {nil, []string{"1.2.3.4", "5.6.7.8"}, []string{"1.2.3.4", "5.6.7.8"}, nil}, // These should return toRm: // Present, not in Md {[]string{"1.2.3.4"}, nil, nil, []string{"1.2.3.4"}}, {[]string{"1.2.3.4", "5.6.7.8"}, []string{"5.6.7.8"}, nil, []string{"1.2.3.4"}}, // These should return nil, nil: // Present, in Md {[]string{"1.2.3.4"}, []string{"1.2.3.4"}, nil, nil}, {[]string{"1.2.3.4", "5.6.7.8"}, []string{"1.2.3.4", "5.6.7.8"}, nil, nil}, {[]string{"1.2.3.4", "5.6.7.8"}, []string{"1.2.3.4", "5.6.7.8"}, nil, nil}, } for idx, tt := range tests { toAdd, toRm := compareRoutes(tt.forwarded, tt.metadata) if !reflect.DeepEqual(tt.wantAdd, toAdd) { t.Errorf("case %d: toAdd does not match expected: forwarded: %q, metadata: %q, got: %q, want: %q", idx, tt.forwarded, tt.metadata, toAdd, tt.wantAdd) } if !reflect.DeepEqual(tt.wantRm, toRm) { t.Errorf("case %d: toRm does not match expected: forwarded: %q, metadata: %q, got: %q, want: %q", idx, tt.forwarded, tt.metadata, toRm, tt.wantRm) } } } func TestAddressDisabled(t *testing.T) { var tests = []struct { name string data []byte md *metadata want bool }{ {"not explicitly disabled", []byte(""), &metadata{}, false}, {"enabled in cfg only", []byte("[addressManager]\ndisable=false"), &metadata{}, false}, {"disabled in cfg only", []byte("[addressManager]\ndisable=true"), &metadata{}, true}, {"disabled in cfg, enabled in instance metadata", []byte("[addressManager]\ndisable=true"), &metadata{Instance: instance{Attributes: attributes{DisableAddressManager: mkptr(false)}}}, true}, {"enabled in cfg, disabled in instance metadata", []byte("[addressManager]\ndisable=false"), &metadata{Instance: instance{Attributes: attributes{DisableAddressManager: mkptr(true)}}}, false}, {"enabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAddressManager: mkptr(false)}}}, false}, {"enabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{DisableAddressManager: mkptr(false)}}}, false}, {"disabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAddressManager: mkptr(true)}}}, true}, {"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAddressManager: mkptr(false)}}, Project: project{Attributes: attributes{DisableAddressManager: mkptr(true)}}}, false}, {"disabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{DisableAddressManager: mkptr(true)}}}, true}, } for _, tt := range tests { cfg, err := ini.InsensitiveLoad(tt.data) if err != nil { t.Errorf("test case %q: error parsing config: %v", tt.name, err) continue } if cfg == nil { cfg = &ini.File{} } newMetadata = tt.md config = cfg got := (&addressMgr{}).disabled("") if got != tt.want { t.Errorf("test case %q, addressMgr.disabled() got: %t, want: %t", tt.name, got, tt.want) } } } func TestAddressDiff(t *testing.T) { var tests = []struct { name string data []byte md *metadata want bool }{ {"not set", []byte(""), &metadata{}, false}, {"enabled in cfg only", []byte("[wsfc]\nenable=true"), &metadata{}, true}, {"disabled in cfg only", []byte("[wsfc]\nenable=false"), &metadata{}, false}, {"disabled in cfg, enabled in instance metadata", []byte("[wsfc]\nenable=false"), &metadata{Instance: instance{Attributes: attributes{EnableWSFC: mkptr(true)}}}, false}, {"enabled in cfg, disabled in instance metadata", []byte("[wsfc]\nenable=true"), &metadata{Instance: instance{Attributes: attributes{EnableWSFC: mkptr(false)}}}, true}, {"enabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableWSFC: mkptr(true)}}}, true}, {"enabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{EnableWSFC: mkptr(true)}}}, true}, {"disabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableWSFC: mkptr(false)}}}, false}, {"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableWSFC: mkptr(true)}}, Project: project{Attributes: attributes{EnableWSFC: mkptr(false)}}}, true}, {"disabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{EnableWSFC: mkptr(false)}}}, false}, } for _, tt := range tests { cfg, err := ini.InsensitiveLoad(tt.data) if err != nil { t.Errorf("test case %q: error parsing config: %v", tt.name, err) continue } if cfg == nil { cfg = &ini.File{} } oldWSFCEnable = false oldMetadata = &metadata{} newMetadata = tt.md config = cfg got := (&addressMgr{}).diff() if got != tt.want { t.Errorf("test case %q, addresses.diff() got: %t, want: %t", tt.name, got, tt.want) } } } func TestWsfcFilter(t *testing.T) { var tests = []struct { metaDataJSON []byte expectedIps []string }{ // signle nic with enable-wsfc set to true {[]byte(`{"instance":{"attributes":{"enable-wsfc":"true"}, "networkInterfaces":[{"forwardedIps":["192.168.0.0", "192.168.0.1"]}]}}`), []string{}}, // multi nic with enable-wsfc set to true {[]byte(`{"instance":{"attributes":{"enable-wsfc":"true"}, "networkInterfaces":[{"forwardedIps":["192.168.0.0", "192.168.0.1"]},{"forwardedIps":["192.168.0.2"]}]}}`), []string{}}, // filter with wsfc-addrs {[]byte(`{"instance":{"attributes":{"wsfc-addrs":"192.168.0.1"}, "networkInterfaces":[{"forwardedIps":["192.168.0.0", "192.168.0.1"]}]}}`), []string{"192.168.0.0"}}, // filter with both wsfc-addrs and enable-wsfc flag {[]byte(`{"instance":{"attributes":{"wsfc-addrs":"192.168.0.1", "enable-wsfc":"true"}, "networkInterfaces":[{"forwardedIps":["192.168.0.0", "192.168.0.1"]}]}}`), []string{"192.168.0.0"}}, // filter with invalid wsfc-addrs {[]byte(`{"instance":{"attributes":{"wsfc-addrs":"192.168.0"}, "networkInterfaces":[{"forwardedIps":["192.168.0.0", "192.168.0.1"]}]}}`), []string{"192.168.0.0", "192.168.0.1"}}, } config = ini.Empty() for idx, tt := range tests { var md metadata if err := json.Unmarshal(tt.metaDataJSON, &md); err != nil { t.Error("failed to unmarshal test JSON:", tt, err) } newMetadata = &md testAddress := addressMgr{} testAddress.applyWSFCFilter() forwardedIps := []string{} for _, ni := range newMetadata.Instance.NetworkInterfaces { forwardedIps = append(forwardedIps, ni.ForwardedIps...) } if !reflect.DeepEqual(forwardedIps, tt.expectedIps) { t.Errorf("wsfc filter failed test %d: expect - %q, actual - %q", idx, tt.expectedIps, forwardedIps) } } } func TestWsfcFlagTriggerAddressDiff(t *testing.T) { var tests = []struct { newMetadata, oldMetadata *metadata }{ // trigger diff on wsfc-addrs {&metadata{Instance: instance{Attributes: attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata{}}, {&metadata{Project: project{Attributes: attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata{}}, {&metadata{Instance: instance{Attributes: attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata{Instance: instance{Attributes: attributes{WSFCAddresses: "192.168.0.2"}}}}, {&metadata{Project: project{Attributes: attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata{Project: project{Attributes: attributes{WSFCAddresses: "192.168.0.2"}}}}, } config = ini.Empty() for _, tt := range tests { oldWSFCAddresses = tt.oldMetadata.Instance.Attributes.WSFCAddresses newMetadata = tt.newMetadata oldMetadata = tt.oldMetadata testAddress := addressMgr{} if !testAddress.diff() { t.Errorf("old: %v new: %v doesn't trigger diff.", tt.oldMetadata, tt.newMetadata) } } } guest-agent-20220622.00/google_guest_agent/addresses_unix.go000066400000000000000000000025651425470135300236400ustar00rootroot00000000000000// 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. // +build !windows package main import ( "errors" "net" ) // TODO: addLocalRoute and addRoute should be merged with the addition of ipForwardType to ipForwardEntry. func addIPForwardEntry(route ipForwardEntry) error { return errors.New("addIPForwardEntry unimplemented on non Windows systems") } // TODO: getLocalRoutes and getIPForwardEntries should be merged. func getIPForwardEntries() ([]ipForwardEntry, error) { return nil, errors.New("getIPForwardEntries unimplemented on non Windows systems") } func addAddress(ip net.IP, mask net.IPMask, index uint32) error { return errors.New("addAddress unimplemented on non Windows systems") } func removeAddress(ip net.IP, index uint32) error { return errors.New("removeAddress unimplemented on non Windows systems") } guest-agent-20220622.00/google_guest_agent/addresses_windows.go000066400000000000000000000220641425470135300243430ustar00rootroot00000000000000// 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. // +build windows package main import ( "bytes" "encoding/binary" "fmt" "net" "syscall" "unsafe" "golang.org/x/sys/windows" ) var ( ipHlpAPI = windows.NewLazySystemDLL("iphlpapi.dll") procAddIPAddress = ipHlpAPI.NewProc("AddIPAddress") procDeleteIPAddress = ipHlpAPI.NewProc("DeleteIPAddress") procCreateIpForwardEntry = ipHlpAPI.NewProc("CreateIpForwardEntry") procDeleteIpForwardEntry = ipHlpAPI.NewProc("DeleteIpForwardEntry") procGetIpForwardTable = ipHlpAPI.NewProc("GetIpForwardTable") procGetIpInterfaceEntry = ipHlpAPI.NewProc("GetIpInterfaceEntry") procSetIpInterfaceEntry = ipHlpAPI.NewProc("SetIpInterfaceEntry") procCreateUnicastIpAddressEntry = ipHlpAPI.NewProc("CreateUnicastIpAddressEntry") procInitializeUnicastIpAddressEntry = ipHlpAPI.NewProc("InitializeUnicastIpAddressEntry") procGetUnicastIpAddressEntry = ipHlpAPI.NewProc("GetUnicastIpAddressEntry") procDeleteUnicastIpAddressEntry = ipHlpAPI.NewProc("DeleteUnicastIpAddressEntry") ) const ( AF_NET = 2 AF_INET6 = 23 ) type MIB_IPFORWARD_TYPE DWORD const ( MIB_IPROUTE_TYPE_OTHER MIB_IPFORWARD_TYPE = 1 MIB_IPROUTE_TYPE_INVALID = 2 MIB_IPROUTE_TYPE_DIRECT = 3 MIB_IPROUTE_TYPE_INDIRECT = 4 ) type MIB_IPFORWARD_PROTO DWORD const MIB_IPPROTO_NETMGMT MIB_IPFORWARD_PROTO = 3 type ( in_addr struct { S_un struct { S_addr uint32 } } SOCKADDR_IN struct { sin_family int16 sin_addr in_addr } SOCKADDR_INET struct { Ipv4 SOCKADDR_IN si_family int16 } NET_LUID struct { Value uint64 Info struct { NetLuidIndex uint64 IfType uint64 } } MIB_UNICASTIPADDRESS_ROW struct { Address SOCKADDR_INET InterfaceLuid NET_LUID InterfaceIndex uint32 PrefixOrigin uint32 SuffixOrigin uint32 ValidLifetime uint32 PreferredLifetime uint32 OnLinkPrefixLength uint8 SkipAsSource bool } IF_INDEX DWORD MIB_IPFORWARDROW struct { dwForwardDest uint32 dwForwardMask uint32 dwForwardPolicy uint32 dwForwardNextHop uint32 dwForwardIfIndex IF_INDEX dwForwardType MIB_IPFORWARD_TYPE dwForwardProto MIB_IPFORWARD_PROTO dwForwardAge int32 dwForwardNextHopAS int32 dwForwardMetric1 int32 dwForwardMetric2 int32 dwForwardMetric3 int32 dwForwardMetric4 int32 dwForwardMetric5 int32 } ) func addAddress(ip net.IP, mask net.IPMask, index uint32) error { // CreateUnicastIpAddressEntry only available Vista onwards. if err := procCreateUnicastIpAddressEntry.Find(); err != nil { return addIPAddress(ip, mask, index) } subnet, _ := mask.Size() return createUnicastIpAddressEntry(ip, uint8(subnet), index) } func removeAddress(ip net.IP, index uint32) error { // DeleteUnicastIpAddressEntry only available Vista onwards. if err := procDeleteUnicastIpAddressEntry.Find(); err != nil { return deleteIPAddress(ip) } return deleteUnicastIpAddressEntry(ip, index) } func createUnicastIpAddressEntry(ip net.IP, prefix uint8, index uint32) error { ipRow := new(MIB_UNICASTIPADDRESS_ROW) // No return value. procInitializeUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))) ipRow.InterfaceIndex = index ipRow.OnLinkPrefixLength = prefix // https://blogs.technet.microsoft.com/rmilne/2012/02/08/fine-grained-control-when-registering-multiple-ip-addresses-on-a-network-card/ ipRow.SkipAsSource = true ipRow.Address.si_family = AF_NET ipRow.Address.Ipv4.sin_family = AF_NET ipRow.Address.Ipv4.sin_addr.S_un.S_addr = binary.LittleEndian.Uint32(ip.To4()) if ret, _, _ := procCreateUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from CreateUnicastIpAddressEntry: %s", syscall.Errno(ret)) } return nil } func deleteUnicastIpAddressEntry(ip net.IP, index uint32) error { ipRow := new(MIB_UNICASTIPADDRESS_ROW) ipRow.InterfaceIndex = index ipRow.Address.si_family = AF_NET ipRow.Address.Ipv4.sin_family = AF_NET ipRow.Address.Ipv4.sin_addr.S_un.S_addr = binary.LittleEndian.Uint32(ip.To4()) ret, _, _ := procGetUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))) // ERROR_NOT_FOUND if ret == 1168 { // This address was added by addIPAddress(), need to remove with deleteIPAddress() return deleteIPAddress(ip) } if ret != 0 { return fmt.Errorf("nonzero return code from GetUnicastIpAddressEntry: %s", syscall.Errno(ret)) } if ret, _, _ := procDeleteUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 { return fmt.Errorf("nonzero return code from DeleteUnicastIpAddressEntry: %s", syscall.Errno(ret)) } return nil } func addIPAddress(ip net.IP, mask net.IPMask, index uint32) error { var nteC int var nteI int ret, _, _ := procAddIPAddress.Call( uintptr(binary.LittleEndian.Uint32(ip.To4())), uintptr(binary.LittleEndian.Uint32(mask)), uintptr(index), uintptr(unsafe.Pointer(&nteC)), uintptr(unsafe.Pointer(&nteI))) if ret != 0 { return fmt.Errorf("nonzero return code from AddIPAddress: %s", syscall.Errno(ret)) } return nil } func deleteIPAddress(ip net.IP) error { ip = ip.To4() b := make([]byte, 1) ai := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) l := uint32(0) syscall.GetAdaptersInfo(ai, &l) b = make([]byte, int32(l)) ai = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) if err := syscall.GetAdaptersInfo(ai, &l); err != nil { return err } for ; ai != nil; ai = ai.Next { for ipl := &ai.IpAddressList; ipl != nil; ipl = ipl.Next { ipb := bytes.Trim(ipl.IpAddress.String[:], "\x00") if string(ipb) != ip.String() { continue } nteC := ipl.Context ret, _, _ := procDeleteIPAddress.Call(uintptr(nteC)) if ret != 0 { return fmt.Errorf("nonzero return code from DeleteIPAddress: %s", syscall.Errno(ret)) } return nil } } return fmt.Errorf("did not find address %s on system", ip) } func getIPForwardEntries() ([]ipForwardEntry, error) { buf := make([]byte, 1) size := uint32(len(buf)) // First call gets the size of MIB_IPFORWARDTABLE. procGetIpForwardTable.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), 0, ) buf = make([]byte, size) if ret, _, _ := procGetIpForwardTable.Call( uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), 0, ); ret != 0 { return nil, fmt.Errorf("nonzero return code from GetIpForwardTable: %s", syscall.Errno(ret)) } /* struct MIB_IPFORWARDTABLE { DWORD dwNumEntries; MIB_IPFORWARDROW table[ANY_SIZE]; } */ numEntries := *(*uint32)(unsafe.Pointer(&buf[0])) // Walk through the returned table for each entry. var fes []ipForwardEntry for i := uint32(0); i < numEntries; i++ { // Extract each MIB_IPFORWARDROW from MIB_IPFORWARDTABLE fr := *((*MIB_IPFORWARDROW)(unsafe.Pointer( (uintptr(unsafe.Pointer(&buf[0])) + unsafe.Sizeof(numEntries)) + (unsafe.Sizeof(MIB_IPFORWARDROW{}) * uintptr(i)), ))) fd := make([]byte, 4) binary.LittleEndian.PutUint32(fd, uint32(fr.dwForwardDest)) fm := make([]byte, 4) binary.LittleEndian.PutUint32(fm, uint32(fr.dwForwardMask)) nh := make([]byte, 4) binary.LittleEndian.PutUint32(nh, uint32(fr.dwForwardNextHop)) fe := ipForwardEntry{ ipForwardDest: net.IP(fd), ipForwardMask: net.IPMask(fm), ipForwardNextHop: net.IP(nh), ipForwardIfIndex: int32(fr.dwForwardIfIndex), ipForwardMetric1: fr.dwForwardMetric1, } fes = append(fes, fe) } return fes, nil } func addIPForwardEntry(fe ipForwardEntry) error { // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-createipforwardentry fr := &MIB_IPFORWARDROW{ dwForwardDest: binary.LittleEndian.Uint32(fe.ipForwardDest.To4()), dwForwardMask: binary.LittleEndian.Uint32(fe.ipForwardMask), dwForwardPolicy: 0, // unused dwForwardNextHop: binary.LittleEndian.Uint32(fe.ipForwardNextHop.To4()), dwForwardIfIndex: IF_INDEX(fe.ipForwardIfIndex), dwForwardType: MIB_IPROUTE_TYPE_INDIRECT, // unused dwForwardProto: MIB_IPPROTO_NETMGMT, dwForwardAge: 0, // unused dwForwardNextHopAS: 0, // unused dwForwardMetric1: fe.ipForwardMetric1, dwForwardMetric2: -1, // unused dwForwardMetric3: -1, // unused dwForwardMetric4: -1, // unused dwForwardMetric5: -1, // unused } if ret, _, _ := procCreateIpForwardEntry.Call(uintptr(unsafe.Pointer(fr))); ret != 0 { return fmt.Errorf("nonzero return code from CreateIpForwardEntry: %s", syscall.Errno(ret)) } return nil } guest-agent-20220622.00/google_guest_agent/clock.go000066400000000000000000000034251425470135300217070ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "os/exec" "runtime" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) type clockskewMgr struct{} func (a *clockskewMgr) diff() bool { return oldMetadata.Instance.VirtualClock.DriftToken != newMetadata.Instance.VirtualClock.DriftToken } func (a *clockskewMgr) timeout() bool { return false } func (a *clockskewMgr) disabled(os string) (disabled bool) { enabled := config.Section("Daemons").Key("clock_skew_daemon").MustBool(true) return os == "windows" || !enabled } func (a *clockskewMgr) set() error { if runtime.GOOS == "freebsd" { err := runCmd(exec.Command("service", "ntpd", "status")) if err == nil { if err := runCmd(exec.Command("service", "ntpd", "stop")); err != nil { return err } defer func() { if err := runCmd(exec.Command("service", "ntpd", "start")); err != nil { logger.Warningf("Error starting 'ntpd' after clock sync: %v.", err) } }() } // TODO get server return runCmd(exec.Command("ntpdate", "169.254.169.254")) } res := runCmdOutput(exec.Command("/sbin/hwclock", "--hctosys", "-u", "--noadjfile")) if res.ExitCode() != 0 || res.Stderr() != "" { return error(res) } return nil } guest-agent-20220622.00/google_guest_agent/diagnostics.go000066400000000000000000000075211425470135300231240ustar00rootroot00000000000000// Copyright 2018 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. package main import ( "encoding/json" "os/exec" "reflect" "strconv" "sync/atomic" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) const diagnosticsCmd = `C:\Program Files\Google\Compute Engine\diagnostics\diagnostics.exe` var ( diagnosticsRegKey = "Diagnostics" diagnosticsDisabled = false // Indicate whether an existing job is runing to collect logs // 0 -> not running, 1 -> running isDiagnosticsRunning int32 = 0 ) type diagnosticsEntry struct { SignedURL string ExpireOn string Trace bool } func (k diagnosticsEntry) expired() bool { expired, err := utils.CheckExpired(k.ExpireOn) if err != nil { if !utils.ContainsString(k.ExpireOn, badExpire) { logger.Errorf("error parsing time: %s", err) badExpire = append(badExpire, k.ExpireOn) } return true } return expired } type diagnosticsMgr struct{} func (d *diagnosticsMgr) diff() bool { return !reflect.DeepEqual(newMetadata.Instance.Attributes.Diagnostics, oldMetadata.Instance.Attributes.Diagnostics) } func (d *diagnosticsMgr) timeout() bool { return false } func (d *diagnosticsMgr) disabled(os string) (disabled bool) { if os != "windows" { return true } defer func() { if disabled != diagnosticsDisabled { diagnosticsDisabled = disabled logStatus("diagnostics", disabled) } }() // Diagnostics are opt-in and enabled by default. var err error var enabled bool enabled, err = strconv.ParseBool(config.Section("diagnostics").Key("enable").String()) if err == nil { return !enabled } if newMetadata.Instance.Attributes.EnableDiagnostics != nil { enabled = *newMetadata.Instance.Attributes.EnableDiagnostics return !enabled } if newMetadata.Project.Attributes.EnableDiagnostics != nil { enabled = *newMetadata.Project.Attributes.EnableDiagnostics return !enabled } return diagnosticsDisabled } func (d *diagnosticsMgr) set() error { logger.Infof("Diagnostics: logs export requested.") diagnosticsEntries, err := readRegMultiString(regKeyBase, diagnosticsRegKey) if err != nil && err != errRegNotExist { return err } strEntry := newMetadata.Instance.Attributes.Diagnostics if utils.ContainsString(strEntry, diagnosticsEntries) { return nil } diagnosticsEntries = append(diagnosticsEntries, strEntry) var entry diagnosticsEntry if err := json.Unmarshal([]byte(strEntry), &entry); err != nil { return err } if entry.SignedURL == "" || entry.expired() { return nil } args := []string{ "-signedUrl", entry.SignedURL, } if entry.Trace { args = append(args, "-trace") } // If no existing running job, set it to 1 and block other requests if !atomic.CompareAndSwapInt32(&isDiagnosticsRunning, 0, 1) { logger.Infof("Diagnostics: reject the request, as an existing process is collecting logs from the system") return nil } cmd := exec.Command(diagnosticsCmd, args...) go func() { logger.Infof("Diagnostics: collecting logs from the system.") out, err := cmd.CombinedOutput() logger.Infof(string(out[:])) if err != nil { logger.Infof("Error collecting logs: %v", err) } // Job is done, unblock the following requests atomic.SwapInt32(&isDiagnosticsRunning, 0) }() return writeRegMultiString(regKeyBase, diagnosticsRegKey, diagnosticsEntries) } guest-agent-20220622.00/google_guest_agent/diagnostics_test.go000066400000000000000000000061241425470135300241610ustar00rootroot00000000000000// Copyright 2018 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. package main import ( "testing" "time" "github.com/go-ini/ini" ) func TestDiagnosticsEntryExpired(t *testing.T) { var tests = []struct { sTime string e bool }{ {time.Now().Add(5 * time.Minute).Format(time.RFC3339), false}, {time.Now().Add(-5 * time.Minute).Format(time.RFC3339), true}, {"some bad time", true}, } for _, tt := range tests { k := diagnosticsEntry{ExpireOn: tt.sTime} if tt.e != k.expired() { t.Errorf("diagnosticsEntry.expired() with ExpiredOn %q should return %t", k.ExpireOn, tt.e) } } } func TestDiagnosticsDisabled(t *testing.T) { var tests = []struct { name string data []byte md *metadata want bool }{ {"not explicitly enabled", []byte(""), &metadata{}, false}, {"enabled in cfg only", []byte("[diagnostics]\nenable=true"), &metadata{}, false}, {"disabled in cfg only", []byte("[diagnostics]\nenable=false"), &metadata{}, true}, {"disabled in cfg, enabled in instance metadata", []byte("[diagnostics]\nenable=false"), &metadata{Instance: instance{Attributes: attributes{EnableDiagnostics: mkptr(true)}}}, true}, {"enabled in cfg, disabled in instance metadata", []byte("[diagnostics]\nenable=true"), &metadata{Instance: instance{Attributes: attributes{EnableDiagnostics: mkptr(false)}}}, false}, {"enabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableDiagnostics: mkptr(true)}}}, false}, {"enabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{EnableDiagnostics: mkptr(true)}}}, false}, {"disabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableDiagnostics: mkptr(false)}}}, true}, {"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata{Instance: instance{Attributes: attributes{EnableDiagnostics: mkptr(true)}}, Project: project{Attributes: attributes{EnableDiagnostics: mkptr(false)}}}, false}, {"disabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{EnableDiagnostics: mkptr(false)}}}, true}, } for _, tt := range tests { cfg, err := ini.InsensitiveLoad(tt.data) if err != nil { t.Errorf("test case %q: error parsing config: %v", tt.name, err) continue } if cfg == nil { cfg = &ini.File{} } newMetadata = tt.md config = cfg got := (&diagnosticsMgr{}).disabled("windows") if got != tt.want { t.Errorf("test case %q, diagnostics.disabled() got: %t, want: %t", tt.name, got, tt.want) } } } guest-agent-20220622.00/google_guest_agent/getrelease.go000066400000000000000000000064571425470135300227440ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "fmt" "io/ioutil" "runtime" "strconv" "strings" ) type ver struct { major, minor, patch, length int } // release holds Linux distribution release information. type release struct { os string version ver } func (v ver) String() string { if v.major == 0 { return "" } ret := fmt.Sprintf("%d", v.major) if v.length > 1 { ret = fmt.Sprintf("%s.%d", ret, v.minor) } if v.length > 2 { ret = fmt.Sprintf("%s.%d", ret, v.patch) } return ret } func parseOSRelease(osRelease string) (release, error) { var ret release for _, line := range strings.Split(osRelease, "\n") { var id = line if id = strings.TrimPrefix(line, "ID="); id != line { if len(id) > 0 && id[0] == '"' { id = id[1:] } if len(id) > 0 && id[len(id)-1] == '"' { id = id[:len(id)-1] } ret.os = parseID(id) } if id = strings.TrimPrefix(line, "VERSION_ID="); id != line { if len(id) > 0 && id[0] == '"' { id = id[1:] } if len(id) > 0 && id[len(id)-1] == '"' { id = id[:len(id)-1] } version, err := parseVersion(id) if err != nil { return ret, err } ret.version = version } } return ret, nil } func parseSystemRelease(systemRelease string) (release, error) { var ret release var key = " release " idx := strings.Index(systemRelease, key) if idx == -1 { return ret, fmt.Errorf("SystemRelease does not match format") } ret.os = parseID(systemRelease[:idx]) versionFromRelease := strings.Split(systemRelease[idx+len(key):], " ")[0] version, err := parseVersion(versionFromRelease) if err != nil { return ret, err } ret.version = version return ret, nil } func parseVersion(version string) (ver, error) { versionparts := strings.Split(version, ".") ret := ver{length: len(versionparts)} // Must have at least major version. var err error ret.major, err = strconv.Atoi(versionparts[0]) if err != nil { return ret, err } if ret.length > 1 { ret.minor, err = strconv.Atoi(versionparts[1]) if err != nil { return ret, err } } if ret.length > 2 { ret.patch, err = strconv.Atoi(versionparts[2]) if err != nil { return ret, err } } return ret, nil } func parseID(id string) string { switch id { case "Red Hat Enterprise Linux Server": return "rhel" case "CentOS", "CentOS Linux": return "centos" default: return id } } func getRelease() (release, error) { if runtime.GOOS == "linux" { releaseFile, err := ioutil.ReadFile("/etc/os-release") if err == nil { return parseOSRelease(string(releaseFile)) } releaseFile, err = ioutil.ReadFile("/etc/system-release") if err == nil { return parseSystemRelease(string(releaseFile)) } } return release{}, fmt.Errorf("%s is a supported platform", runtime.GOOS) } guest-agent-20220622.00/google_guest_agent/getrelease_test.go000066400000000000000000000041101425470135300237630ustar00rootroot00000000000000// 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. package main import ( "reflect" "testing" ) func TestParseSystemRelease(t *testing.T) { tests := []struct { file string want release }{ {"Red Hat Enterprise Linux Server release 6.10 (Santiago)", release{os: "rhel", version: ver{6, 10, 0, 2}}}, {"Red Hat Enterprise Linux Server release 6.10.1", release{os: "rhel", version: ver{6, 10, 1, 3}}}, {"CentOS Linux release 7.6.1810 (Core)", release{os: "centos", version: ver{7, 6, 1810, 3}}}, } for _, tt := range tests { if got, err := parseSystemRelease(tt.file); err != nil || !reflect.DeepEqual(got, tt.want) { t.Errorf("parseSystemRelease(%s) incorrect return: got %v, want %v", tt.file, got, tt.want) } } } func TestParseOSRelease(t *testing.T) { tests := []struct { file string want release }{ {"ID=\"sles\"\nNAME=\"SLES\"\nVERSION=\"12-SP4\"\nVERSION_ID=12", release{os: "sles", version: ver{12, 0, 0, 1}}}, {"ID=sles\nNAME=\"SLES\"\nVERSION=\"12-SP4\"\nVERSION_ID=\"12.4\"", release{os: "sles", version: ver{12, 4, 0, 2}}}, {"ID=debian\nNAME=\"Debian GNU/Linux\"\nVERSION=\"9 (stretch)\"\nVERSION_ID=\"9\"", release{os: "debian", version: ver{9, 0, 0, 1}}}, {"ID=\"debian\"\nNAME=\"Debian GNU/Linux\"\nVERSION=9\nVERSION_ID=\"9\"", release{os: "debian", version: ver{9, 0, 0, 1}}}, } for _, tt := range tests { if got, err := parseOSRelease(tt.file); err != nil || !reflect.DeepEqual(got, tt.want) { t.Errorf("parseOSRelease(%s) incorrect return: got %v, want %v", tt.file, got, tt.want) } } } guest-agent-20220622.00/google_guest_agent/instance_setup.go000066400000000000000000000255341425470135300236450ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "context" "fmt" "io/ioutil" "net" "net/http" "os" "os/exec" "runtime" "sort" "strings" "time" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "github.com/go-ini/ini" ) func agentInit(ctx context.Context) { // Actions to take on agent startup. // // All platforms: // - Determine if metadata hostname can be resolved. // // On Windows: // - Add route to metadata server // On Linux: // - Generate SSH host keys (one time only). // - Generate boto.cfg (one time only). // - Set sysctl values. // - Set scheduler values. // - Run `google_optimize_local_ssd` script. // - Run `google_set_multiqueue` script. // TODO incorporate these scripts into the agent. liamh@12-11-19 if runtime.GOOS == "windows" { msg := "Could not set default route to metadata" fes, err := getIPForwardEntries() if err != nil { logger.Errorf("%s, error listing IPForwardEntries: %v", msg, err) return } // Choose the first adapter index that has the default route setup. // This is equivalent to how route.exe works when interface is not provided. var index int32 var found bool var metric int32 sort.Slice(fes, func(i, j int) bool { return fes[i].ipForwardIfIndex < fes[j].ipForwardIfIndex }) for _, fe := range fes { if fe.ipForwardDest.Equal(net.ParseIP("0.0.0.0")) { index = fe.ipForwardIfIndex metric = fe.ipForwardMetric1 found = true break } } if found == false { logger.Errorf("%s, could not find the default route in IPForwardEntries: %+v", msg, fes) return } iface, err := net.InterfaceByIndex(int(index)) if err != nil { logger.Errorf("%s, error from net.InterfaceByIndex(%d): %v", msg, index, err) return } forwardEntry := ipForwardEntry{ ipForwardDest: net.ParseIP("169.254.169.254"), ipForwardMask: net.IPv4Mask(255, 255, 255, 255), ipForwardNextHop: net.ParseIP("0.0.0.0"), ipForwardMetric1: metric, // This needs to be at least equal to the default route metric. ipForwardIfIndex: int32(iface.Index), } for _, fe := range fes { if fe.ipForwardDest.Equal(forwardEntry.ipForwardDest) && fe.ipForwardIfIndex == forwardEntry.ipForwardIfIndex { // No need to add entry, it's already setup. return } } logger.Infof("Adding route to metadata server on %q (index: %d)", iface.Name, iface.Index) if err := addIPForwardEntry(forwardEntry); err != nil { logger.Errorf("%s, error adding IPForwardEntry on %q (index: %d): %v", msg, iface.Name, iface.Index, err) return } } else { // Linux instance setup. defer runCmd(exec.Command("systemd-notify", "--ready")) defer logger.Debugf("notify systemd") if config.Section("Snapshots").Key("enabled").MustBool(false) { logger.Infof("Snapshot listener enabled") snapshotServiceIP := config.Section("Snapshots").Key("snapshot_service_ip").MustString("169.254.169.254") snapshotServicePort := config.Section("Snapshots").Key("snapshot_service_port").MustInt(8081) startSnapshotListener(snapshotServiceIP, snapshotServicePort) } // These scripts are run regardless of metadata/network access and config options. for _, script := range []string{"optimize_local_ssd", "set_multiqueue"} { if config.Section("InstanceSetup").Key(script).MustBool(true) { if err := runCmd(exec.Command("google_" + script)); err != nil { logger.Warningf("Failed to run %q script: %v", "google_"+script, err) } } } // Below actions happen on every agent start. They only need to // run once per boot, but it's harmless to run them on every // boot. If this changes, we will hook these to an explicit // on-boot signal. logger.Debugf("set IO scheduler config") if err := setIOScheduler(); err != nil { logger.Warningf("Failed to set IO scheduler: %v", err) } // Allow users to opt out of below instance setup actions. if !config.Section("InstanceSetup").Key("network_enabled").MustBool(true) { logger.Infof("InstanceSetup.network_enabled is false, skipping setup actions that require metadata") return } // The below actions require metadata to be set, so if it // hasn't yet been set, wait on it here. In instances without // network access, this will become an indefinite wait. // TODO: split agentInit into needs-network and no-network functions. for newMetadata == nil { logger.Debugf("populate first time metadata...") newMetadata, _ = getMetadata(ctx, false) time.Sleep(1 * time.Second) } // Disable overcommit accounting; e2 instances only. parts := strings.Split(newMetadata.Instance.MachineType, "/") if strings.HasPrefix(parts[len(parts)-1], "e2-") { if err := runCmd(exec.Command("sysctl", "vm.overcommit_memory=1")); err != nil { logger.Warningf("Failed to run 'sysctl vm.overcommit_memory=1': %v", err) } } // Check if instance ID has changed, and if so, consider this // the first boot of the instance. // TODO Also do this for windows. liamh@13-11-2019 instanceIDFile := config.Section("Instance").Key("instance_id_dir").MustString("/etc") + "/google_instance_id" instanceID, err := ioutil.ReadFile(instanceIDFile) if err != nil && !os.IsNotExist(err) { logger.Warningf("Not running first-boot actions, error reading instance ID: %v", err) } else { if string(instanceID) == "" { // If the file didn't exist or was empty, try legacy key from instance configs. instanceID = []byte(config.Section("Instance").Key("instance_id").String()) // Write instance ID to file for next time before moving on. towrite := fmt.Sprintf("%s\n", newMetadata.Instance.ID.String()) if err := ioutil.WriteFile(instanceIDFile, []byte(towrite), 0644); err != nil { logger.Warningf("Failed to write instance ID file: %v", err) } } if newMetadata.Instance.ID.String() != strings.TrimSpace(string(instanceID)) { logger.Infof("Instance ID changed, running first-boot actions") if config.Section("InstanceSetup").Key("set_host_keys").MustBool(true) { if err := generateSSHKeys(); err != nil { logger.Warningf("Failed to generate SSH keys: %v", err) } } if config.Section("InstanceSetup").Key("set_boto_config").MustBool(true) { if err := generateBotoConfig(); err != nil { logger.Warningf("Failed to create boto.cfg: %v", err) } } // Write instance ID to file. towrite := fmt.Sprintf("%s\n", newMetadata.Instance.ID.String()) if err := ioutil.WriteFile(instanceIDFile, []byte(towrite), 0644); err != nil { logger.Warningf("Failed to write instance ID file: %v", err) } } } } } func generateSSHKeys() error { hostKeyDir := config.Section("InstanceSetup").Key("host_key_dir").MustString("/etc/ssh") dir, err := os.Open(hostKeyDir) if err != nil { return err } defer dir.Close() files, err := dir.Readdirnames(0) if err != nil { return err } keytypes := make(map[string]bool) // Find keys present on disk, and deduce their type from filename. prefix := "ssh_host_" suffix := "_key" for _, file := range files { if strings.HasPrefix(file, prefix) && strings.HasSuffix(file, suffix) && len(file) > len(prefix+suffix) { keytype := file keytype = strings.TrimPrefix(keytype, prefix) keytype = strings.TrimSuffix(keytype, suffix) keytypes[keytype] = true } } // List keys we should generate, according to the config. configKeys := config.Section("InstanceSetup").Key("host_key_types").MustString("ecdsa,ed25519,rsa") for _, keytype := range strings.Split(configKeys, ",") { keytypes[keytype] = true } // Generate new keys and upload to guest attributes. for keytype := range keytypes { keyfile := fmt.Sprintf("%s/ssh_host_%s_key", hostKeyDir, keytype) if err := runCmd(exec.Command("ssh-keygen", "-t", keytype, "-f", keyfile+".temp", "-N", "", "-q")); err != nil { logger.Warningf("Failed to generate SSH host key %q: %v", keyfile, err) continue } if err := os.Chmod(keyfile+".temp", 0600); err != nil { logger.Errorf("Failed to chmod SSH host key %q: %v", keyfile, err) continue } if err := os.Chmod(keyfile+".temp.pub", 0644); err != nil { logger.Errorf("Failed to chmod SSH host key %q: %v", keyfile+".pub", err) continue } if err := os.Rename(keyfile+".temp", keyfile); err != nil { logger.Errorf("Failed to overwrite %q: %v", keyfile, err) continue } if err := os.Rename(keyfile+".temp.pub", keyfile+".pub"); err != nil { logger.Errorf("Failed to overwrite %q: %v", keyfile+".pub", err) continue } pubKey, err := ioutil.ReadFile(keyfile + ".pub") if err != nil { logger.Errorf("Can't read %s public key: %v", keytype, err) continue } if vals := strings.Split(string(pubKey), " "); len(vals) >= 2 { if err := writeGuestAttributes("hostkeys/"+vals[0], vals[1]); err != nil { logger.Errorf("Failed to upload %s key to guest attributes: %v", keytype, err) } } else { logger.Warningf("Generated key is malformed, not uploading") } } runCmd(exec.Command("restorecon", "-FR", hostKeyDir)) return nil } func generateBotoConfig() error { path := "/etc/boto.cfg" botoCfg, err := ini.LooseLoad(path, path+".template") if err != nil { return err } botoCfg.Section("GSUtil").Key("default_project_id").SetValue(newMetadata.Project.NumericProjectID.String()) botoCfg.Section("GSUtil").Key("default_api_version").SetValue("2") botoCfg.Section("GoogleCompute").Key("service_account").SetValue("default") return botoCfg.SaveTo(path) } func writeGuestAttributes(key, value string) error { logger.Debugf("write guest attribute %q", key) client := &http.Client{Timeout: defaultTimeout} finalURL := metadataURL + "instance/guest-attributes/" + key req, err := http.NewRequest("PUT", finalURL, strings.NewReader(value)) if err != nil { return err } req.Header.Add("Metadata-Flavor", "Google") _, err = client.Do(req) return err } func setIOScheduler() error { dir, err := os.Open("/sys/block") if err != nil { return err } defer dir.Close() devs, err := dir.Readdirnames(0) if err != nil { return err } for _, dev := range devs { // Detect if device is using MQ subsystem. stat, err := os.Stat("/sys/block/" + dev + "/mq") if err == nil && stat.IsDir() { f, err := os.OpenFile("/sys/block/"+dev+"/queue/scheduler", os.O_WRONLY|os.O_TRUNC, 0700) if err != nil { return err } _, err = f.Write([]byte("none")) if err != nil { return err } } } return nil } guest-agent-20220622.00/google_guest_agent/instance_setup_integ_test.go000066400000000000000000000150441425470135300260650ustar00rootroot00000000000000// Copyright 2021 Google LLC // // 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. // +build integration package main import ( "context" "io/ioutil" "os" "strings" "testing" ) const ( botoCfg = "/etc/boto.cfg" ) // TestInstanceSetupSSHKeys validates SSH keys are generated on first boot and not changed afterward. func TestInstanceSetupSSHKeys(t *testing.T) { cfg, err := parseConfig("") // get empty config if err != nil { t.Fatal("failed to init config object") } config = cfg // set the global defer func() { config = nil }() // unset at end of test tempdir, err := ioutil.TempDir("/tmp", "test_instance_setup") if err != nil { t.Fatal("failed to create working dir") } // Configure a non-standard instance ID dir for us to play with. config.Section("Instance").Key("instance_id_dir").SetValue(tempdir) config.Section("InstanceSetup").Key("host_key_dir").SetValue(tempdir) ctx := context.Background() agentInit(ctx) if _, err := os.Stat(tempdir + "/google_instance_id"); err != nil { t.Fatal("instance ID File was not created by agentInit") } dir, err := os.Open(tempdir) if err != nil { t.Fatal("failed to open working dir") } defer dir.Close() files, err := dir.Readdirnames(0) if err != nil { t.Fatal("failed to read files") } var keys []string for _, file := range files { if strings.HasPrefix(file, "ssh_host_") { keys = append(keys, file) } } if len(keys) == 0 { t.Fatal("instance setup didn't create SSH host keys") } // Remove one key file and run again to confirm SSH keys have not // changed because the instance ID file has not changed. if err := os.Remove(tempdir + "/" + keys[0]); err != nil { t.Fatal("failed to remove key file") } agentInit(ctx) if _, err := dir.Seek(0, 0); err != nil { t.Fatal("failed to rewind dir for second check") } files2, err := dir.Readdirnames(0) if err != nil { t.Fatal("failed to read files") } var keys2 []string for _, file := range files2 { if strings.HasPrefix(file, "ssh_host_") { keys2 = append(keys2, file) } if file == keys[0] { t.Fatalf("agentInit recreated key %s", file) } } if len(keys) == len(keys2) { t.Fatal("agentInit recreated SSH host keys") } } // TestInstanceSetupSSHKeysDisabled validates the config option to disable host // key generation is respected. func TestInstanceSetupSSHKeysDisabled(t *testing.T) { cfg, err := parseConfig("") // get empty config if err != nil { t.Fatal("failed to init config object") } config = cfg // set the global defer func() { config = nil }() // unset at end of test tempdir, err := ioutil.TempDir("/tmp", "test_instance_setup") if err != nil { t.Fatal("failed to create working dir") } // Configure a non-standard instance ID dir for us to play with. config.Section("Instance").Key("instance_id_dir").SetValue(tempdir) config.Section("InstanceSetup").Key("host_key_dir").SetValue(tempdir) // Disable SSH host key generation. config.Section("InstanceSetup").Key("set_host_keys").SetValue("false") ctx := context.Background() agentInit(ctx) dir, err := os.Open(tempdir) if err != nil { t.Fatal("failed to open working dir") } defer dir.Close() files, err := dir.Readdirnames(0) if err != nil { t.Fatal("failed to read files") } for _, file := range files { if strings.HasPrefix(file, "ssh_host_") { t.Fatal("agentInit created SSH host keys when disabled") } } } func TestInstanceSetupBotoConfig(t *testing.T) { cfg, err := parseConfig("") // get empty config if err != nil { t.Fatal("failed to init config object") } config = cfg // set the global defer func() { config = nil }() // unset at end of test tempdir, err := ioutil.TempDir("/tmp", "test_instance_setup") if err != nil { t.Fatal("failed to create working dir") } // Configure a non-standard instance ID dir for us to play with. config.Section("Instance").Key("instance_id_dir").SetValue(tempdir) config.Section("InstanceSetup").Key("host_key_dir").SetValue(tempdir) ctx := context.Background() if err := os.Rename(botoCfg, botoCfg+".bak"); err != nil { t.Fatalf("failed to move boto config: %v", err) } defer func() { // Restore file at end of test. if err := os.Rename(botoCfg+".bak", botoCfg); err != nil { t.Fatalf("failed to restore boto config: %v", err) } }() // Test it is created by default on first boot agentInit(ctx) if _, err := os.Stat(botoCfg); err != nil { t.Fatal("boto config was not created on first boot") } // Test it is not recreated on subsequent invocations if err := os.Remove(botoCfg); err != nil { t.Fatal("failed to remove boto config") } agentInit(ctx) if _, err := os.Stat(botoCfg); err == nil || !os.IsNotExist(err) { // If we didn't get an error, or if we got some other kind of error t.Fatal("boto config was recreated after first boot") } } func TestInstanceSetupBotoConfigDisabled(t *testing.T) { cfg, err := parseConfig("") // get empty config if err != nil { t.Fatal("failed to init config object") } config = cfg // set the global defer func() { config = nil }() // unset at end of test tempdir, err := ioutil.TempDir("/tmp", "test_instance_setup") if err != nil { t.Fatal("failed to create working dir") } // Configure a non-standard instance ID dir for us to play with. config.Section("Instance").Key("instance_id_dir").SetValue(tempdir) config.Section("InstanceSetup").Key("host_key_dir").SetValue(tempdir) ctx := context.Background() if err := os.Rename(botoCfg, botoCfg+".bak"); err != nil { t.Fatalf("failed to move boto config: %v", err) } defer func() { // Restore file at end of test. if err := os.Rename(botoCfg+".bak", botoCfg); err != nil { t.Fatalf("failed to restore boto config: %v", err) } }() // Test it is not created if disabled in config. config.Section("InstanceSetup").Key("set_boto_config").SetValue("false") agentInit(ctx) if _, err := os.Stat(botoCfg); err == nil || !os.IsNotExist(err) { // If we didn't get an error, or if we got some other kind of error t.Fatal("boto config was created when disabled in config") } } guest-agent-20220622.00/google_guest_agent/main.go000066400000000000000000000162741425470135300215460ustar00rootroot00000000000000// 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. // GCEGuestAgent is the Google Compute Engine guest agent executable. package main import ( "bytes" "context" "errors" "fmt" "io" "net" "net/url" "os" "os/exec" "runtime" "strings" "sync" "time" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "github.com/go-ini/ini" ) var ( programName = "GCEGuestAgent" version string ticker = time.Tick(70 * time.Second) oldMetadata, newMetadata *metadata config *ini.File osRelease release action string ) const ( winConfigPath = `C:\Program Files\Google\Compute Engine\instance_configs.cfg` configPath = `/etc/default/instance_configs.cfg` regKeyBase = `SOFTWARE\Google\ComputeEngine` ) type manager interface { diff() bool disabled(string) bool set() error timeout() bool } func logStatus(name string, disabled bool) { var status string switch disabled { case false: status = "enabled" case true: status = "disabled" } logger.Infof("GCE %s manager status: %s", name, status) } func parseConfig(file string) (*ini.File, error) { // Priority: file.cfg, file.cfg.distro, file.cfg.template cfg, err := ini.LoadSources(ini.LoadOptions{Loose: true, Insensitive: true}, file, file+".distro", file+".template") if err != nil { return nil, err } return cfg, nil } func closeFile(c io.Closer) { err := c.Close() if err != nil { logger.Warningf("Error closing file: %v.", err) } } func runUpdate() { var wg sync.WaitGroup mgrs := []manager{&addressMgr{}} switch runtime.GOOS { case "windows": mgrs = append(mgrs, []manager{newWsfcManager(), &winAccountsMgr{}, &diagnosticsMgr{}}...) default: mgrs = append(mgrs, []manager{&clockskewMgr{}, &osloginMgr{}, &accountsMgr{}}...) } for _, mgr := range mgrs { wg.Add(1) go func(mgr manager) { defer wg.Done() if mgr.disabled(runtime.GOOS) { logger.Debugf("manager %#v disabled, skipping", mgr) return } if !mgr.timeout() && !mgr.diff() { logger.Debugf("manager %#v reports no diff", mgr) return } logger.Debugf("running %#v manager", mgr) if err := mgr.set(); err != nil { logger.Errorf("error running %#v manager: %s", mgr, err) } }(mgr) } wg.Wait() } func run(ctx context.Context) { opts := logger.LogOpts{LoggerName: programName} if runtime.GOOS == "windows" { opts.FormatFunction = logFormatWindows opts.Writers = []io.Writer{&utils.SerialPort{Port: "COM1"}} } else { opts.FormatFunction = logFormat opts.Writers = []io.Writer{os.Stdout} // Local logging is syslog; we will just use stdout in Linux. opts.DisableLocalLogging = true } if os.Getenv("GUEST_AGENT_DEBUG") != "" { opts.Debug = true } var err error newMetadata, err = getMetadata(ctx, false) if err == nil { opts.ProjectName = newMetadata.Project.ProjectID } if err := logger.Init(ctx, opts); err != nil { fmt.Printf("Error initializing logger: %v", err) os.Exit(1) } logger.Infof("GCE Agent Started (version %s)", version) osRelease, err = getRelease() if err != nil && runtime.GOOS != "windows" { logger.Warningf("Couldn't detect OS release") } cfgfile := configPath if runtime.GOOS == "windows" { cfgfile = winConfigPath } config, err = parseConfig(cfgfile) if err != nil && !os.IsNotExist(err) { logger.Errorf("Error parsing config %s: %s", cfgfile, err) } agentInit(ctx) go func() { oldMetadata = &metadata{} webError := 0 for { var err error newMetadata, err = watchMetadata(ctx) if err != nil { // Only log the second web error to avoid transient errors and // not to spam the log on network failures. if webError == 1 { if urlErr, ok := err.(*url.Error); ok { if _, ok := urlErr.Err.(*net.OpError); ok { logger.Errorf("Network error when requesting metadata, make sure your instance has an active network and can reach the metadata server.") } } logger.Errorf("Error watching metadata: %s", err) } webError++ time.Sleep(5 * time.Second) continue } select { case <-ctx.Done(): return default: } runUpdate() oldMetadata = newMetadata webError = 0 } }() <-ctx.Done() logger.Infof("GCE Agent Stopped") } type execResult struct { // Return code. Set to -1 if we failed to run the command. code int // Stderr or err.Error if we failed to run the command. err string // Stdout or "" if we failed to run the command. out string } func (e execResult) Error() string { return strings.TrimSuffix(e.err, "\n") } func (e execResult) ExitCode() int { return e.code } func (e execResult) Stdout() string { return e.out } func (e execResult) Stderr() string { return e.err } func runCmd(cmd *exec.Cmd) error { res := runCmdOutput(cmd) if res.ExitCode() != 0 { return res } return nil } func runCmdOutput(cmd *exec.Cmd) *execResult { logger.Debugf("exec: %v", cmd) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { if ee, ok := err.(*exec.ExitError); ok { return &execResult{code: ee.ExitCode(), out: stdout.String(), err: stderr.String()} } return &execResult{code: -1, err: err.Error()} } return &execResult{code: 0, out: stdout.String()} } func runCmdOutputWithTimeout(timeoutSec time.Duration, name string, args ...string) *execResult { ctx, cancel := context.WithTimeout(context.Background(), timeoutSec) defer cancel() execResult := runCmdOutput(exec.CommandContext(ctx, name, args...)) if ctx.Err() != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) { execResult.code = 124 // By convention } return execResult } func logFormatWindows(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") // 2006/01/02 15:04:05 GCEGuestAgent This is a log message. return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func logFormat(e logger.LogEntry) string { switch e.Severity { case logger.Error, logger.Critical, logger.Debug: // ERROR file.go:82 This is a log message. return fmt.Sprintf("%s %s:%d %s", strings.ToUpper(e.Severity.String()), e.Source.File, e.Source.Line, e.Message) default: // This is a log message. return e.Message } } func closer(c io.Closer) { err := c.Close() if err != nil { logger.Warningf("Error closing %v: %v.", c, err) } } func main() { ctx := context.Background() var action string if len(os.Args) < 2 { action = "run" } else { action = os.Args[1] } if action == "noservice" { run(ctx) os.Exit(0) } if err := register(ctx, "GCEAgent", "GCEAgent", "", run, action); err != nil { logger.Fatalf("error registering service: %s", err) } } guest-agent-20220622.00/google_guest_agent/main_test.go000066400000000000000000000011711425470135300225730ustar00rootroot00000000000000// 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. package main guest-agent-20220622.00/google_guest_agent/metadata.go000066400000000000000000000167771425470135300224120ustar00rootroot00000000000000// 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. package main import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "strings" "time" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) const defaultEtag = "NONE" var ( metadataURL = "http://169.254.169.254/computeMetadata/v1/" metadataRecursive = "/?recursive=true&alt=json" metadataHang = "&wait_for_change=true&timeout_sec=60" defaultTimeout = 70 * time.Second etag = defaultEtag ) type metadata struct { Instance instance Project project } func (m *metadata) UnmarshalJSON(b []byte) error { // We can't unmarshal into metadata directly as it would create an infinite loop. type temp metadata var t temp err := json.Unmarshal(b, &t) if err == nil { *m = metadata(t) return nil } // If this is a syntax error return a useful error. sErr, ok := err.(*json.SyntaxError) if !ok { return err } // Byte number where the error line starts. start := bytes.LastIndex(b[:sErr.Offset], []byte("\n")) + 1 // Assume end byte of error line is EOF unless this isn't the last line. end := len(b) if i := bytes.Index(b[start:], []byte("\n")); i >= 0 { end = start + i } // Position of error in line (where to place the '^'). pos := int(sErr.Offset) - start if pos != 0 { pos = pos - 1 } return fmt.Errorf("JSON syntax error: %s \n%s\n%s^", err, b[start:end], strings.Repeat(" ", pos)) } type virtualClock struct { DriftToken int `json:"drift-token"` } type instance struct { ID json.Number MachineType string Attributes attributes NetworkInterfaces []networkInterfaces VirtualClock virtualClock } type networkInterfaces struct { ForwardedIps []string ForwardedIpv6s []string TargetInstanceIps []string IPAliases []string Mac string DHCPv6Refresh string } type project struct { Attributes attributes ProjectID string NumericProjectID json.Number } type attributes struct { BlockProjectKeys bool EnableOSLogin *bool EnableWindowsSSH *bool TwoFactor *bool SecurityKey *bool SSHKeys []string WindowsKeys windowsKeys Diagnostics string DisableAddressManager *bool DisableAccountManager *bool EnableDiagnostics *bool EnableWSFC *bool WSFCAddresses string WSFCAgentPort string } type windowsKey struct { Email string ExpireOn string Exponent string Modulus string UserName string HashFunction string AddToAdministrators *bool PasswordLength int } type windowsKeys []windowsKey func (k *windowsKeys) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } for _, jskey := range strings.Split(s, "\n") { var wk windowsKey if err := json.Unmarshal([]byte(jskey), &wk); err != nil { if !utils.ContainsString(jskey, badKeys) { logger.Errorf("failed to unmarshal windows key from metadata: %s", err) badKeys = append(badKeys, jskey) } continue } if wk.Exponent != "" && wk.Modulus != "" && wk.UserName != "" && !wk.expired() { *k = append(*k, wk) } } return nil } func (a *attributes) UnmarshalJSON(b []byte) error { var mkbool = func(value bool) *bool { res := new(bool) *res = value return res } // Unmarshal to literal JSON types before doing anything else. type inner struct { BlockProjectKeys string `json:"block-project-ssh-keys"` Diagnostics string `json:"diagnostics"` DisableAccountManager string `json:"disable-account-manager"` DisableAddressManager string `json:"disable-address-manager"` EnableDiagnostics string `json:"enable-diagnostics"` EnableOSLogin string `json:"enable-oslogin"` EnableWindowsSSH string `json:"enable-windows-ssh"` EnableWSFC string `json:"enable-wsfc"` OldSSHKeys string `json:"sshKeys"` SSHKeys string `json:"ssh-keys"` TwoFactor string `json:"enable-oslogin-2fa"` SecurityKey string `json:"enable-oslogin-sk"` WindowsKeys windowsKeys `json:"windows-keys"` WSFCAddresses string `json:"wsfc-addrs"` WSFCAgentPort string `json:"wsfc-agent-port"` } var temp inner if err := json.Unmarshal(b, &temp); err != nil { return err } a.Diagnostics = temp.Diagnostics a.WSFCAddresses = temp.WSFCAddresses a.WSFCAgentPort = temp.WSFCAgentPort a.WindowsKeys = temp.WindowsKeys value, err := strconv.ParseBool(temp.BlockProjectKeys) if err == nil { a.BlockProjectKeys = value } value, err = strconv.ParseBool(temp.EnableDiagnostics) if err == nil { a.EnableDiagnostics = mkbool(value) } value, err = strconv.ParseBool(temp.DisableAccountManager) if err == nil { a.DisableAccountManager = mkbool(value) } value, err = strconv.ParseBool(temp.DisableAddressManager) if err == nil { a.DisableAddressManager = mkbool(value) } value, err = strconv.ParseBool(temp.EnableOSLogin) if err == nil { a.EnableOSLogin = mkbool(value) } value, err = strconv.ParseBool(temp.EnableWindowsSSH) if err == nil { a.EnableWindowsSSH = mkbool(value) } value, err = strconv.ParseBool(temp.EnableWSFC) if err == nil { a.EnableWSFC = mkbool(value) } value, err = strconv.ParseBool(temp.TwoFactor) if err == nil { a.TwoFactor = mkbool(value) } value, err = strconv.ParseBool(temp.SecurityKey) if err == nil { a.SecurityKey = mkbool(value) } // So SSHKeys will be nil instead of []string{} if temp.SSHKeys != "" { a.SSHKeys = strings.Split(temp.SSHKeys, "\n") } if temp.OldSSHKeys != "" { a.BlockProjectKeys = true a.SSHKeys = append(a.SSHKeys, strings.Split(temp.OldSSHKeys, "\n")...) } return nil } func updateEtag(resp *http.Response) bool { oldEtag := etag etag = resp.Header.Get("etag") if etag == "" { etag = defaultEtag } return etag != oldEtag } func watchMetadata(ctx context.Context) (*metadata, error) { return getMetadata(ctx, true) } func getMetadata(ctx context.Context, hang bool) (*metadata, error) { logger.Debugf("getMetadata, %t", hang) client := &http.Client{ Timeout: defaultTimeout, } finalURL := metadataURL + metadataRecursive if hang { finalURL += metadataHang } finalURL += ("&last_etag=" + etag) req, err := http.NewRequest("GET", finalURL, nil) if err != nil { return nil, err } req.Header.Add("Metadata-Flavor", "Google") req = req.WithContext(ctx) resp, err := client.Do(req) // Don't return error on a canceled context. if err != nil && ctx.Err() != nil { return nil, nil } if err != nil { return nil, err } // We return the response even if the etag has not been updated. if hang { updateEtag(resp) } md, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, err } var ret metadata return &ret, json.Unmarshal(md, &ret) } guest-agent-20220622.00/google_guest_agent/metadata_test.go000066400000000000000000000072311425470135300234320ustar00rootroot00000000000000// Copyright 2018 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. package main import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "reflect" "testing" "time" ) func TestWatchMetadata(t *testing.T) { etag1, etag2 := "foo", "bar" var req int et := time.Now().Add(10 * time.Second).Format(time.RFC3339) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if req == 0 { w.Header().Set("etag", etag1) } else { w.Header().Set("etag", etag2) } fmt.Fprintf(w, `{"instance":{"attributes":{"enable-oslogin":"true","ssh-keys":"name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname","windows-keys":"{}\n{\"expireOn\":\"%[1]s\",\"exponent\":\"exponent\",\"modulus\":\"modulus\",\"username\":\"username\"}\n{\"expireOn\":\"%[1]s\",\"exponent\":\"exponent\",\"modulus\":\"modulus\",\"username\":\"username\",\"addToAdministrators\":true}","wsfc-addrs":"foo"}}}`, et) req++ })) defer ts.Close() metadataURL = ts.URL // So that the test wont timeout. defaultTimeout = 1 * time.Second truebool := new(bool) *truebool = true want := attributes{ EnableOSLogin: truebool, WSFCAddresses: "foo", WindowsKeys: windowsKeys{ windowsKey{Exponent: "exponent", UserName: "username", Modulus: "modulus", ExpireOn: et, AddToAdministrators: nil}, windowsKey{Exponent: "exponent", UserName: "username", Modulus: "modulus", ExpireOn: et, AddToAdministrators: func() *bool { ret := true; return &ret }()}, }, SSHKeys: []string{"name:ssh-rsa [KEY] hostname", "name:ssh-rsa [KEY] hostname"}, } for _, e := range []string{etag1, etag2} { got, err := watchMetadata(context.Background()) if err != nil { t.Fatalf("error running watchMetadata: %v", err) } gotA := got.Instance.Attributes if !reflect.DeepEqual(gotA, want) { t.Fatalf("Did not parse expected metadata.\ngot:\n'%+v'\nwant:\n'%+v'", gotA, want) } if etag != e { t.Fatalf("etag not updated as expected (%q != %q)", etag, e) } } } func TestBlockProjectKeys(t *testing.T) { tests := []struct { json string res bool }{ { `{"instance": {"attributes": {"ssh-keys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"},"project": {"attributes": {"ssh-keys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"}}}}`, false, }, { `{"instance": {"attributes": {"sshKeys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"},"project": {"attributes": {"ssh-keys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"}}}}`, true, }, { `{"instance": {"attributes": {"block-project-ssh-keys": "true", "ssh-keys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"},"project": {"attributes": {"ssh-keys": "name:ssh-rsa [KEY] hostname\nname:ssh-rsa [KEY] hostname"}}}}`, true, }, } for _, test := range tests { var md metadata if err := json.Unmarshal([]byte(test.json), &md); err != nil { t.Errorf("failed to unmarshal JSON: %v", err) } if md.Instance.Attributes.BlockProjectKeys != test.res { t.Errorf("instance-level sshKeys didn't set block-project-keys (got %t expected %t)", md.Instance.Attributes.BlockProjectKeys, test.res) } } } guest-agent-20220622.00/google_guest_agent/non_windows_accounts.go000066400000000000000000000322571425470135300250640ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "bufio" "bytes" "fmt" "io/ioutil" "os" "os/exec" "path" "sort" "strconv" "strings" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( // sshKeys is a cache of what we have added to each managed users' authorized // keys file. Avoids necessity of re-reading all files on every change. sshKeys map[string][]string googleUsersFile = "/var/lib/google/google_users" ) // compareStringSlice returns true if two string slices are equal, false // otherwise. Does not modify the slices. func compareStringSlice(first, second []string) bool { if len(first) != len(second) { return false } for _, list := range [][]string{first, second} { sortfunc := func(i, j int) bool { return list[i] < list[j] } list = append([]string{}, list...) sort.Slice(list, sortfunc) } for idx := range first { if first[idx] != second[idx] { return false } } return true } type accountsMgr struct{} func (a *accountsMgr) diff() bool { // If any keys have changed. if !compareStringSlice(newMetadata.Instance.Attributes.SSHKeys, oldMetadata.Instance.Attributes.SSHKeys) { return true } if !compareStringSlice(newMetadata.Project.Attributes.SSHKeys, oldMetadata.Project.Attributes.SSHKeys) { return true } if newMetadata.Instance.Attributes.BlockProjectKeys != oldMetadata.Instance.Attributes.BlockProjectKeys { return true } // If any on-disk keys have expired. for _, keys := range sshKeys { if len(keys) != len(getUserKeys(keys)) { return true } } // If we've just disabled OS Login. oldOslogin, _, _ := getOSLoginEnabled(oldMetadata) newOslogin, _, _ := getOSLoginEnabled(newMetadata) if oldOslogin && !newOslogin { return true } return false } func (a *accountsMgr) timeout() bool { return false } func (a *accountsMgr) disabled(os string) bool { oslogin, _, _ := getOSLoginEnabled(newMetadata) return false || os == "windows" || oslogin || !config.Section("Daemons").Key("accounts_daemon").MustBool(true) } func (a *accountsMgr) set() error { if sshKeys == nil { logger.Debugf("initialize sshKeys map") sshKeys = make(map[string][]string) } logger.Debugf("create sudoers file if needed") if err := createSudoersFile(); err != nil { logger.Errorf("Error creating google-sudoers file: %v.", err) } logger.Debugf("create sudoers group if needed") if err := createSudoersGroup(); err != nil { logger.Errorf("Error creating google-sudoers group: %v.", err) } mdkeys := newMetadata.Instance.Attributes.SSHKeys if !newMetadata.Instance.Attributes.BlockProjectKeys { mdkeys = append(mdkeys, newMetadata.Project.Attributes.SSHKeys...) } mdKeyMap := getUserKeys(mdkeys) logger.Debugf("read google users file") gUsers, err := readGoogleUsersFile() if err != nil { // TODO: is this OK to continue past? logger.Errorf("Couldn't read google_users file: %v.", err) } // Update SSH keys, creating Google users as needed. for user, userKeys := range mdKeyMap { if _, err := getPasswd(user); err != nil { logger.Infof("Creating user %s.", user) if err := createGoogleUser(user); err != nil { logger.Errorf("Error creating user: %s.", err) continue } gUsers[user] = "" } if _, ok := gUsers[user]; !ok { logger.Infof("Adding existing user %s to google-sudoers group.", user) if err := addUserToGroup(user, "google-sudoers"); err != nil { logger.Errorf("%v.", err) } } if !compareStringSlice(userKeys, sshKeys[user]) { logger.Infof("Updating keys for user %s.", user) if err := updateAuthorizedKeysFile(user, userKeys); err != nil { logger.Errorf("Error updating SSH keys for %s: %v.", user, err) continue } sshKeys[user] = userKeys } } // Remove Google users not found in metadata. for user := range gUsers { if _, ok := mdKeyMap[user]; !ok && user != "" { logger.Infof("Removing user %s.", user) err = removeGoogleUser(user) if err != nil { logger.Errorf("Error removing user: %v.", err) } delete(sshKeys, user) } } // Update the google_users file if we've added or removed any users. logger.Debugf("write google_users file") if err := writeGoogleUsersFile(); err != nil { logger.Errorf("Error writing google_users file: %v.", err) } // Start SSHD if not started. We do this in agent instead of adding a // Wants= directive, and here instead of instance setup, so that this // can be disabled by the instance configs file. for _, svc := range []string{"ssh", "sshd"} { // Ignore output, it's just a best effort. systemctlStart(svc) } return nil } var badSSHKeys []string // getUserKeys returns the keys which are not expired and non-expiring key. // valid formats are: // user:ssh-rsa [KEY_VALUE] [USERNAME] // user:ssh-rsa [KEY_VALUE] // user:ssh-rsa [KEY_VALUE] google-ssh {"userName":"[USERNAME]","expireOn":"[EXPIRE_TIME]"} func getUserKeys(mdkeys []string) map[string][]string { mdKeyMap := make(map[string][]string) for i := 0; i < len(mdkeys); i++ { trimmedKey := strings.Trim(mdkeys[i], " ") if trimmedKey != "" { user, keyVal, err := utils.GetUserKey(trimmedKey) if err != nil { if !utils.ContainsString(trimmedKey, badSSHKeys) { logger.Errorf("%s: %s", err.Error(), trimmedKey) badSSHKeys = append(badSSHKeys, trimmedKey) } continue } // key which is not expired or non-expiring key, add it. userKeys := mdKeyMap[user] userKeys = append(userKeys, keyVal) mdKeyMap[user] = userKeys } } return mdKeyMap } // passwdEntry is a user.User with omitted passwd fields restored. type passwdEntry struct { Username string Passwd string UID int GID int Name string HomeDir string Shell string } // getPasswd returns a passwdEntry from the local passwd database. Code adapted from os/user func getPasswd(user string) (*passwdEntry, error) { prefix := []byte(user + ":") colon := []byte{':'} parse := func(line []byte) (*passwdEntry, error) { if !bytes.HasPrefix(line, prefix) || bytes.Count(line, colon) < 6 { return nil, nil } // kevin:x:1005:1006::/home/kevin:/usr/bin/zsh parts := strings.SplitN(string(line), ":", 7) if len(parts) < 7 { return nil, fmt.Errorf("invalid passwd entry for %s", user) } uid, err := strconv.Atoi(parts[2]) if err != nil { return nil, fmt.Errorf("invalid passwd entry for %s", user) } gid, err := strconv.Atoi(parts[3]) if err != nil { return nil, fmt.Errorf("invalid passwd entry for %s", user) } u := &passwdEntry{ Username: parts[0], Passwd: parts[1], UID: uid, GID: gid, Name: parts[4], HomeDir: parts[5], Shell: parts[6], } return u, nil } passwd, err := os.Open("/etc/passwd") if err != nil { return nil, err } bs := bufio.NewScanner(passwd) for bs.Scan() { line := bs.Bytes() // There's no spec for /etc/passwd or /etc/group, but we try to follow // the same rules as the glibc parser, which allows comments and blank // space at the beginning of a line. line = bytes.TrimSpace(line) if len(line) == 0 || line[0] == '#' { continue } v, err := parse(line) if v != nil || err != nil { return v, err } } return nil, fmt.Errorf("User not found") } func writeGoogleUsersFile() error { dir := path.Dir(googleUsersFile) if _, err := os.Stat(dir); err != nil { if err = os.Mkdir(dir, 0755); err != nil { return err } } gfile, err := os.OpenFile(googleUsersFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err == nil { defer gfile.Close() for user := range sshKeys { fmt.Fprintf(gfile, "%s\n", user) } } return err } func readGoogleUsersFile() (map[string]string, error) { res := make(map[string]string) gUsers, err := ioutil.ReadFile(googleUsersFile) if err != nil && !os.IsNotExist(err) { return nil, err } for _, user := range strings.Split(string(gUsers), "\n") { if user != "" { res[user] = "" } } return res, nil } // Replaces {user} or {group} in command string. Supports legacy python-era // user command overrides. func createUserGroupCmd(cmd, user, group string) *exec.Cmd { cmd = strings.Replace(cmd, "{user}", user, 1) cmd = strings.Replace(cmd, "{group}", group, 1) cmds := strings.Fields(cmd) // We don't use runCmd here because we might need the exit codes. return exec.Command(cmds[0], cmds[1:]...) } // createGoogleUser creates a Google managed user account if needed and adds it // to the configured groups. func createGoogleUser(user string) error { var uid string if config.Section("Accounts").Key("reuse_homedir").MustBool(false) { uid = getUID(fmt.Sprintf("/home/%s", user)) } if err := createUser(user, uid); err != nil { return err } groups := config.Section("Accounts").Key("groups").MustString("adm,dip,docker,lxd,plugdev,video") for _, group := range strings.Split(groups, ",") { addUserToGroup(user, group) } return addUserToGroup(user, "google-sudoers") } // removeGoogleUser removes Google managed users. If deprovision_remove is true, the // user and its home directory are removed. Otherwise, SSH keys and sudoer // permissions are removed but the user remains on the system. Group membership // is not changed. func removeGoogleUser(user string) error { if config.Section("Accounts").Key("deprovision_remove").MustBool(false) { userdel := config.Section("Accounts").Key("userdel_cmd").MustString("userdel -r {user}") return runCmd(createUserGroupCmd(userdel, user, "")) } if err := updateAuthorizedKeysFile(user, []string{}); err != nil { return err } gpasswddel := config.Section("Accounts").Key("gpasswd_remove_cmd").MustString("gpasswd -d {user} {group}") return runCmd(createUserGroupCmd(gpasswddel, user, "google-sudoers")) } // createSudoersFile creates the google_sudoers configuration file if it does // not exist and specifies the group 'google-sudoers' should have all // permissions. func createSudoersFile() error { sudoFile, err := os.OpenFile("/etc/sudoers.d/google_sudoers", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0440) if err != nil { if os.IsExist(err) { return nil } return err } defer sudoFile.Close() fmt.Fprintf(sudoFile, "%%google-sudoers ALL=(ALL:ALL) NOPASSWD:ALL\n") return nil } // createSudoersGroup creates the google-sudoers group if it does not exist. func createSudoersGroup() error { groupadd := config.Section("Accounts").Key("groupadd_cmd").MustString("groupadd {group}") ret := runCmdOutput(createUserGroupCmd(groupadd, "", "google-sudoers")) if ret.ExitCode() == 9 { // 9 means group already exists. return nil } if ret.ExitCode() != 0 { return error(ret) } logger.Infof("Created google sudoers file") return nil } // updateAuthorizedKeysFile adds provided keys to the user's SSH // AuthorizedKeys file. The file and containing directory are created if it // does not exist. Uses a temporary file to avoid partial updates in case of // errors. If no keys are provided, the authorized keys file is removed. func updateAuthorizedKeysFile(user string, keys []string) error { gcomment := "# Added by Google" passwd, err := getPasswd(user) if err != nil { return err } if passwd.HomeDir == "" { return fmt.Errorf("user %s has no homedir set", user) } if passwd.Shell == "/sbin/nologin" { return nil } sshpath := path.Join(passwd.HomeDir, ".ssh") if _, err := os.Stat(sshpath); err != nil { if os.IsNotExist(err) { if err = os.Mkdir(sshpath, 0700); err != nil { return err } if err = os.Chown(sshpath, passwd.UID, passwd.GID); err != nil { return err } } else { return err } } akpath := path.Join(sshpath, "authorized_keys") // Remove empty file. if len(keys) == 0 { os.Remove(akpath) return nil } tempPath := akpath + ".google" akcontents, err := ioutil.ReadFile(akpath) if err != nil && !os.IsNotExist(err) { return err } var isgoogle bool var userKeys []string for _, key := range strings.Split(string(akcontents), "\n") { if key == "" { continue } if isgoogle { isgoogle = false continue } if key == gcomment { isgoogle = true continue } userKeys = append(userKeys, key) } newfile, err := os.OpenFile(tempPath, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } defer newfile.Close() for _, key := range userKeys { fmt.Fprintf(newfile, "%s\n", key) } for _, key := range keys { fmt.Fprintf(newfile, "%s\n%s\n", gcomment, key) } err = os.Chown(tempPath, passwd.UID, passwd.GID) if err != nil { // Existence of temp file will block further updates for this user. // Don't catch remove error, nothing we can do. Return the // chown error which caused the issue. os.Remove(tempPath) return fmt.Errorf("error setting ownership of new keys file: %v", err) } runCmd(exec.Command("restorecon", tempPath)) return os.Rename(tempPath, akpath) } guest-agent-20220622.00/google_guest_agent/non_windows_accounts_integ_test.go000066400000000000000000000057521425470135300273110ustar00rootroot00000000000000// Copyright 2021 Google LLC // // 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. // +build integration package main import ( "fmt" "os" "os/exec" "strings" "testing" ) const ( testUser = "integration-test-user" defaultgroupstring = "adm,dip,docker,lxd,plugdev,video,google-sudoers" ) func TestCreateAndRemoveGoogleUser(t *testing.T) { if exist, err := userExists(testUser); err != nil && exist { t.Fatalf("test user should not exist") } if err := createGoogleUser(testUser); err != nil { t.Errorf("createGoogleUser failed creating test user") } if exist, err := userExists(testUser); exist != true || err != nil { t.Errorf("test user should exist") } cmd := exec.Command("groups", testUser) ret := runCmdOutput(cmd) if ret.ExitCode() != 0 { t.Errorf("failed looking up groups for user: stdout:%s stderr:%s", ret.Stdout(), ret.Stderr()) } groups := strings.Split(strings.TrimSpace(strings.Split(ret.Stdout(), ":")[1]), " ") expectedGroupString := config.Section("Accounts").Key("groups").MustString(defaultgroupstring) expectedGroups := strings.Split(expectedGroupString, ",") for _, group := range groups { if !contains(group, expectedGroups) { t.Errorf("test user has been added to an unexpected group %s", group) } } if _, err := os.Stat(fmt.Sprintf("/home/%s", testUser)); err != nil { t.Errorf("test user home directory does not exist") } if err := createGoogleUser(testUser); err == nil { t.Errorf("createGoogleUser did not return error when creating user that already exists") } if err := removeGoogleUser(testUser); err != nil { t.Errorf("removeGoogleUser did not remove user") } if exist, err := userExists(testUser); err != nil && exist == true { t.Errorf("test user should not exist") } if err := removeGoogleUser(testUser); err == nil { t.Errorf("removeGoogleUser did not return error when removing user that doesn't exist") } } func TestGroupaddDuplicates(t *testing.T) { cmd := exec.Command("groupadd", "integ-test-group") ret := runCmdOutput(cmd) if ret.ExitCode() != 0 { t.Fatalf("got wrong exit code running \"groupadd integ-test-group\", expected 0 got %v\n", ret.ExitCode()) } cmd = exec.Command("groupadd", "integ-test-group") ret = runCmdOutput(cmd) if ret.ExitCode() != 9 { t.Fatalf("got wrong exit code running \"groupadd integ-test-group\", expected 9 got %v\n", ret.ExitCode()) } } func contains(target string, expected []string) bool { for _, e := range expected { if e == target { return true } } return false } guest-agent-20220622.00/google_guest_agent/oslogin.go000066400000000000000000000304401425470135300222630ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "fmt" "io/ioutil" "os" "os/exec" "runtime" "strings" "time" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( googleComment = "# Added by Google Compute Engine OS Login." googleBlockStart = "#### Google OS Login control. Do not edit this section. ####" googleBlockEnd = "#### End Google OS Login control section. ####" ) type osloginMgr struct{} // We also read project keys first, letting instance-level keys take // precedence. func getOSLoginEnabled(md *metadata) (bool, bool, bool) { var enable bool if md.Project.Attributes.EnableOSLogin != nil { enable = *md.Project.Attributes.EnableOSLogin } if md.Instance.Attributes.EnableOSLogin != nil { enable = *md.Instance.Attributes.EnableOSLogin } var twofactor bool if md.Project.Attributes.TwoFactor != nil { twofactor = *md.Project.Attributes.TwoFactor } if md.Instance.Attributes.TwoFactor != nil { twofactor = *md.Instance.Attributes.TwoFactor } var skey bool if md.Project.Attributes.SecurityKey != nil { skey = *md.Project.Attributes.SecurityKey } if md.Instance.Attributes.SecurityKey != nil { skey = *md.Instance.Attributes.SecurityKey } return enable, twofactor, skey } func (o *osloginMgr) diff() bool { oldEnable, oldTwoFactor, oldSkey := getOSLoginEnabled(oldMetadata) enable, twofactor, skey := getOSLoginEnabled(newMetadata) return oldMetadata.Project.ProjectID == "" || // True on first run or if any value has changed. (oldTwoFactor != twofactor) || (oldEnable != enable) || (oldSkey != skey) } func (o *osloginMgr) timeout() bool { return false } func (o *osloginMgr) disabled(os string) bool { return os == "windows" } func (o *osloginMgr) set() error { // We need to know if it was previously enabled for the clearing of // metadata-based SSH keys. oldEnable, _, _ := getOSLoginEnabled(oldMetadata) enable, twofactor, skey := getOSLoginEnabled(newMetadata) if enable && !oldEnable { logger.Infof("Enabling OS Login") newMetadata.Instance.Attributes.SSHKeys = nil newMetadata.Project.Attributes.SSHKeys = nil (&accountsMgr{}).set() } if !enable && oldEnable { logger.Infof("Disabling OS Login") } if err := writeSSHConfig(enable, twofactor, skey); err != nil { logger.Errorf("Error updating SSH config: %v.", err) } if err := writeNSSwitchConfig(enable); err != nil { logger.Errorf("Error updating NSS config: %v.", err) } if err := writePAMConfig(enable, twofactor); err != nil { logger.Errorf("Error updating PAM config: %v.", err) } if err := writeGroupConf(enable); err != nil { logger.Errorf("Error updating group.conf: %v.", err) } for _, svc := range []string{"nscd", "unscd", "systemd-logind", "cron", "crond"} { // These services should be restarted if running logger.Debugf("systemctl try-restart %s, if it exists", svc) if err := systemctlTryRestart(svc); err != nil { logger.Errorf("Error restarting service: %v.", err) } } // SSH should be started if not running, reloaded otherwise. for _, svc := range []string{"ssh", "sshd"} { logger.Debugf("systemctl reload-or-restart %s, if it exists", svc) if err := systemctlReloadOrRestart(svc); err != nil { logger.Errorf("Error reloading service: %v.", err) } } now := fmt.Sprintf("%d", time.Now().Unix()) writeGuestAttributes("guest-agent/sshable", now) if enable { logger.Debugf("Create OS Login dirs, if needed") if err := createOSLoginDirs(); err != nil { logger.Errorf("Error creating OS Login directory: %v.", err) } logger.Debugf("create OS Login sudoers config, if needed") if err := createOSLoginSudoersFile(); err != nil { logger.Errorf("Error creating OS Login sudoers file: %v.", err) } logger.Debugf("starting OS Login nss cache fill") if err := runCmd(exec.Command("google_oslogin_nss_cache")); err != nil { logger.Errorf("Error updating NSS cache: %v.", err) } } return nil } func filterGoogleLines(contents string) []string { var isgoogle, isgoogleblock bool var filtered []string for _, line := range strings.Split(contents, "\n") { switch { case strings.Contains(line, googleComment) && !isgoogleblock: isgoogle = true case strings.Contains(line, googleBlockEnd): isgoogleblock = false isgoogle = false case isgoogleblock, strings.Contains(line, googleBlockStart): isgoogleblock = true case isgoogle: isgoogle = false default: filtered = append(filtered, line) } } return filtered } func writeConfigFile(path, contents string) error { logger.Debugf("writing %s", path) file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) if err != nil { return err } defer closeFile(file) file.WriteString(contents) return nil } func updateSSHConfig(sshConfig string, enable, twofactor, skey bool) string { // TODO: this feels like a case for a text/template challengeResponseEnable := "ChallengeResponseAuthentication yes" authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys" if skey { authorizedKeysCommand = "AuthorizedKeysCommand /usr/bin/google_authorized_keys_sk" } if runtime.GOOS == "freebsd" { authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys" if skey { authorizedKeysCommand = "AuthorizedKeysCommand /usr/local/bin/google_authorized_keys_sk" } } authorizedKeysUser := "AuthorizedKeysCommandUser root" twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive" if (osRelease.os == "rhel" || osRelease.os == "centos") && osRelease.version.major == 6 { authorizedKeysUser = "AuthorizedKeysCommandRunAs root" twoFactorAuthMethods = "RequiredAuthentications2 publickey,keyboard-interactive" } matchblock1 := `Match User sa_*` matchblock2 := ` AuthenticationMethods publickey` filtered := filterGoogleLines(string(sshConfig)) if enable { osLoginBlock := []string{googleBlockStart, authorizedKeysCommand, authorizedKeysUser} if twofactor { osLoginBlock = append(osLoginBlock, twoFactorAuthMethods, challengeResponseEnable) } osLoginBlock = append(osLoginBlock, googleBlockEnd) filtered = append(osLoginBlock, filtered...) if twofactor { filtered = append(filtered, googleBlockStart, matchblock1, matchblock2, googleBlockEnd) } } return strings.Join(filtered, "\n") } func writeSSHConfig(enable, twofactor, skey bool) error { sshConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config") if err != nil { return err } proposed := updateSSHConfig(string(sshConfig), enable, twofactor, skey) if proposed == string(sshConfig) { return nil } return writeConfigFile("/etc/ssh/sshd_config", proposed) } func updateNSSwitchConfig(nsswitch string, enable bool) string { oslogin := " cache_oslogin oslogin" var filtered []string for _, line := range strings.Split(string(nsswitch), "\n") { if strings.HasPrefix(line, "passwd:") || strings.HasPrefix(line, "group:") { present := strings.Contains(line, "oslogin") if enable && !present { line += oslogin } else if !enable && present { line = strings.TrimSuffix(line, oslogin) } if runtime.GOOS == "freebsd" { line = strings.Replace(line, "compat", "files", 1) } } filtered = append(filtered, line) } return strings.Join(filtered, "\n") } func writeNSSwitchConfig(enable bool) error { nsswitch, err := ioutil.ReadFile("/etc/nsswitch.conf") if err != nil { return err } proposed := updateNSSwitchConfig(string(nsswitch), enable) if proposed == string(nsswitch) { return nil } return writeConfigFile("/etc/nsswitch.conf", proposed) } // Adds entries to the PAM config for sshd and su which reflect the current // enablements. Only writes files if they have changed from what's on disk. func updatePAMsshd(pamsshd string, enable, twofactor bool) string { authOSLogin := "auth [success=done perm_denied=die default=ignore] pam_oslogin_login.so" authGroup := "auth [default=ignore] pam_group.so" accountOSLogin := "account [success=ok ignore=ignore default=die] pam_oslogin_login.so" accountOSLoginAdmin := "account [success=ok default=ignore] pam_oslogin_admin.so" sessionHomeDir := "session [success=ok default=ignore] pam_mkhomedir.so" if runtime.GOOS == "freebsd" { authOSLogin = "auth optional pam_oslogin_login.so" authGroup = "auth optional pam_group.so" accountOSLogin = "account requisite pam_oslogin_login.so" accountOSLoginAdmin = "account optional pam_oslogin_admin.so" sessionHomeDir = "session optional pam_mkhomedir.so" } filtered := filterGoogleLines(string(pamsshd)) if enable { topOfFile := []string{googleBlockStart} if twofactor { topOfFile = append(topOfFile, authOSLogin) } topOfFile = append(topOfFile, authGroup, googleBlockEnd) bottomOfFile := []string{googleBlockStart, accountOSLogin, accountOSLoginAdmin, sessionHomeDir, googleBlockEnd} filtered = append(topOfFile, filtered...) filtered = append(filtered, bottomOfFile...) } return strings.Join(filtered, "\n") } func writePAMConfig(enable, twofactor bool) error { pamsshd, err := ioutil.ReadFile("/etc/pam.d/sshd") if err != nil { return err } proposed := updatePAMsshd(string(pamsshd), enable, twofactor) if proposed != string(pamsshd) { if err := writeConfigFile("/etc/pam.d/sshd", proposed); err != nil { return err } } return nil } func updateGroupConf(groupconf string, enable bool) string { config := "sshd;*;*;Al0000-2400;video\n" filtered := filterGoogleLines(groupconf) if enable { filtered = append(filtered, []string{googleComment, config}...) } return strings.Join(filtered, "\n") } func writeGroupConf(enable bool) error { groupconf, err := ioutil.ReadFile("/etc/security/group.conf") if err != nil { return err } proposed := updateGroupConf(string(groupconf), enable) if proposed != string(groupconf) { if err := writeConfigFile("/etc/security/group.conf", proposed); err != nil { return err } } return nil } // Creates necessary OS Login directories if they don't exist. func createOSLoginDirs() error { restorecon, restoreconerr := exec.LookPath("restorecon") for _, dir := range []string{"/var/google-sudoers.d", "/var/google-users.d"} { err := os.Mkdir(dir, 0750) if err != nil && !os.IsExist(err) { return err } if restoreconerr == nil { runCmd(exec.Command(restorecon, dir)) } } return nil } func createOSLoginSudoersFile() error { osloginSudoers := "/etc/sudoers.d/google-oslogin" if runtime.GOOS == "freebsd" { osloginSudoers = "/usr/local" + osloginSudoers } sudoFile, err := os.OpenFile(osloginSudoers, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0440) if err != nil { if os.IsExist(err) { return nil } return err } fmt.Fprintf(sudoFile, "#includedir /var/google-sudoers.d\n") return sudoFile.Close() } // systemctlTryRestart tries to restart a systemd service if it is already // running. Stopped services will be ignored. func systemctlTryRestart(servicename string) error { if !systemctlUnitExists(servicename) { return nil } return runCmd(exec.Command("systemctl", "try-restart", servicename+".service")) } // systemctlReloadOrRestart tries to reload a running systemd service if // supported, restart otherwise. Stopped services will be started. func systemctlReloadOrRestart(servicename string) error { if !systemctlUnitExists(servicename) { return nil } return runCmd(exec.Command("systemctl", "reload-or-restart", servicename+".service")) } // systemctlStart tries to start a stopped systemd service. Started services // will be ignored. func systemctlStart(servicename string) error { if !systemctlUnitExists(servicename) { return nil } return runCmd(exec.Command("systemctl", "start", servicename+".service")) } func systemctlUnitExists(servicename string) bool { res := runCmdOutput(exec.Command("systemctl", "list-units", "--all", servicename+".service")) return !strings.Contains(res.Stdout(), "0 loaded units listed") } guest-agent-20220622.00/google_guest_agent/oslogin_test.go000066400000000000000000000256351425470135300233340ustar00rootroot00000000000000// Copyright 2019 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. package main import ( "encoding/json" "strings" "testing" ) func TestFilterGoogleLines(t *testing.T) { cmpslice := func(a, b []string) bool { if len(a) != len(b) { return false } for idx := 0; idx < len(a); idx++ { if a[idx] != b[idx] { return false } } return true } var tests = []struct { contents, want []string }{ { []string{ "line1", "line2", googleComment, "line3 after google comment", "line4", googleBlockStart, "line5 inside google block", "line6 inside google block", googleBlockEnd, "line7", }, []string{ "line1", "line2", "line4", "line7", }, }, { []string{ "line1", "line2", googleBlockEnd, "line3", "line4", }, []string{ "line1", "line2", "line3", "line4", }, }, { []string{ googleBlockStart, "line1 inside google block", "line2 inside google block", googleBlockEnd, "line3", }, []string{ "line3", }, }, { []string{ googleBlockStart, "line1 inside google block", googleBlockStart, "line2 inside google block", googleBlockEnd, "line3", googleBlockEnd, "line4", }, []string{ "line3", "line4", }, }, { []string{ googleBlockEnd, googleBlockStart, "line1 inside google block", "line2 inside google block", googleComment, googleBlockEnd, "line3", }, []string{ "line3", }, }, } for idx, tt := range tests { if res := filterGoogleLines(strings.Join(tt.contents, "\n")); !cmpslice(res, tt.want) { t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, tt.want, res) } } } func TestUpdateNSSwitchConfig(t *testing.T) { oslogin := " cache_oslogin oslogin" var tests = []struct { contents, want []string enable bool }{ { contents: []string{ "line1", "passwd: line2", "group: line3", }, want: []string{ "line1", "passwd: line2" + oslogin, "group: line3" + oslogin, }, enable: true, }, { contents: []string{ "line1", "passwd: line2" + oslogin, "group: line3" + oslogin, }, want: []string{ "line1", "passwd: line2", "group: line3", }, enable: false, }, { contents: []string{ "line1", "passwd: line2" + oslogin, "group: line3" + oslogin, }, want: []string{ "line1", "passwd: line2" + oslogin, "group: line3" + oslogin, }, enable: true, }, } for idx, tt := range tests { contents := strings.Join(tt.contents, "\n") want := strings.Join(tt.want, "\n") if res := updateNSSwitchConfig(contents, tt.enable); res != want { t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, want, res) } } } func TestUpdateSSHConfig(t *testing.T) { challengeResponseEnable := "ChallengeResponseAuthentication yes" authorizedKeysCommand := "AuthorizedKeysCommand /usr/bin/google_authorized_keys" authorizedKeysCommandSk := "AuthorizedKeysCommand /usr/bin/google_authorized_keys_sk" authorizedKeysUser := "AuthorizedKeysCommandUser root" twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive" matchblock1 := `Match User sa_*` matchblock2 := ` AuthenticationMethods publickey` var tests = []struct { contents, want []string enable, twofactor, skey bool }{ { // Full block is created, any others removed. contents: []string{ "line1", googleBlockStart, "line2", googleBlockEnd, }, want: []string{ googleBlockStart, authorizedKeysCommand, authorizedKeysUser, twoFactorAuthMethods, challengeResponseEnable, googleBlockEnd, "line1", googleBlockStart, matchblock1, matchblock2, googleBlockEnd, }, enable: true, twofactor: true, skey: false, }, { // Full block is created, google comments removed. contents: []string{ "line1", googleComment, "line2", "line3", }, want: []string{ googleBlockStart, authorizedKeysCommand, authorizedKeysUser, twoFactorAuthMethods, challengeResponseEnable, googleBlockEnd, "line1", "line3", googleBlockStart, matchblock1, matchblock2, googleBlockEnd, }, enable: true, twofactor: true, skey: false, }, { // Block is created without two-factor options. contents: []string{ "line1", "line2", }, want: []string{ googleBlockStart, authorizedKeysCommand, authorizedKeysUser, googleBlockEnd, "line1", "line2", }, enable: true, twofactor: false, skey: false, }, { // Existing block is removed. contents: []string{ "line1", "line2", googleBlockStart, "line3", googleBlockEnd, }, want: []string{ "line1", "line2", }, enable: false, twofactor: true, skey: false, }, { // Skey binary is chosen instead. contents: []string{ "line1", "line2", googleBlockStart, "line3", googleBlockEnd, }, want: []string{ googleBlockStart, authorizedKeysCommandSk, authorizedKeysUser, googleBlockEnd, "line1", "line2", }, enable: true, twofactor: false, skey: true, }, } for idx, tt := range tests { contents := strings.Join(tt.contents, "\n") want := strings.Join(tt.want, "\n") if res := updateSSHConfig(contents, tt.enable, tt.twofactor, tt.skey); res != want { t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, want, res) } } } func TestUpdatePAMsshd(t *testing.T) { authOSLogin := "auth [success=done perm_denied=die default=ignore] pam_oslogin_login.so" authGroup := "auth [default=ignore] pam_group.so" accountOSLogin := "account [success=ok ignore=ignore default=die] pam_oslogin_login.so" accountOSLoginAdmin := "account [success=ok default=ignore] pam_oslogin_admin.so" sessionHomeDir := "session [success=ok default=ignore] pam_mkhomedir.so" var tests = []struct { contents, want []string enable, twofactor bool }{ { contents: []string{ "line1", "line2", }, want: []string{ googleBlockStart, authOSLogin, authGroup, googleBlockEnd, "line1", "line2", googleBlockStart, accountOSLogin, accountOSLoginAdmin, sessionHomeDir, googleBlockEnd, }, enable: true, twofactor: true, }, { contents: []string{ "line1", "line2", }, want: []string{ googleBlockStart, authGroup, googleBlockEnd, "line1", "line2", googleBlockStart, accountOSLogin, accountOSLoginAdmin, sessionHomeDir, googleBlockEnd, }, enable: true, twofactor: false, }, { contents: []string{ googleBlockStart, "line1", googleBlockEnd, "line2", googleBlockStart, "line3", googleBlockEnd, }, want: []string{ "line2", }, enable: false, twofactor: true, }, } for idx, tt := range tests { contents := strings.Join(tt.contents, "\n") want := strings.Join(tt.want, "\n") if res := updatePAMsshd(contents, tt.enable, tt.twofactor); res != want { t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, want, res) } } } func TestUpdateGroupConf(t *testing.T) { config := "sshd;*;*;Al0000-2400;video" var tests = []struct { contents, want []string enable bool }{ { contents: []string{ "line1", "line2", }, want: []string{ "line1", "line2", googleComment, config, "", }, enable: true, }, { contents: []string{ "line1", "line2", }, want: []string{ "line1", "line2", }, enable: false, }, { contents: []string{ "line1", "line2", googleComment, "line3", // not the right line }, want: []string{ "line1", "line2", googleComment, config, "", }, enable: true, }, { contents: []string{ "line1", "line2", googleComment, "line3", }, want: []string{ "line1", "line2", }, enable: false, }, } for idx, tt := range tests { contents := strings.Join(tt.contents, "\n") want := strings.Join(tt.want, "\n") if res := updateGroupConf(contents, tt.enable); res != want { t.Errorf("test %v\nwant:\n%v\ngot:\n%v\n", idx, want, res) } } } func TestGetOSLoginEnabled(t *testing.T) { var tests = []struct { md string enable, twofactor, skey bool }{ { md: `{"instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`, enable: true, twofactor: true, skey: false, }, { md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`, enable: true, twofactor: true, skey: false, }, { // Instance keys take precedence md: `{"project": {"attributes": {"enable-oslogin": "false", "enable-oslogin-2fa": "false"}}, "instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}}`, enable: true, twofactor: true, skey: false, }, { // Instance keys take precedence md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}, "instance": {"attributes": {"enable-oslogin": "false", "enable-oslogin-2fa": "false"}}}`, enable: false, twofactor: false, skey: false, }, { // Handle weird values md: `{"instance": {"attributes": {"enable-oslogin": "TRUE", "enable-oslogin-2fa": "foobar"}}}`, enable: true, twofactor: false, skey: false, }, { // Mixed test md: `{"project": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true"}}, "instance": {"attributes": {"enable-oslogin-2fa": "false"}}}`, enable: true, twofactor: false, skey: false, }, { // Skey test md: `{"instance": {"attributes": {"enable-oslogin": "true", "enable-oslogin-2fa": "true", "enable-oslogin-sk": "true"}}}`, enable: true, twofactor: true, skey: true, }, } for idx, tt := range tests { var md metadata if err := json.Unmarshal([]byte(tt.md), &md); err != nil { t.Errorf("Failed to unmarshal metadata JSON for test %v: %v", idx, err) } enable, twofactor, skey := getOSLoginEnabled(&md) if enable != tt.enable || twofactor != tt.twofactor || skey != tt.skey { t.Errorf("Test %v failed. Expected: %v/%v/%v Got: %v/%v/%v", idx, tt.enable, tt.twofactor, tt.skey, enable, twofactor, skey) } } } guest-agent-20220622.00/google_guest_agent/service.go000066400000000000000000000051261425470135300222540ustar00rootroot00000000000000// 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. package main import ( "context" "fmt" "os" "path/filepath" "time" "github.com/kardianos/service" ) type program struct { run func(context.Context) ctx context.Context cancel context.CancelFunc done chan struct{} timeout time.Duration } func (p *program) Start(s service.Service) error { go func() { p.run(p.ctx) close(p.done) }() return nil } func (p *program) Stop(s service.Service) error { p.cancel() select { case <-p.done: return nil case <-time.After(p.timeout): return fmt.Errorf("failed to shutdown within timeout %s", p.timeout) } } func usage(name string) { fmt.Printf( "Usage:\n"+ " %[1]s install: install the %[2]s service\n"+ " %[1]s remove: remove the %[2]s service\n"+ " %[1]s start: start the %[2]s service\n"+ " %[1]s stop: stop the %[2]s service\n", filepath.Base(os.Args[0]), name) } func register(ctx context.Context, name, displayName, desc string, run func(context.Context), action string) error { svcConfig := &service.Config{ Name: name, DisplayName: displayName, Description: desc, } ctx, cancel := context.WithCancel(ctx) done := make(chan struct{}) prg := &program{ run: run, ctx: ctx, cancel: cancel, done: done, timeout: 15 * time.Second, } svc, err := service.New(prg, svcConfig) if err != nil { return err } switch action { case "run": return svc.Run() case "install": if err := svc.Install(); err != nil { return fmt.Errorf("failed to install service %s: %s", name, err) } case "remove": if err := svc.Uninstall(); err != nil { return fmt.Errorf("failed to remove service %s: %s", name, err) } case "start": if err := svc.Start(); err != nil { return fmt.Errorf("failed to start service %s: %s", name, err) } case "stop": if err := svc.Stop(); err != nil { return fmt.Errorf("failed to stop service %s: %s", name, err) } case "help": usage(name) default: fmt.Printf("%q is not a valid argument.\n", action) usage(name) } return nil } guest-agent-20220622.00/google_guest_agent/snapshot_listener.go000066400000000000000000000134461425470135300243640ustar00rootroot00000000000000// Copyright 2020 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. package main import ( "context" "fmt" "os" "time" sspb "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/snapshot_service" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "github.com/golang/groupcache/lru" "google.golang.org/grpc" ) var ( scriptsDir = "/etc/google/snapshots/" seenPreSnapshotOperationIds = lru.New(128) seenPostSnapshotOperationIds = lru.New(128) ) type snapshotConfig struct { timeout time.Duration // seconds } type invalidSnapshotConfig struct { msg string } func (e *invalidSnapshotConfig) Error() string { return fmt.Sprintf("invalid config: %s", e.msg) } func getSnapshotConfig() (snapshotConfig, error) { var conf snapshotConfig conf.timeout = time.Duration(config.Section("Snapshots").Key("timeout_in_seconds").MustInt(60)) * time.Second return conf, nil } func runScript(path, disks string, config snapshotConfig) (int, sspb.AgentErrorCode) { logger.Infof("Running guest consistent snapshot script at: %s.", path) if _, err := os.Stat(path); os.IsNotExist(err) { return -1, sspb.AgentErrorCode_SCRIPT_NOT_FOUND } execResult := runCmdOutputWithTimeout(config.timeout, path, disks) if execResult.code == 124 { return execResult.code, sspb.AgentErrorCode_SCRIPT_TIMED_OUT } if execResult.code != 0 { return execResult.code, sspb.AgentErrorCode_UNHANDLED_SCRIPT_ERROR } return execResult.code, sspb.AgentErrorCode_NO_ERROR } func listenForSnapshotRequests(address string, requestChan chan<- *sspb.GuestMessage) { for { // Start hanging connection on server that feeds to channel logger.Infof("Attempting to connect to snapshot service at %s.", address) conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { logger.Errorf("Failed to connect to snapshot service: %v.", err) return } c := sspb.NewSnapshotServiceClient(conn) ctx, cancel := context.WithCancel(context.Background()) guestReady := sspb.GuestReady{ RequestServerInfo: false, } r, err := c.CreateConnection(ctx, &guestReady) if err != nil { logger.Errorf("Error creating connection: %v.", err) cancel() continue } for { request, err := r.Recv() if err != nil { logger.Errorf("Error reading snapshot request: %v.", err) cancel() break } logger.Infof("Received snapshot request.") requestChan <- request } } } func getSnapshotResponse(guestMessage *sspb.GuestMessage) *sspb.SnapshotResponse { switch { case guestMessage.GetSnapshotRequest() != nil: request := guestMessage.GetSnapshotRequest() response := &sspb.SnapshotResponse{ OperationId: request.GetOperationId(), Type: request.GetType(), } config, err := getSnapshotConfig() if err != nil { response.AgentReturnCode = sspb.AgentErrorCode_INVALID_CONFIG return response } var url string switch request.GetType() { case sspb.OperationType_PRE_SNAPSHOT: logger.Infof("Handling pre snapshot request for operation id %d.", request.GetOperationId()) _, found := seenPreSnapshotOperationIds.Get(request.GetOperationId()) if found { logger.Infof("Duplicate pre snapshot request operation id %d.", request.GetOperationId()) return nil } seenPreSnapshotOperationIds.Add(request.GetOperationId(), request.GetOperationId()) url = scriptsDir + "pre.sh" case sspb.OperationType_POST_SNAPSHOT: logger.Infof("Handling post snapshot request for operation id %d.", request.GetOperationId()) _, found := seenPostSnapshotOperationIds.Get(request.GetOperationId()) if found { logger.Infof("Duplicate post snapshot request operation id %d.", request.GetOperationId()) return nil } seenPostSnapshotOperationIds.Add(request.GetOperationId(), request.GetOperationId()) url = scriptsDir + "post.sh" default: logger.Errorf("Unhandled operation type %d.", request.GetType()) return nil } scriptsReturnCode, agentErrorCode := runScript(url, request.GetDiskList(), config) response.ScriptsReturnCode = int32(scriptsReturnCode) response.AgentReturnCode = agentErrorCode return response default: } return nil } func handleSnapshotRequests(address string, requestChan <-chan *sspb.GuestMessage) { for { conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { logger.Errorf("Failed to connect to snapshot service: %v.", err) return } for { // Listen on channel and respond guestMessage := <-requestChan response := getSnapshotResponse(guestMessage) for { c := sspb.NewSnapshotServiceClient(conn) ctx, cancel := context.WithCancel(context.Background()) _, err = c.HandleResponsesFromGuest(ctx, response) if err == nil { cancel() break } logger.Errorf("Error sending response: %v.", err) } } } } func startSnapshotListener(snapshotServiceIP string, snapshotServicePort int) { requestChan := make(chan *sspb.GuestMessage) address := fmt.Sprintf("%s:%d", snapshotServiceIP, snapshotServicePort) // Create scripts directory if it doesn't exist. _, err := os.Stat(scriptsDir) if os.IsNotExist(err) { // Make the directory only readable/writable/executable by root. os.MkdirAll(scriptsDir, 0700) } go listenForSnapshotRequests(address, requestChan) go handleSnapshotRequests(address, requestChan) } guest-agent-20220622.00/google_guest_agent/snapshot_service/000077500000000000000000000000001425470135300236405ustar00rootroot00000000000000guest-agent-20220622.00/google_guest_agent/snapshot_service/snapshot_service.proto000066400000000000000000000050271425470135300303100ustar00rootroot00000000000000// Copyright 2019 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. syntax = "proto3"; package cloud.vmm; enum OperationType { NOT_SET = 0; PRE_SNAPSHOT = 1; POST_SNAPSHOT = 2; } enum SupportedFeatures { NONE = 0; SNAPSHOTS = 1; } message SnapshotRequest { // The operation id of the snapshot. int32 operation_id = 1; // A list of comma separated target/lun values, e.g "1/2,3/4". string disk_list = 2; // The operation type. OperationType type = 3; } message ServerInfo { repeated SupportedFeatures supported_features = 1; } enum AgentErrorCode { NO_ERROR = 0; // The snapshot config was improper in some way. INVALID_CONFIG = 1; // The pre or post snapshot script was not found on disk. SCRIPT_NOT_FOUND = 2; // The pre or post snapshot script timed out. SCRIPT_TIMED_OUT = 3; // The pre or post snapshot script returned an error, but the "continue on // error" flag was not set. UNHANDLED_SCRIPT_ERROR = 4; } message SnapshotResponse { // The operation id of the snapshot. int32 operation_id = 1; // The return code of the scripts run by the guest. If this is non-zero, then // agent_return_code should be UNHANDLED_SCRIPT_ERROR. int32 scripts_return_code = 2; // The agent return code. AgentErrorCode agent_return_code = 3; // The operation type. OperationType type = 4; } message GuestReady { bool request_server_info = 1; } message GuestMessage { oneof msg { SnapshotRequest snapshot_request = 1; ServerInfo server_info = 2; } } message ServerAck {} // Service to handle pre and post snapshot requests from vanadium. service SnapshotService { // The client is expected to first send a "GuestReady" message, indicating // they are available and establishing the connection that vanadium writes // requests to the agent on. rpc CreateConnection(GuestReady) returns (stream GuestMessage) {} // Handles the agent's responses to the above requests. rpc HandleResponsesFromGuest(SnapshotResponse) returns (ServerAck) {} } guest-agent-20220622.00/google_guest_agent/snapshot_service/snapshot_service.proto.pb.go000066400000000000000000000556301425470135300313210ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // source: snapshot_service.proto package cloud_vmm import ( context "context" fmt "fmt" proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" math "math" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type OperationType int32 const ( OperationType_NOT_SET OperationType = 0 OperationType_PRE_SNAPSHOT OperationType = 1 OperationType_POST_SNAPSHOT OperationType = 2 ) var OperationType_name = map[int32]string{ 0: "NOT_SET", 1: "PRE_SNAPSHOT", 2: "POST_SNAPSHOT", } var OperationType_value = map[string]int32{ "NOT_SET": 0, "PRE_SNAPSHOT": 1, "POST_SNAPSHOT": 2, } func (x OperationType) String() string { return proto.EnumName(OperationType_name, int32(x)) } func (OperationType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{0} } type SupportedFeatures int32 const ( SupportedFeatures_NONE SupportedFeatures = 0 SupportedFeatures_SNAPSHOTS SupportedFeatures = 1 ) var SupportedFeatures_name = map[int32]string{ 0: "NONE", 1: "SNAPSHOTS", } var SupportedFeatures_value = map[string]int32{ "NONE": 0, "SNAPSHOTS": 1, } func (x SupportedFeatures) String() string { return proto.EnumName(SupportedFeatures_name, int32(x)) } func (SupportedFeatures) EnumDescriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{1} } type AgentErrorCode int32 const ( AgentErrorCode_NO_ERROR AgentErrorCode = 0 // The snapshot config was improper in some way. AgentErrorCode_INVALID_CONFIG AgentErrorCode = 1 // The pre or post snapshot script was not found on disk. AgentErrorCode_SCRIPT_NOT_FOUND AgentErrorCode = 2 // The pre or post snapshot script timed out. AgentErrorCode_SCRIPT_TIMED_OUT AgentErrorCode = 3 // The pre or post snapshot script returned an error, but the "continue on // error" flag was not set. AgentErrorCode_UNHANDLED_SCRIPT_ERROR AgentErrorCode = 4 ) var AgentErrorCode_name = map[int32]string{ 0: "NO_ERROR", 1: "INVALID_CONFIG", 2: "SCRIPT_NOT_FOUND", 3: "SCRIPT_TIMED_OUT", 4: "UNHANDLED_SCRIPT_ERROR", } var AgentErrorCode_value = map[string]int32{ "NO_ERROR": 0, "INVALID_CONFIG": 1, "SCRIPT_NOT_FOUND": 2, "SCRIPT_TIMED_OUT": 3, "UNHANDLED_SCRIPT_ERROR": 4, } func (x AgentErrorCode) String() string { return proto.EnumName(AgentErrorCode_name, int32(x)) } func (AgentErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{2} } type SnapshotRequest struct { // The operation id of the snapshot. OperationId int32 `protobuf:"varint,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` // A list of comma separated target/lun values, e.g "1/2,3/4". DiskList string `protobuf:"bytes,2,opt,name=disk_list,json=diskList,proto3" json:"disk_list,omitempty"` // The operation type. Type OperationType `protobuf:"varint,3,opt,name=type,proto3,enum=cloud.vmm.OperationType" json:"type,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SnapshotRequest) Reset() { *m = SnapshotRequest{} } func (m *SnapshotRequest) String() string { return proto.CompactTextString(m) } func (*SnapshotRequest) ProtoMessage() {} func (*SnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{0} } func (m *SnapshotRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SnapshotRequest.Unmarshal(m, b) } func (m *SnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_SnapshotRequest.Marshal(b, m, deterministic) } func (m *SnapshotRequest) XXX_Merge(src proto.Message) { xxx_messageInfo_SnapshotRequest.Merge(m, src) } func (m *SnapshotRequest) XXX_Size() int { return xxx_messageInfo_SnapshotRequest.Size(m) } func (m *SnapshotRequest) XXX_DiscardUnknown() { xxx_messageInfo_SnapshotRequest.DiscardUnknown(m) } var xxx_messageInfo_SnapshotRequest proto.InternalMessageInfo func (m *SnapshotRequest) GetOperationId() int32 { if m != nil { return m.OperationId } return 0 } func (m *SnapshotRequest) GetDiskList() string { if m != nil { return m.DiskList } return "" } func (m *SnapshotRequest) GetType() OperationType { if m != nil { return m.Type } return OperationType_NOT_SET } type ServerInfo struct { SupportedFeatures []SupportedFeatures `protobuf:"varint,1,rep,packed,name=supported_features,json=supportedFeatures,proto3,enum=cloud.vmm.SupportedFeatures" json:"supported_features,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ServerInfo) Reset() { *m = ServerInfo{} } func (m *ServerInfo) String() string { return proto.CompactTextString(m) } func (*ServerInfo) ProtoMessage() {} func (*ServerInfo) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{1} } func (m *ServerInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ServerInfo.Unmarshal(m, b) } func (m *ServerInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ServerInfo.Marshal(b, m, deterministic) } func (m *ServerInfo) XXX_Merge(src proto.Message) { xxx_messageInfo_ServerInfo.Merge(m, src) } func (m *ServerInfo) XXX_Size() int { return xxx_messageInfo_ServerInfo.Size(m) } func (m *ServerInfo) XXX_DiscardUnknown() { xxx_messageInfo_ServerInfo.DiscardUnknown(m) } var xxx_messageInfo_ServerInfo proto.InternalMessageInfo func (m *ServerInfo) GetSupportedFeatures() []SupportedFeatures { if m != nil { return m.SupportedFeatures } return nil } type SnapshotResponse struct { // The operation id of the snapshot. OperationId int32 `protobuf:"varint,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` // The return code of the scripts run by the guest. If this is non-zero, then // agent_return_code should be UNHANDLED_SCRIPT_ERROR. ScriptsReturnCode int32 `protobuf:"varint,2,opt,name=scripts_return_code,json=scriptsReturnCode,proto3" json:"scripts_return_code,omitempty"` // The agent return code. AgentReturnCode AgentErrorCode `protobuf:"varint,3,opt,name=agent_return_code,json=agentReturnCode,proto3,enum=cloud.vmm.AgentErrorCode" json:"agent_return_code,omitempty"` // The operation type. Type OperationType `protobuf:"varint,4,opt,name=type,proto3,enum=cloud.vmm.OperationType" json:"type,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *SnapshotResponse) Reset() { *m = SnapshotResponse{} } func (m *SnapshotResponse) String() string { return proto.CompactTextString(m) } func (*SnapshotResponse) ProtoMessage() {} func (*SnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{2} } func (m *SnapshotResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SnapshotResponse.Unmarshal(m, b) } func (m *SnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_SnapshotResponse.Marshal(b, m, deterministic) } func (m *SnapshotResponse) XXX_Merge(src proto.Message) { xxx_messageInfo_SnapshotResponse.Merge(m, src) } func (m *SnapshotResponse) XXX_Size() int { return xxx_messageInfo_SnapshotResponse.Size(m) } func (m *SnapshotResponse) XXX_DiscardUnknown() { xxx_messageInfo_SnapshotResponse.DiscardUnknown(m) } var xxx_messageInfo_SnapshotResponse proto.InternalMessageInfo func (m *SnapshotResponse) GetOperationId() int32 { if m != nil { return m.OperationId } return 0 } func (m *SnapshotResponse) GetScriptsReturnCode() int32 { if m != nil { return m.ScriptsReturnCode } return 0 } func (m *SnapshotResponse) GetAgentReturnCode() AgentErrorCode { if m != nil { return m.AgentReturnCode } return AgentErrorCode_NO_ERROR } func (m *SnapshotResponse) GetType() OperationType { if m != nil { return m.Type } return OperationType_NOT_SET } type GuestReady struct { RequestServerInfo bool `protobuf:"varint,1,opt,name=request_server_info,json=requestServerInfo,proto3" json:"request_server_info,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GuestReady) Reset() { *m = GuestReady{} } func (m *GuestReady) String() string { return proto.CompactTextString(m) } func (*GuestReady) ProtoMessage() {} func (*GuestReady) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{3} } func (m *GuestReady) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GuestReady.Unmarshal(m, b) } func (m *GuestReady) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GuestReady.Marshal(b, m, deterministic) } func (m *GuestReady) XXX_Merge(src proto.Message) { xxx_messageInfo_GuestReady.Merge(m, src) } func (m *GuestReady) XXX_Size() int { return xxx_messageInfo_GuestReady.Size(m) } func (m *GuestReady) XXX_DiscardUnknown() { xxx_messageInfo_GuestReady.DiscardUnknown(m) } var xxx_messageInfo_GuestReady proto.InternalMessageInfo func (m *GuestReady) GetRequestServerInfo() bool { if m != nil { return m.RequestServerInfo } return false } type GuestMessage struct { // Types that are valid to be assigned to Msg: // *GuestMessage_SnapshotRequest // *GuestMessage_ServerInfo Msg isGuestMessage_Msg `protobuf_oneof:"msg"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *GuestMessage) Reset() { *m = GuestMessage{} } func (m *GuestMessage) String() string { return proto.CompactTextString(m) } func (*GuestMessage) ProtoMessage() {} func (*GuestMessage) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{4} } func (m *GuestMessage) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GuestMessage.Unmarshal(m, b) } func (m *GuestMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GuestMessage.Marshal(b, m, deterministic) } func (m *GuestMessage) XXX_Merge(src proto.Message) { xxx_messageInfo_GuestMessage.Merge(m, src) } func (m *GuestMessage) XXX_Size() int { return xxx_messageInfo_GuestMessage.Size(m) } func (m *GuestMessage) XXX_DiscardUnknown() { xxx_messageInfo_GuestMessage.DiscardUnknown(m) } var xxx_messageInfo_GuestMessage proto.InternalMessageInfo type isGuestMessage_Msg interface { isGuestMessage_Msg() } type GuestMessage_SnapshotRequest struct { SnapshotRequest *SnapshotRequest `protobuf:"bytes,1,opt,name=snapshot_request,json=snapshotRequest,proto3,oneof"` } type GuestMessage_ServerInfo struct { ServerInfo *ServerInfo `protobuf:"bytes,2,opt,name=server_info,json=serverInfo,proto3,oneof"` } func (*GuestMessage_SnapshotRequest) isGuestMessage_Msg() {} func (*GuestMessage_ServerInfo) isGuestMessage_Msg() {} func (m *GuestMessage) GetMsg() isGuestMessage_Msg { if m != nil { return m.Msg } return nil } func (m *GuestMessage) GetSnapshotRequest() *SnapshotRequest { if x, ok := m.GetMsg().(*GuestMessage_SnapshotRequest); ok { return x.SnapshotRequest } return nil } func (m *GuestMessage) GetServerInfo() *ServerInfo { if x, ok := m.GetMsg().(*GuestMessage_ServerInfo); ok { return x.ServerInfo } return nil } // XXX_OneofWrappers is for the internal use of the proto package. func (*GuestMessage) XXX_OneofWrappers() []interface{} { return []interface{}{ (*GuestMessage_SnapshotRequest)(nil), (*GuestMessage_ServerInfo)(nil), } } type ServerAck struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } func (m *ServerAck) Reset() { *m = ServerAck{} } func (m *ServerAck) String() string { return proto.CompactTextString(m) } func (*ServerAck) ProtoMessage() {} func (*ServerAck) Descriptor() ([]byte, []int) { return fileDescriptor_ef5e407b8dc8e20f, []int{5} } func (m *ServerAck) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ServerAck.Unmarshal(m, b) } func (m *ServerAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ServerAck.Marshal(b, m, deterministic) } func (m *ServerAck) XXX_Merge(src proto.Message) { xxx_messageInfo_ServerAck.Merge(m, src) } func (m *ServerAck) XXX_Size() int { return xxx_messageInfo_ServerAck.Size(m) } func (m *ServerAck) XXX_DiscardUnknown() { xxx_messageInfo_ServerAck.DiscardUnknown(m) } var xxx_messageInfo_ServerAck proto.InternalMessageInfo func init() { proto.RegisterEnum("cloud.vmm.OperationType", OperationType_name, OperationType_value) proto.RegisterEnum("cloud.vmm.SupportedFeatures", SupportedFeatures_name, SupportedFeatures_value) proto.RegisterEnum("cloud.vmm.AgentErrorCode", AgentErrorCode_name, AgentErrorCode_value) proto.RegisterType((*SnapshotRequest)(nil), "cloud.vmm.SnapshotRequest") proto.RegisterType((*ServerInfo)(nil), "cloud.vmm.ServerInfo") proto.RegisterType((*SnapshotResponse)(nil), "cloud.vmm.SnapshotResponse") proto.RegisterType((*GuestReady)(nil), "cloud.vmm.GuestReady") proto.RegisterType((*GuestMessage)(nil), "cloud.vmm.GuestMessage") proto.RegisterType((*ServerAck)(nil), "cloud.vmm.ServerAck") } func init() { proto.RegisterFile("snapshot_service.proto", fileDescriptor_ef5e407b8dc8e20f) } var fileDescriptor_ef5e407b8dc8e20f = []byte{ // 608 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xc1, 0x6e, 0xda, 0x40, 0x10, 0xb5, 0x81, 0xb4, 0x30, 0x10, 0xb2, 0x6c, 0xd3, 0x94, 0x92, 0x1e, 0xa8, 0x4f, 0x28, 0x8a, 0x50, 0x45, 0x2f, 0x3d, 0xf4, 0xe2, 0x82, 0x09, 0x56, 0x13, 0x1b, 0xad, 0x9d, 0x4a, 0x3d, 0xad, 0x5c, 0x7b, 0x93, 0x5a, 0x09, 0x5e, 0x77, 0x77, 0x89, 0x94, 0x5e, 0xfa, 0x1b, 0xfd, 0x86, 0x7e, 0x52, 0xbf, 0xa6, 0xb2, 0x31, 0x60, 0x92, 0x1e, 0x72, 0x64, 0xe6, 0xcd, 0x63, 0xde, 0x7b, 0xb3, 0x86, 0x23, 0x99, 0x04, 0xa9, 0xfc, 0xce, 0x15, 0x95, 0x4c, 0xdc, 0xc5, 0x21, 0x1b, 0xa6, 0x82, 0x2b, 0x8e, 0x1b, 0xe1, 0x2d, 0x5f, 0x46, 0xc3, 0xbb, 0xc5, 0xc2, 0xf8, 0x05, 0x07, 0x5e, 0x01, 0x22, 0xec, 0xc7, 0x92, 0x49, 0x85, 0xdf, 0x42, 0x8b, 0xa7, 0x4c, 0x04, 0x2a, 0xe6, 0x09, 0x8d, 0xa3, 0xae, 0xde, 0xd7, 0x07, 0x7b, 0xa4, 0xb9, 0xa9, 0xd9, 0x11, 0x3e, 0x86, 0x46, 0x14, 0xcb, 0x1b, 0x7a, 0x1b, 0x4b, 0xd5, 0xad, 0xf4, 0xf5, 0x41, 0x83, 0xd4, 0xb3, 0xc2, 0x79, 0x2c, 0x15, 0x3e, 0x85, 0x9a, 0xba, 0x4f, 0x59, 0xb7, 0xda, 0xd7, 0x07, 0xed, 0x51, 0x77, 0xb8, 0xf9, 0xb3, 0xa1, 0xbb, 0xa6, 0xf0, 0xef, 0x53, 0x46, 0x72, 0x94, 0xf1, 0x15, 0xc0, 0x63, 0xe2, 0x8e, 0x09, 0x3b, 0xb9, 0xe2, 0xf8, 0x33, 0x60, 0xb9, 0x4c, 0x53, 0x2e, 0x14, 0x8b, 0xe8, 0x15, 0x0b, 0xd4, 0x52, 0x30, 0xd9, 0xd5, 0xfb, 0xd5, 0x41, 0x7b, 0xf4, 0xa6, 0xc4, 0xe4, 0xad, 0x41, 0xd3, 0x02, 0x43, 0x3a, 0xf2, 0x61, 0xc9, 0xf8, 0xab, 0x03, 0xda, 0x8a, 0x93, 0x29, 0x4f, 0x24, 0x7b, 0x8a, 0xba, 0x21, 0xbc, 0x90, 0xa1, 0x88, 0x53, 0x25, 0xa9, 0x60, 0x6a, 0x29, 0x12, 0x1a, 0xf2, 0x88, 0xe5, 0x3a, 0xf7, 0x48, 0xa7, 0x68, 0x91, 0xbc, 0x33, 0xe6, 0x11, 0xc3, 0x16, 0x74, 0x82, 0x6b, 0x96, 0xa8, 0x1d, 0xf4, 0x4a, 0xfd, 0xeb, 0xd2, 0xce, 0x66, 0x86, 0xb1, 0x84, 0xe0, 0x22, 0x9b, 0x22, 0x07, 0xf9, 0x4c, 0x89, 0x66, 0xed, 0x5b, 0xed, 0x49, 0xbe, 0x7d, 0x04, 0x38, 0xcb, 0xe2, 0x22, 0x2c, 0x88, 0xee, 0xb3, 0x95, 0xc5, 0x2a, 0xbe, 0x3c, 0x6a, 0x26, 0x68, 0x9c, 0x5c, 0xf1, 0x5c, 0x5c, 0x9d, 0x74, 0x8a, 0xd6, 0xd6, 0x67, 0xe3, 0xb7, 0x0e, 0xad, 0x7c, 0xfc, 0x82, 0x49, 0x19, 0x5c, 0x33, 0x7c, 0x06, 0x68, 0x73, 0x2c, 0x05, 0x3c, 0x9f, 0x6e, 0x8e, 0x7a, 0x65, 0xdb, 0x77, 0x4f, 0x65, 0xa6, 0x91, 0x03, 0xf9, 0xe0, 0x7a, 0x3e, 0x40, 0xb3, 0xbc, 0x41, 0x25, 0xe7, 0x78, 0x59, 0xe6, 0xd8, 0x6c, 0x31, 0xd3, 0x08, 0xc8, 0xcd, 0xaf, 0x4f, 0x7b, 0x50, 0x5d, 0xc8, 0x6b, 0xa3, 0x09, 0x8d, 0x15, 0xc4, 0x0c, 0x6f, 0x4e, 0x4c, 0xd8, 0xdf, 0x11, 0x8f, 0x9b, 0xf0, 0xdc, 0x71, 0x7d, 0xea, 0x59, 0x3e, 0xd2, 0x30, 0x82, 0xd6, 0x9c, 0x58, 0xd4, 0x73, 0xcc, 0xb9, 0x37, 0x73, 0x7d, 0xa4, 0xe3, 0x0e, 0xec, 0xcf, 0x5d, 0xcf, 0xdf, 0x96, 0x2a, 0x27, 0xa7, 0xd0, 0x79, 0x74, 0x2d, 0xb8, 0x0e, 0x35, 0xc7, 0x75, 0x2c, 0xa4, 0xe1, 0x7d, 0x68, 0xac, 0xc1, 0x1e, 0xd2, 0x4f, 0x7e, 0x42, 0x7b, 0x37, 0x27, 0xdc, 0x82, 0xba, 0xe3, 0x52, 0x8b, 0x10, 0x97, 0x20, 0x0d, 0x63, 0x68, 0xdb, 0xce, 0x17, 0xf3, 0xdc, 0x9e, 0xd0, 0xb1, 0xeb, 0x4c, 0xed, 0x33, 0xa4, 0xe3, 0x43, 0x40, 0xde, 0x98, 0xd8, 0x73, 0x9f, 0x66, 0xab, 0x4d, 0xdd, 0x4b, 0x67, 0x82, 0x2a, 0xa5, 0xaa, 0x6f, 0x5f, 0x58, 0x13, 0xea, 0x5e, 0xfa, 0xa8, 0x8a, 0x7b, 0x70, 0x74, 0xe9, 0xcc, 0x4c, 0x67, 0x72, 0x6e, 0x4d, 0x68, 0xd1, 0x5f, 0x71, 0xd7, 0x46, 0x7f, 0xf4, 0xed, 0x63, 0xf4, 0x56, 0x0f, 0x16, 0x4f, 0x01, 0x8d, 0x05, 0x0b, 0x14, 0x1b, 0xf3, 0x24, 0x61, 0x61, 0xe6, 0x03, 0x2e, 0xbb, 0xb9, 0xbd, 0x81, 0xde, 0xab, 0x87, 0xe5, 0x22, 0x5b, 0x43, 0x7b, 0xa7, 0x63, 0x17, 0xba, 0xb3, 0x20, 0x89, 0x6e, 0xd9, 0xfa, 0x21, 0xc8, 0xa9, 0xe0, 0x8b, 0x1c, 0x86, 0x8f, 0xff, 0x9b, 0xf0, 0x0a, 0xd6, 0x3b, 0x7c, 0x14, 0x9d, 0x19, 0xde, 0x18, 0xda, 0xb7, 0x67, 0xf9, 0xa7, 0xe4, 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x49, 0x78, 0xe7, 0x64, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 // SnapshotServiceClient is the client API for SnapshotService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type SnapshotServiceClient interface { // The client is expected to first send a "GuestReady" message, indicating // they are available and establishing the connection that vanadium writes // requests to the agent on. CreateConnection(ctx context.Context, in *GuestReady, opts ...grpc.CallOption) (SnapshotService_CreateConnectionClient, error) // Handles the agent's responses to the above requests. HandleResponsesFromGuest(ctx context.Context, in *SnapshotResponse, opts ...grpc.CallOption) (*ServerAck, error) } type snapshotServiceClient struct { cc *grpc.ClientConn } func NewSnapshotServiceClient(cc *grpc.ClientConn) SnapshotServiceClient { return &snapshotServiceClient{cc} } func (c *snapshotServiceClient) CreateConnection(ctx context.Context, in *GuestReady, opts ...grpc.CallOption) (SnapshotService_CreateConnectionClient, error) { stream, err := c.cc.NewStream(ctx, &_SnapshotService_serviceDesc.Streams[0], "/cloud.vmm.SnapshotService/CreateConnection", opts...) if err != nil { return nil, err } x := &snapshotServiceCreateConnectionClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } type SnapshotService_CreateConnectionClient interface { Recv() (*GuestMessage, error) grpc.ClientStream } type snapshotServiceCreateConnectionClient struct { grpc.ClientStream } func (x *snapshotServiceCreateConnectionClient) Recv() (*GuestMessage, error) { m := new(GuestMessage) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *snapshotServiceClient) HandleResponsesFromGuest(ctx context.Context, in *SnapshotResponse, opts ...grpc.CallOption) (*ServerAck, error) { out := new(ServerAck) err := c.cc.Invoke(ctx, "/cloud.vmm.SnapshotService/HandleResponsesFromGuest", in, out, opts...) if err != nil { return nil, err } return out, nil } // SnapshotServiceServer is the server API for SnapshotService service. type SnapshotServiceServer interface { // The client is expected to first send a "GuestReady" message, indicating // they are available and establishing the connection that vanadium writes // requests to the agent on. CreateConnection(*GuestReady, SnapshotService_CreateConnectionServer) error // Handles the agent's responses to the above requests. HandleResponsesFromGuest(context.Context, *SnapshotResponse) (*ServerAck, error) } // UnimplementedSnapshotServiceServer can be embedded to have forward compatible implementations. type UnimplementedSnapshotServiceServer struct { } func (*UnimplementedSnapshotServiceServer) CreateConnection(req *GuestReady, srv SnapshotService_CreateConnectionServer) error { return status.Errorf(codes.Unimplemented, "method CreateConnection not implemented") } func (*UnimplementedSnapshotServiceServer) HandleResponsesFromGuest(ctx context.Context, req *SnapshotResponse) (*ServerAck, error) { return nil, status.Errorf(codes.Unimplemented, "method HandleResponsesFromGuest not implemented") } func RegisterSnapshotServiceServer(s *grpc.Server, srv SnapshotServiceServer) { s.RegisterService(&_SnapshotService_serviceDesc, srv) } func _SnapshotService_CreateConnection_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(GuestReady) if err := stream.RecvMsg(m); err != nil { return err } return srv.(SnapshotServiceServer).CreateConnection(m, &snapshotServiceCreateConnectionServer{stream}) } type SnapshotService_CreateConnectionServer interface { Send(*GuestMessage) error grpc.ServerStream } type snapshotServiceCreateConnectionServer struct { grpc.ServerStream } func (x *snapshotServiceCreateConnectionServer) Send(m *GuestMessage) error { return x.ServerStream.SendMsg(m) } func _SnapshotService_HandleResponsesFromGuest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SnapshotResponse) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SnapshotServiceServer).HandleResponsesFromGuest(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/cloud.vmm.SnapshotService/HandleResponsesFromGuest", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SnapshotServiceServer).HandleResponsesFromGuest(ctx, req.(*SnapshotResponse)) } return interceptor(ctx, in, info, handler) } var _SnapshotService_serviceDesc = grpc.ServiceDesc{ ServiceName: "cloud.vmm.SnapshotService", HandlerType: (*SnapshotServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "HandleResponsesFromGuest", Handler: _SnapshotService_HandleResponsesFromGuest_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "CreateConnection", Handler: _SnapshotService_CreateConnection_Handler, ServerStreams: true, }, }, Metadata: "snapshot_service.proto", } guest-agent-20220622.00/google_guest_agent/stub.go000066400000000000000000000023471425470135300215730ustar00rootroot00000000000000// 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. // +build !windows package main import ( "errors" ) var errRegNotExist = errors.New("error") func resetPwd(username, pwd string) error { return nil } func readRegMultiString(key, name string) ([]string, error) { return nil, nil } func writeRegMultiString(key, name string, value []string) error { return nil } func deleteRegKey(key, name string) error { return nil } func getWindowsServiceImagePath(regKey string) (string, error) { return "", nil } func getWindowsExeVersion(path string) (versionInfo, error) { return versionInfo{0, 0}, nil } func checkWindowsServiceRunning(servicename string) bool { return false } guest-agent-20220622.00/google_guest_agent/system_windows.go000066400000000000000000000061211425470135300237060ustar00rootroot00000000000000// 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. package main import ( "os/exec" "strings" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "golang.org/x/sys/windows/registry" ) var errRegNotExist = registry.ErrNotExist var startRegKey = "Start" type ( DWORD uint32 LPWSTR *uint16 ) func init() { key, _, err := registry.CreateKey(registry.LOCAL_MACHINE, regKeyBase, registry.WRITE) if err != nil { logger.Fatalf(err.Error()) } key.Close() key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, addressKey, registry.WRITE) if err != nil { logger.Fatalf(err.Error()) } key.Close() } func readRegMultiString(key, name string) ([]string, error) { k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE) if err != nil { return nil, err } defer k.Close() s, _, err := k.GetStringsValue(name) if err != nil { return nil, err } return s, nil } func readRegString(key, name string) (string, error) { k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE) if err != nil { return "", err } defer k.Close() s, _, err := k.GetStringValue(name) if err != nil { return "", err } return s, nil } func readRegInteger(key, name string) (uint64, error) { k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.QUERY_VALUE) if err != nil { return 0, err } defer k.Close() i, _, err := k.GetIntegerValue(name) if err != nil { return 0, err } return i, nil } func writeRegMultiString(key, name string, value []string) error { k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.WRITE) if err != nil { return err } defer k.Close() return k.SetStringsValue(name, value) } func deleteRegKey(key, name string) error { k, err := registry.OpenKey(registry.LOCAL_MACHINE, key, registry.WRITE) if err != nil { return err } defer k.Close() return k.DeleteValue(name) } func checkWindowsServiceRunning(servicename string) bool { res := runCmdOutput(exec.Command("sc", "query", servicename)) return strings.Contains(res.Stdout(), "RUNNING") } func getWindowsServiceImagePath(regKey string) (string, error) { regValue, err := readRegString(regKey, "ImagePath") if err != nil { return "", err } imagePath := strings.Trim(string(regValue), `"`) return imagePath, nil } func getWindowsExeVersion(path string) (versionInfo, error) { psCmd := "(Get-Item '" + path + "').VersionInfo.FileVersion" psVer, err := exec.Command("powershell", "-c", psCmd).Output() if err != nil { return versionInfo{0, 0}, err } return parseVersionInfo(psVer) } guest-agent-20220622.00/google_guest_agent/windows_accounts.go000066400000000000000000000273641425470135300242150ustar00rootroot00000000000000// 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. package main import ( "bytes" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/base64" "encoding/json" "fmt" "hash" "math/big" "reflect" "strconv" "strings" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) var ( accountRegKey = "PublicKeys" credsWriter = &utils.SerialPort{Port: "COM4"} minSSHVersion = versionInfo{8, 6} sshdRegKey = `SYSTEM\CurrentControlSet\Services\sshd` ) // newPwd will generate a random password that meets Windows complexity // requirements: https://technet.microsoft.com/en-us/library/cc786468. // Characters that are difficult for users to type on a command line (quotes, // non english characters) are not used. func newPwd(userPwLgth int) (string, error) { var pwLgth int minPwLgth := 15 maxPwLgth := 255 lower := []byte("abcdefghijklmnopqrstuvwxyz") upper := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") numbers := []byte("0123456789") special := []byte(`~!@#$%^&*_-+=|\(){}[]:;<>,.?/`) chars := bytes.Join([][]byte{lower, upper, numbers, special}, nil) pwLgth = minPwLgth if userPwLgth > minPwLgth { pwLgth = userPwLgth } if userPwLgth > maxPwLgth { pwLgth = maxPwLgth } for { b := make([]byte, pwLgth) for i := range b { ci, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) if err != nil { return "", err } b[i] = chars[ci.Int64()] } var l, u, n, s int if bytes.ContainsAny(lower, string(b)) { l = 1 } if bytes.ContainsAny(upper, string(b)) { u = 1 } if bytes.ContainsAny(numbers, string(b)) { n = 1 } if bytes.ContainsAny(special, string(b)) { s = 1 } // If the password does not meet Windows complexity requirements, try again. // https://technet.microsoft.com/en-us/library/cc786468 if l+u+n+s >= 3 { return string(b), nil } } } type credsJSON struct { ErrorMessage string `json:"errorMessage,omitempty"` EncryptedPassword string `json:"encryptedPassword,omitempty"` UserName string `json:"userName,omitempty"` PasswordFound bool `json:"passwordFound,omitempty"` Exponent string `json:"exponent,omitempty"` Modulus string `json:"modulus,omitempty"` HashFunction string `json:"hashFunction,omitempty"` } func printCreds(creds *credsJSON) error { data, err := json.Marshal(creds) if err != nil { return err } _, err = credsWriter.Write(append(data, []byte("\n")...)) return err } var badExpire []string func (k windowsKey) expired() bool { expired, err := utils.CheckExpired(k.ExpireOn) if err != nil { if !utils.ContainsString(k.ExpireOn, badExpire) { logger.Errorf("error parsing time: %s", err) badExpire = append(badExpire, k.ExpireOn) } return true } return expired } func (k windowsKey) createOrResetPwd() (*credsJSON, error) { pwd, err := newPwd(k.PasswordLength) if err != nil { return nil, fmt.Errorf("error creating password: %v", err) } if _, err := userExists(k.UserName); err == nil { logger.Infof("Resetting password for user %s", k.UserName) if err := resetPwd(k.UserName, pwd); err != nil { return nil, fmt.Errorf("error running resetPwd: %v", err) } if k.AddToAdministrators != nil && *k.AddToAdministrators == true { if err := addUserToGroup(k.UserName, "Administrators"); err != nil { return nil, fmt.Errorf("error running addUserToGroup: %v", err) } } } else { logger.Infof("Creating user %s", k.UserName) if err := createUser(k.UserName, pwd); err != nil { return nil, fmt.Errorf("error running createUser: %v", err) } if k.AddToAdministrators == nil || *k.AddToAdministrators == true { if err := addUserToGroup(k.UserName, "Administrators"); err != nil { return nil, fmt.Errorf("error running addUserToGroup: %v", err) } } } return createcredsJSON(k, pwd) } func createSSHUser(user string) error { pwd, err := newPwd(20) if err != nil { return fmt.Errorf("error creating password: %v", err) } if _, err := userExists(user); err == nil { return nil } logger.Infof("Creating user %s", user) if err := createUser(user, pwd); err != nil { return fmt.Errorf("error running createUser: %v", err) } if err := addUserToGroup(user, "Administrators"); err != nil { return fmt.Errorf("error running addUserToGroup: %v", err) } return nil } func createcredsJSON(k windowsKey, pwd string) (*credsJSON, error) { mod, err := base64.StdEncoding.DecodeString(k.Modulus) if err != nil { return nil, fmt.Errorf("error decoding modulus: %v", err) } exp, err := base64.StdEncoding.DecodeString(k.Exponent) if err != nil { return nil, fmt.Errorf("error decoding exponent: %v", err) } key := &rsa.PublicKey{ N: new(big.Int).SetBytes(mod), E: int(new(big.Int).SetBytes(exp).Int64()), } if k.HashFunction == "" { k.HashFunction = "sha1" } var hashFunc hash.Hash switch k.HashFunction { case "sha1": hashFunc = sha1.New() case "sha256": hashFunc = sha256.New() case "sha512": hashFunc = sha512.New() default: return nil, fmt.Errorf("unknown hash function requested: %q", k.HashFunction) } encPwd, err := rsa.EncryptOAEP(hashFunc, rand.Reader, key, []byte(pwd), nil) if err != nil { return nil, fmt.Errorf("error encrypting password: %v", err) } return &credsJSON{ PasswordFound: true, Exponent: k.Exponent, Modulus: k.Modulus, UserName: k.UserName, HashFunction: k.HashFunction, EncryptedPassword: base64.StdEncoding.EncodeToString(encPwd), }, nil } func getWinSSHEnabled(md *metadata) bool { var enable bool if md.Project.Attributes.EnableWindowsSSH != nil { enable = *md.Project.Attributes.EnableWindowsSSH } if md.Instance.Attributes.EnableWindowsSSH != nil { enable = *md.Instance.Attributes.EnableWindowsSSH } return enable } type winAccountsMgr struct{} func (a *winAccountsMgr) diff() bool { oldSSHEnable := getWinSSHEnabled(oldMetadata) sshEnable := getWinSSHEnabled(newMetadata) if sshEnable != oldSSHEnable { return true } if !reflect.DeepEqual(newMetadata.Instance.Attributes.WindowsKeys, oldMetadata.Instance.Attributes.WindowsKeys) { return true } if !compareStringSlice(newMetadata.Instance.Attributes.SSHKeys, oldMetadata.Instance.Attributes.SSHKeys) { return true } if !compareStringSlice(newMetadata.Project.Attributes.SSHKeys, oldMetadata.Project.Attributes.SSHKeys) { return true } if newMetadata.Instance.Attributes.BlockProjectKeys != oldMetadata.Instance.Attributes.BlockProjectKeys { return true } return false } func (a *winAccountsMgr) timeout() bool { return false } func (a *winAccountsMgr) disabled(os string) (disabled bool) { if os != "windows" { return true } disabled, err := config.Section("accountManager").Key("disable").Bool() if err == nil { return disabled } if newMetadata.Instance.Attributes.DisableAccountManager != nil { return *newMetadata.Instance.Attributes.DisableAccountManager } if newMetadata.Project.Attributes.DisableAccountManager != nil { return *newMetadata.Project.Attributes.DisableAccountManager } return false } var badKeys []string type versionInfo struct { major int minor int } func (v versionInfo) String() string { return fmt.Sprintf("%d.%d", v.major, v.minor) } func parseVersionInfo(psOutput []byte) (versionInfo, error) { verInfo := versionInfo{0, 0} verStr := strings.TrimSpace(string(psOutput)) splitVer := strings.Split(verStr, ".") if len(splitVer) < 2 { return verInfo, fmt.Errorf("Cannot parse OpenSSH version string: %v", verStr) } majorVer, err := strconv.Atoi(splitVer[0]) if err != nil { return verInfo, err } verInfo.major = majorVer minorVer, err := strconv.Atoi(splitVer[1]) if err != nil { return verInfo, err } verInfo.minor = minorVer return verInfo, nil } func versionOk(checkVersion versionInfo, minVersion versionInfo) error { versionError := fmt.Errorf("Detected OpenSSH version may be incompatible with enable_windows_ssh. Found version %s, Need Version: %s", checkVersion, minVersion) if checkVersion.major < minVersion.major { return versionError } if checkVersion.major == minVersion.major && checkVersion.minor < minVersion.minor { return versionError } return nil } func verifyWinSSHVersion() error { sshdPath, err := getWindowsServiceImagePath(sshdRegKey) if err != nil { return fmt.Errorf("Cannot determine sshd path: %v", err) } sshdVersion, err := getWindowsExeVersion(sshdPath) if err != nil { return fmt.Errorf("Cannot determine OpenSSH Version: %v", err) } return versionOk(sshdVersion, minSSHVersion) } func (a *winAccountsMgr) set() error { oldSSHEnable := getWinSSHEnabled(oldMetadata) sshEnable := getWinSSHEnabled(newMetadata) if sshEnable { if sshEnable != oldSSHEnable { err := verifyWinSSHVersion() if err != nil { logger.Warningf(err.Error()) } if !checkWindowsServiceRunning("sshd") { logger.Warningf("The 'enable-windows-ssh' metadata key is set to 'true' " + "but sshd does not appear to be running.") } } if sshKeys == nil { logger.Debugf("initialize sshKeys map") sshKeys = make(map[string][]string) } mdkeys := newMetadata.Instance.Attributes.SSHKeys if !newMetadata.Instance.Attributes.BlockProjectKeys { mdkeys = append(mdkeys, newMetadata.Project.Attributes.SSHKeys...) } mdKeyMap := getUserKeys(mdkeys) for user := range mdKeyMap { if err := createSSHUser(user); err != nil { logger.Errorf("Error creating user: %s", err) } } } newKeys := newMetadata.Instance.Attributes.WindowsKeys regKeys, err := readRegMultiString(regKeyBase, accountRegKey) if err != nil && err != errRegNotExist { return err } toAdd := compareAccounts(newKeys, regKeys) for _, key := range toAdd { creds, err := key.createOrResetPwd() if err == nil { printCreds(creds) continue } logger.Errorf("error setting password: %s", err) creds = &credsJSON{ PasswordFound: false, Exponent: key.Exponent, Modulus: key.Modulus, UserName: key.UserName, ErrorMessage: err.Error(), } printCreds(creds) } var jsonKeys []string for _, key := range newKeys { jsn, err := json.Marshal(key) if err != nil { // This *should* never happen as each key was just Unmarshalled above. logger.Errorf("Failed to marshal windows key to JSON: %s", err) continue } jsonKeys = append(jsonKeys, string(jsn)) } return writeRegMultiString(regKeyBase, accountRegKey, jsonKeys) } var badReg []string func compareAccounts(newKeys windowsKeys, oldStrKeys []string) windowsKeys { if len(newKeys) == 0 { return nil } if len(oldStrKeys) == 0 { return newKeys } var oldKeys windowsKeys for _, s := range oldStrKeys { var key windowsKey if err := json.Unmarshal([]byte(s), &key); err != nil { if !utils.ContainsString(s, badReg) { logger.Errorf("Bad windows key from registry: %s", err) badReg = append(badReg, s) } continue } oldKeys = append(oldKeys, key) } var toAdd windowsKeys for _, key := range newKeys { if func(key windowsKey, oldKeys windowsKeys) bool { for _, oldKey := range oldKeys { if oldKey.UserName == key.UserName && oldKey.Modulus == key.Modulus && oldKey.ExpireOn == key.ExpireOn { return false } } return true }(key, oldKeys) { toAdd = append(toAdd, key) } } return toAdd } guest-agent-20220622.00/google_guest_agent/windows_accounts_test.go000066400000000000000000000244771425470135300252560ustar00rootroot00000000000000// 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. package main import ( "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/base64" "hash" "math/big" "reflect" "testing" "time" "unicode" "github.com/go-ini/ini" ) func mkptr(b bool) *bool { ret := b return &ret } func TestExpired(t *testing.T) { var tests = []struct { sTime string e bool }{ {time.Now().Add(5 * time.Minute).Format(time.RFC3339), false}, {time.Now().Add(-5 * time.Minute).Format(time.RFC3339), true}, {"some bad time", true}, } for _, tt := range tests { k := windowsKey{ExpireOn: tt.sTime} if tt.e != k.expired() { t.Errorf("windowsKey.expired() with ExpiredOn %q should return %t", k.ExpireOn, tt.e) } } } func TestAccountsDisabled(t *testing.T) { var tests = []struct { name string data []byte md *metadata want bool }{ {"not explicitly disabled", []byte(""), &metadata{}, false}, {"enabled in cfg only", []byte("[accountManager]\ndisable=false"), &metadata{}, false}, {"disabled in cfg only", []byte("[accountManager]\ndisable=true"), &metadata{}, true}, {"disabled in cfg, enabled in instance metadata", []byte("[accountManager]\ndisable=true"), &metadata{Instance: instance{Attributes: attributes{DisableAccountManager: mkptr(false)}}}, true}, {"enabled in cfg, disabled in instance metadata", []byte("[accountManager]\ndisable=false"), &metadata{Instance: instance{Attributes: attributes{DisableAccountManager: mkptr(true)}}}, false}, {"enabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAccountManager: mkptr(false)}}}, false}, {"enabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{DisableAccountManager: mkptr(false)}}}, false}, {"disabled in instance metadata only", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAccountManager: mkptr(true)}}}, true}, {"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata{Instance: instance{Attributes: attributes{DisableAccountManager: mkptr(false)}}, Project: project{Attributes: attributes{DisableAccountManager: mkptr(true)}}}, false}, {"disabled in project metadata only", []byte(""), &metadata{Project: project{Attributes: attributes{DisableAccountManager: mkptr(true)}}}, true}, } for _, tt := range tests { cfg, err := ini.InsensitiveLoad(tt.data) if err != nil { t.Errorf("test case %q: error parsing config: %v", tt.name, err) continue } if cfg == nil { cfg = &ini.File{} } newMetadata = tt.md config = cfg got := (&winAccountsMgr{}).disabled("windows") if got != tt.want { t.Errorf("test case %q, accounts.disabled() got: %t, want: %t", tt.name, got, tt.want) } } got := (&winAccountsMgr{}).disabled("linux") if got != true { t.Errorf("winAccountsMgr.disabled(\"linux\") got: %t, want: true", got) } } // rename this with leading disabled because this is a resource // intensive test. this test takes approx. 141 seconds to complete, next // longest test is 0.43 seconds. func disabledTestNewPwd(t *testing.T) { minPasswordLength := 15 maxPasswordLength := 255 var tests = []struct { name string passwordLength int wantPasswordLength int }{ {"0 characters, default value", 0, minPasswordLength}, {"5 characters, below min", 5, minPasswordLength}, {"15 characters", 5, minPasswordLength}, {"30 characters", 30, 30}, {"127 characters", 127, 127}, {"254 characters", 254, 254}, {"256 characters", 256, maxPasswordLength}, } for _, tt := range tests { for i := 0; i < 100000; i++ { pwd, err := newPwd(tt.passwordLength) if err != nil { t.Fatal(err) } if len(pwd) != tt.wantPasswordLength { t.Errorf("Password is not %d characters: len(%s)=%d", tt.wantPasswordLength, pwd, len(pwd)) } var l, u, n, s int for _, r := range pwd { switch { case unicode.IsLower(r): l = 1 case unicode.IsUpper(r): u = 1 case unicode.IsDigit(r): n = 1 case unicode.IsPunct(r) || unicode.IsSymbol(r): s = 1 } } if l+u+n+s < 3 { t.Errorf("Password does not have at least one character from 3 categories: '%v'", pwd) } } } } func TestCreatecredsJSON(t *testing.T) { pwd := "password" prv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("error generating key: %v", err) } k := windowsKey{ Email: "email", ExpireOn: "expire", Exponent: base64.StdEncoding.EncodeToString(new(big.Int).SetInt64(int64(prv.PublicKey.E)).Bytes()), Modulus: base64.StdEncoding.EncodeToString(prv.PublicKey.N.Bytes()), UserName: "username", } for name, hashFunc := range map[string]hash.Hash{"": sha1.New(), "sha1": sha1.New(), "sha256": sha256.New(), "sha512": sha512.New()} { k.HashFunction = name c, err := createcredsJSON(k, pwd) if err != nil { t.Fatalf("error running createcredsJSON: %v", err) } if k.HashFunction == "" { k.HashFunction = "sha1" } bPwd, err := base64.StdEncoding.DecodeString(c.EncryptedPassword) if err != nil { t.Fatalf("error base64 decoding encoded pwd: %v", err) } decPwd, err := rsa.DecryptOAEP(hashFunc, rand.Reader, prv, bPwd, nil) if err != nil { t.Fatalf("error decrypting password: %v", err) } if pwd != string(decPwd) { t.Errorf("decrypted password does not match expected for hash func %q, got: %s, want: %s", name, string(decPwd), pwd) } if k.UserName != c.UserName { t.Errorf("returned credsJSON UserName field unexpected, got: %s, want: %s", c.UserName, k.UserName) } if k.HashFunction != c.HashFunction { t.Errorf("returned credsJSON HashFunction field unexpected, got: %s, want: %s", c.HashFunction, k.HashFunction) } if !c.PasswordFound { t.Error("returned credsJSON PasswordFound field is not true") } } } func TestCompareAccounts(t *testing.T) { var tests = []struct { newKeys windowsKeys oldStrKeys []string wantAdd windowsKeys }{ // These should return toAdd: // In MD, not Reg {windowsKeys{{UserName: "foo"}}, nil, windowsKeys{{UserName: "foo"}}}, {windowsKeys{{UserName: "foo"}}, []string{`{"UserName":"bar"}`}, windowsKeys{{UserName: "foo"}}}, // These should return nothing: // In Reg and MD {windowsKeys{{UserName: "foo"}}, []string{`{"UserName":"foo"}`}, nil}, // In Reg, not MD {nil, []string{`{UserName":"foo"}`}, nil}, } for _, tt := range tests { toAdd := compareAccounts(tt.newKeys, tt.oldStrKeys) if !reflect.DeepEqual(tt.wantAdd, toAdd) { t.Errorf("toAdd does not match expected: newKeys: %v, oldStrKeys: %q, got: %v, want: %v", tt.newKeys, tt.oldStrKeys, toAdd, tt.wantAdd) } } } func TestGetUserKeys(t *testing.T) { var tests = []struct { key string expectedValid int }{ {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0000"}`, 1, }, {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700"}`, 1, }, {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700", "futureField": "UNUSED_FIELDS_IGNORED"}`, 1, }, {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0000"}`, 0, }, {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0700"}`, 0, }, {`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"INVALID_TIMESTAMP"}`, 0, }, {`user:ssh-rsa [KEY] google-ssh`, 0, }, {`user:ssh-rsa [KEY] user`, 1, }, {`user:ssh-rsa [KEY]`, 1, }, {`malformed-ssh-keys [KEY] google-ssh`, 0, }, {`:malformed-ssh-keys [KEY] google-ssh`, 0, }, } for _, tt := range tests { ret := getUserKeys([]string{tt.key}) if userKeys, _ := ret["user"]; len(userKeys) != tt.expectedValid { t.Errorf("expected %d valid keys from getUserKeys, but %d", tt.expectedValid, len(userKeys)) } } } func TestVersionOk(t *testing.T) { tests := []struct { version versionInfo minVersion versionInfo hasErr bool }{ { version: versionInfo{8, 6}, minVersion: versionInfo{8, 6}, hasErr: false, }, { version: versionInfo{9, 3}, minVersion: versionInfo{8, 6}, hasErr: false, }, { version: versionInfo{8, 3}, minVersion: versionInfo{8, 6}, hasErr: true, }, { version: versionInfo{7, 9}, minVersion: versionInfo{8, 6}, hasErr: true, }, } for _, tt := range tests { err := versionOk(tt.version, tt.minVersion) hasErr := err != nil if hasErr != tt.hasErr { t.Errorf("versionOk error not correct: Got: %v, Want: %v for Version %d.%d with Min Version of %d.%d", hasErr, tt.hasErr, tt.version.major, tt.version.minor, tt.minVersion.major, tt.minVersion.minor) } } } func TestParseVersionInfo(t *testing.T) { tests := []struct { psOutput []byte expectedVer versionInfo expectErr bool }{ { psOutput: []byte("8.6.0.0\r\n"), expectedVer: versionInfo{8, 6}, expectErr: false, }, { psOutput: []byte("8.6.0.0"), expectedVer: versionInfo{8, 6}, expectErr: false, }, { psOutput: []byte("8.6\r\n"), expectedVer: versionInfo{8, 6}, expectErr: false, }, { psOutput: []byte("12345.34567.34566.3463456\r\n"), expectedVer: versionInfo{12345, 34567}, expectErr: false, }, { psOutput: []byte("8\r\n"), expectedVer: versionInfo{0, 0}, expectErr: true, }, { psOutput: []byte("\r\n"), expectedVer: versionInfo{0, 0}, expectErr: true, }, } for _, tt := range tests { verInfo, err := parseVersionInfo(tt.psOutput) hasErr := err != nil if verInfo != tt.expectedVer || hasErr != tt.expectErr { t.Errorf("parseVersionInfo(%v) not correct: Got: %v, Error: %v, Want: %v, Error: %v", tt.psOutput, verInfo, hasErr, tt.expectedVer, tt.expectErr) } } } guest-agent-20220622.00/google_guest_agent/wsfc.go000066400000000000000000000157351425470135300215650ustar00rootroot00000000000000// 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. package main import ( "net" "strings" "sync" "time" "github.com/GoogleCloudPlatform/guest-logging-go/logger" ) const wsfcDefaultAgentPort = "59998" type agentState int // Enum for agentState const ( running agentState = iota stopped ) var ( once sync.Once agentInstance *wsfcAgent ) type wsfcManager struct { agentNewState agentState agentNewPort string agent healthAgent } // Create new wsfcManager based on metadata agent request state will be set to // running if one of the following is true: // - EnableWSFC is set // - WSFCAddresses is set (As an advanced setting, it will always override EnableWSFC flag) func newWsfcManager() *wsfcManager { newState := stopped if func() bool { enabled, err := config.Section("wsfc").Key("enable").Bool() if err == nil { return enabled } if config.Section("wsfc").Key("addresses").String() != "" { return true } if newMetadata.Instance.Attributes.EnableWSFC != nil { return *newMetadata.Instance.Attributes.EnableWSFC } if newMetadata.Instance.Attributes.WSFCAddresses != "" { return true } if newMetadata.Project.Attributes.EnableWSFC != nil { return *newMetadata.Project.Attributes.EnableWSFC } if newMetadata.Project.Attributes.WSFCAddresses != "" { return true } return false }() { newState = running } newPort := wsfcDefaultAgentPort port := config.Section("wsfc").Key("port").String() if port != "" { newPort = port } else if newMetadata.Instance.Attributes.WSFCAgentPort != "" { newPort = newMetadata.Instance.Attributes.WSFCAgentPort } else if newMetadata.Project.Attributes.WSFCAgentPort != "" { newPort = newMetadata.Instance.Attributes.WSFCAgentPort } return &wsfcManager{agentNewState: newState, agentNewPort: newPort, agent: getWsfcAgentInstance()} } // Implement manager.diff() func (m *wsfcManager) diff() bool { return m.agentNewState != m.agent.getState() || m.agentNewPort != m.agent.getPort() } // Implement manager.disabled(). // wsfc manager is always enabled. The manager is just a broker which manages the state of wsfcAgent. User // can disable the wsfc feature by setting the metadata. If the manager is disabled, the agent will stop. func (m *wsfcManager) disabled(os string) bool { return false } func (m *wsfcManager) timeout() bool { return false } // Diff will always be called before set. So in set, only two cases are possible: // - state changed: start or stop the wsfc agent accordingly // - port changed: restart the agent if it is running func (m *wsfcManager) set() error { m.agent.setPort(m.agentNewPort) // if state changes if m.agentNewState != m.agent.getState() { if m.agentNewState == running { return m.agent.run() } return m.agent.stop() } // If port changed if m.agent.getState() == running { if err := m.agent.stop(); err != nil { return err } return m.agent.run() } return nil } // interface for agent answering health check ping type healthAgent interface { getState() agentState getPort() string setPort(string) run() error stop() error } // Windows failover cluster agent, implements healthAgent interface type wsfcAgent struct { port string waitGroup *sync.WaitGroup listener *net.TCPListener } // Start agent and taking tcp request func (a *wsfcAgent) run() error { if a.getState() == running { logger.Infof("wsfc agent is already running") return nil } logger.Infof("Starting wsfc agent...") listenerAddr, err := net.ResolveTCPAddr("tcp", ":"+a.port) if err != nil { return err } listener, err := net.ListenTCP("tcp", listenerAddr) if err != nil { return err } // goroutine for handling request go func() { for { conn, err := listener.Accept() if err != nil { // if err is not due to listener closed, return if opErr, ok := err.(*net.OpError); ok && strings.Contains(opErr.Error(), "closed") { logger.Infof("wsfc agent - tcp listener closed.") return } logger.Errorf("wsfc agent - error on accepting request: %s", err) continue } a.waitGroup.Add(1) go a.handleHealthCheckRequest(conn) } }() logger.Infof("wsfc agent stared. Listening on port: %s", a.port) a.listener = listener return nil } // Handle health check request. // The request payload is WSFC ip address. // Sendback 1 if ipaddress is found locally and 0 otherwise. func (a *wsfcAgent) handleHealthCheckRequest(conn net.Conn) { defer closer(conn) defer a.waitGroup.Done() conn.SetDeadline(time.Now().Add(time.Second)) buf := make([]byte, 1024) // Read the incoming connection into the buffer. reqLen, err := conn.Read(buf) if err != nil { logger.Errorf("wsfc - error on processing tcp request for network heartbeat health check: %s", err) return } wsfcIP := strings.TrimSpace(string(buf[:reqLen])) reply, err := checkIPExist(wsfcIP) if err != nil { logger.Errorf("wsfc - error on checking local ip: %s", err) } conn.Write([]byte(reply)) } // Stop agent. Will wait for all existing request to be completed. func (a *wsfcAgent) stop() error { if a.getState() == stopped { logger.Infof("wsfc agent already stopped.") return nil } logger.Infof("Stopping wsfc agent...") // close listener first to avoid taking additional request err := a.listener.Close() // wait for exiting request to finish a.waitGroup.Wait() a.listener = nil logger.Infof("wsfc agent stopped.") return err } // Get the current state of the agent. If there is a valid listener, // return state running and if listener is nil, return stopped func (a *wsfcAgent) getState() agentState { if a.listener != nil { return running } return stopped } func (a *wsfcAgent) getPort() string { return a.port } func (a *wsfcAgent) setPort(newPort string) { if newPort != a.port { logger.Infof("update wsfc agent from port %v to %v", a.port, newPort) a.port = newPort } } // Create wsfc agent only once func getWsfcAgentInstance() *wsfcAgent { once.Do(func() { agentInstance = &wsfcAgent{ port: wsfcDefaultAgentPort, waitGroup: &sync.WaitGroup{}, listener: nil, } }) return agentInstance } // help func to check whether the ip exists on local host. func checkIPExist(ip string) (string, error) { addrs, err := net.InterfaceAddrs() if err != nil { return "0", err } for _, address := range addrs { if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { ipString := ipnet.IP.To4().String() if ip == ipString { return "1", nil } } } return "0", nil } guest-agent-20220622.00/google_guest_agent/wsfc_test.go000066400000000000000000000201621425470135300226120ustar00rootroot00000000000000// 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. package main import ( "bufio" "errors" "fmt" "net" "reflect" "testing" "github.com/go-ini/ini" ) func setEnableWSFC(md metadata, enabled *bool) *metadata { md.Instance.Attributes.EnableWSFC = enabled return &md } func setWSFCAddresses(md metadata, wsfcAddresses string) *metadata { md.Instance.Attributes.WSFCAddresses = wsfcAddresses return &md } func setWSFCAgentPort(md metadata, wsfcPort string) *metadata { md.Instance.Attributes.WSFCAgentPort = wsfcPort return &md } var ( testAgent = getWsfcAgentInstance() testMetadata = metadata{} testListener = &net.TCPListener{} ) func TestNewWsfcManager(t *testing.T) { type args struct { newMetadata *metadata } tests := []struct { name string args args want *wsfcManager }{ {"empty meta config", args{&testMetadata}, &wsfcManager{agentNewState: stopped, agentNewPort: wsfcDefaultAgentPort, agent: testAgent}}, {"wsfc enabled", args{setEnableWSFC(testMetadata, mkptr(true))}, &wsfcManager{agentNewState: running, agentNewPort: wsfcDefaultAgentPort, agent: testAgent}}, {"wsfc addrs is set", args{setWSFCAddresses(testMetadata, "0.0.0.0")}, &wsfcManager{agentNewState: running, agentNewPort: wsfcDefaultAgentPort, agent: testAgent}}, {"wsfc port is set", args{setWSFCAgentPort(testMetadata, "1818")}, &wsfcManager{agentNewState: stopped, agentNewPort: "1818", agent: testAgent}}, } config = ini.Empty() for _, tt := range tests { newMetadata = tt.args.newMetadata if got := newWsfcManager(); !reflect.DeepEqual(got, tt.want) { t.Errorf("test case %q: newWsfcManager() = %v, want %v", tt.name, got, tt.want) } } } func TestWsfcManagerDiff(t *testing.T) { tests := []struct { name string m *wsfcManager want bool }{ {"state change from stop to running", &wsfcManager{agentNewState: running, agent: &wsfcAgent{listener: nil}}, true}, {"state change from running to stop", &wsfcManager{agentNewState: stopped, agent: &wsfcAgent{listener: testListener}}, true}, {"port changed", &wsfcManager{agentNewPort: "1818", agent: &wsfcAgent{port: wsfcDefaultAgentPort}}, true}, {"state does not change both running", &wsfcManager{agentNewState: running, agent: &wsfcAgent{listener: testListener}}, false}, {"state does not change both stopped", &wsfcManager{agentNewState: stopped, agent: &wsfcAgent{listener: nil}}, false}, } for _, tt := range tests { if got := tt.m.diff(); got != tt.want { t.Errorf("test case %q: wsfcManager.diff() = %v, want %v", tt.name, got, tt.want) } } } // Mock health agent for unit testing type mockAgent struct { state agentState port string runError bool stopError bool runInvoked bool stopInvoked bool } func (a *mockAgent) getState() agentState { return a.state } func (a *mockAgent) getPort() string { return a.port } func (a *mockAgent) setPort(newPort string) { a.port = newPort } func (a *mockAgent) run() error { a.runInvoked = true if a.runError { return errors.New("Run error") } a.state = running return nil } func (a *mockAgent) stop() error { a.stopInvoked = true if a.stopError { return errors.New("Stop error") } a.state = stopped return nil } func TestWsfcManagerSet(t *testing.T) { tests := []struct { name string m *wsfcManager wantErr bool runInvoked bool stopInvoked bool }{ {"set start agent", &wsfcManager{agentNewState: running, agent: &mockAgent{state: stopped}}, false, true, false}, {"set start agent error", &wsfcManager{agentNewState: running, agent: &mockAgent{state: stopped, runError: true}}, true, true, false}, {"set stop agent", &wsfcManager{agentNewState: stopped, agent: &mockAgent{state: running}}, false, false, true}, {"set stop agent error", &wsfcManager{agentNewState: stopped, agent: &mockAgent{state: running, stopError: true}}, true, false, true}, {"set restart agent", &wsfcManager{agentNewState: running, agentNewPort: "1", agent: &mockAgent{state: running, port: "0"}}, false, true, true}, {"set restart agent stop error", &wsfcManager{agentNewState: running, agentNewPort: "1", agent: &mockAgent{state: running, port: "0", stopError: true}}, true, false, true}, {"set restart agent start error", &wsfcManager{agentNewState: running, agentNewPort: "1", agent: &mockAgent{state: running, port: "0", runError: true}}, true, true, true}, {"set do nothing", &wsfcManager{agentNewState: stopped, agentNewPort: "1", agent: &mockAgent{state: stopped, port: "0"}}, false, false, false}, } for _, tt := range tests { if err := tt.m.set(); (err != nil) != tt.wantErr { t.Errorf("wsfcManager.set() error = %v, wantErr %v", err, tt.wantErr) } mAgent := tt.m.agent.(*mockAgent) if gotRunInvoked := mAgent.runInvoked; gotRunInvoked != tt.runInvoked { t.Errorf("wsfcManager.set() runInvoked = %v, want %v", gotRunInvoked, tt.runInvoked) } if gotStopInvoked := mAgent.stopInvoked; gotStopInvoked != tt.stopInvoked { t.Errorf("wsfcManager.set() stopInvoked = %v, want %v", gotStopInvoked, tt.stopInvoked) } if tt.m.agentNewPort != mAgent.port { t.Errorf("wsfcManager.set() does not set prot, agent port = %v, want %v", mAgent.port, tt.m.agentNewPort) } } } func getHealthCheckResponce(request string, agent healthAgent) (string, error) { serverAddr := "localhost:" + agent.getPort() conn, err := net.Dial("tcp", serverAddr) if err != nil { return "", err } defer closer(conn) fmt.Fprint(conn, request) return bufio.NewReader(conn).ReadString('\n') } func TestWsfcRunAgentE2E(t *testing.T) { wsfcMgr := &wsfcManager{agentNewState: running, agentNewPort: wsfcDefaultAgentPort, agent: getWsfcAgentInstance()} wsfcMgr.set() // make sure the agent is cleaned up. defer wsfcMgr.agent.stop() addrs, err := net.InterfaceAddrs() if err != nil { t.Fatal("getting localing interface failed.") } // pick first local ip that is not lookback ip var existIP string for _, addr := range addrs { if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { existIP = ipnet.IP.To4().String() break } } // test with existing IP if got, err := getHealthCheckResponce(existIP, wsfcMgr.agent); got != "1" { t.Errorf("health check failed with %v, got = %v, want %v", existIP, got, "1") if err != nil { t.Error(err) } } // test an invalid ip which could not exist invalidIP := "255.255.255.256" if got, err := getHealthCheckResponce(invalidIP, wsfcMgr.agent); got != "0" { t.Errorf("health check failed with %v, got = %v, want %v", invalidIP, got, "0") if err != nil { t.Error(err) } } // test stop agent wsfcMgrStop := &wsfcManager{agentNewState: stopped, agent: getWsfcAgentInstance()} wsfcMgrStop.set() if _, err := getHealthCheckResponce(existIP, wsfcMgr.agent); err == nil { t.Errorf("health check still running after calling stop") } } func TestInvokeRunOnRunningWsfcAgent(t *testing.T) { agent := &wsfcAgent{listener: testListener} if err := agent.run(); err != nil { t.Errorf("Invoke run on running agent, error = %v, want = %v", err, nil) } } func TestInvokeStopOnStoppedWsfcAgent(t *testing.T) { agent := &wsfcAgent{listener: nil} if err := agent.stop(); err != nil { t.Errorf("Invoke stop on stopped agent, error = %v, want = %v", err, nil) } } func TestWsfcAgentSetPort(t *testing.T) { want := "2" agent := &wsfcAgent{port: "1"} agent.setPort(want) if agent.port != want { t.Errorf("WsfcAgent.setPort() port = %v, want %v", agent.port, want) } } func TestGetWsfcAgentInstance(t *testing.T) { agentFirst := getWsfcAgentInstance() agentSecond := getWsfcAgentInstance() if agentFirst != agentSecond { t.Errorf("getWsfcAgentInstance is not returning same instance") } } guest-agent-20220622.00/google_metadata_script_runner/000077500000000000000000000000001425470135300225115ustar00rootroot00000000000000guest-agent-20220622.00/google_metadata_script_runner/main.go000066400000000000000000000331311425470135300237650ustar00rootroot00000000000000// 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. // GCEMetadataScripts handles the running of metadata scripts on Google Compute // Engine instances. package main // TODO: compare log outputs in this utility to linux. // TODO: standardize and consolidate retries. import ( "bufio" "context" "encoding/json" "fmt" "io" "io/ioutil" "net" "net/http" "net/url" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "time" "cloud.google.com/go/storage" "github.com/GoogleCloudPlatform/guest-agent/utils" "github.com/GoogleCloudPlatform/guest-logging-go/logger" "github.com/go-ini/ini" ) var ( programName = "GCEMetadataScripts" version = "dev" metadataURL = "http://169.254.169.254/computeMetadata/v1" metadataHang = "/?recursive=true&alt=json&timeout_sec=10&last_etag=NONE" defaultTimeout = 20 * time.Second powerShellArgs = []string{"-NoProfile", "-NoLogo", "-ExecutionPolicy", "Unrestricted", "-File"} errUsage = fmt.Errorf("No valid arguments specified. Specify one of \"startup\", \"shutdown\" or \"specialize\"") config *ini.File storageURL = "storage.googleapis.com" bucket = `([a-z0-9][-_.a-z0-9]*)` object = `(.+)` // Many of the Google Storage URLs are supported below. // It is preferred that customers specify their object using // its gs:/// URL. bucketRegex = regexp.MustCompile(fmt.Sprintf(`^gs://%s/?$`, bucket)) gsRegex = regexp.MustCompile(fmt.Sprintf(`^gs://%s/%s$`, bucket, object)) // Check for the Google Storage URLs: // http://.storage.googleapis.com/ // https://.storage.googleapis.com/ gsHTTPRegex1 = regexp.MustCompile(fmt.Sprintf(`^http[s]?://%s\.storage\.googleapis\.com/%s$`, bucket, object)) // http://storage.cloud.google.com// // https://storage.cloud.google.com// gsHTTPRegex2 = regexp.MustCompile(fmt.Sprintf(`^http[s]?://storage\.cloud\.google\.com/%s/%s$`, bucket, object)) // Check for the other possible Google Storage URLs: // http://storage.googleapis.com// // https://storage.googleapis.com// // // The following are deprecated but also checked: // http://commondatastorage.googleapis.com// // https://commondatastorage.googleapis.com// gsHTTPRegex3 = regexp.MustCompile(fmt.Sprintf(`^http[s]?://(?:commondata)?storage\.googleapis\.com/%s/%s$`, bucket, object)) testStorageClient *storage.Client ) const ( winConfigPath = `C:\Program Files\Google\Compute Engine\instance_configs.cfg` configPath = `/etc/default/instance_configs.cfg` ) func newStorageClient(ctx context.Context) (*storage.Client, error) { if testStorageClient != nil { return testStorageClient, nil } return storage.NewClient(ctx) } func downloadGSURL(ctx context.Context, bucket, object string, file *os.File) error { client, err := newStorageClient(ctx) if err != nil { return fmt.Errorf("failed to create storage client: %v", err) } defer client.Close() r, err := client.Bucket(bucket).Object(object).NewReader(ctx) if err != nil { return fmt.Errorf("error reading object %q: %v", object, err) } defer r.Close() _, err = io.Copy(file, r) return err } func downloadURL(url string, file *os.File) error { // Retry up to 3 times, only wait 1 second between retries. var res *http.Response var err error for i := 1; ; i++ { res, err = http.Get(url) if err != nil && i > 3 { return err } if err == nil { break } time.Sleep(1 * time.Second) } defer res.Body.Close() if res.StatusCode != http.StatusOK { return fmt.Errorf("GET %q, bad status: %s", url, res.Status) } _, err = io.Copy(file, res.Body) return err } func downloadScript(ctx context.Context, path string, file *os.File) error { // Startup scripts may run before DNS is running on some systems, // particularly once a system is promoted to a domain controller. // Try to lookup storage.googleapis.com and sleep for up to 100s if // we get an error. // TODO: do we need to do this on every script? for i := 0; i < 20; i++ { if _, err := net.LookupHost(storageURL); err == nil { break } time.Sleep(5 * time.Second) } bucket, object := parseGCS(path) if bucket != "" && object != "" { // TODO: why is this retry outer, but downloadURL retry is inner? // Retry up to 3 times, only wait 1 second between retries. for i := 1; ; i++ { err := downloadGSURL(ctx, bucket, object, file) if err == nil { return nil } if err != nil && i > 3 { logger.Infof("Failed to download GCS path: %v", err) break } time.Sleep(1 * time.Second) } logger.Infof("Trying unauthenticated download") path = fmt.Sprintf("https://%s/%s/%s", storageURL, bucket, object) } // Fall back to an HTTP GET of the URL. return downloadURL(path, file) } func parseGCS(path string) (string, string) { for _, re := range []*regexp.Regexp{gsRegex, gsHTTPRegex1, gsHTTPRegex2, gsHTTPRegex3} { match := re.FindStringSubmatch(path) if len(match) == 3 { return match[1], match[2] } } return "", "" } func getMetadataKey(key string) (string, error) { md, err := getMetadata(key, false) if err != nil { return "", err } return string(md), nil } func getMetadataAttributes(key string) (map[string]string, error) { md, err := getMetadata(key, true) if err != nil { return nil, err } var att map[string]string return att, json.Unmarshal(md, &att) } func getMetadata(key string, recurse bool) ([]byte, error) { client := &http.Client{ Timeout: defaultTimeout, } url := metadataURL + key if recurse { url += metadataHang } req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Add("Metadata-Flavor", "Google") var res *http.Response // Retry forever, increase sleep between retries (up to 5 times) in order // to wait for slow network initialization. var rt time.Duration for i := 1; ; i++ { res, err = client.Do(req) if err == nil { break } if i < 6 { rt = time.Duration(3*i) * time.Second } logger.Errorf("error connecting to metadata server, retrying in %s, error: %v", rt, err) time.Sleep(rt) } defer res.Body.Close() md, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } return md, nil } func normalizeFilePathForWindows(filePath string, metadataKey string, gcsScriptURL *url.URL) string { // If either the metadataKey ends in one of these extensions OR if this is a url startup script and if the // url path ends in one of these extensions, append the extension to the filePath name so that Windows can recognize it. for _, ext := range []string{"bat", "cmd", "ps1", "exe"} { if strings.HasSuffix(metadataKey, "-"+ext) || (gcsScriptURL != nil && strings.HasSuffix(gcsScriptURL.Path, "."+ext)) { filePath = fmt.Sprintf("%s.%s", filePath, ext) break } } return filePath } func writeScriptToFile(ctx context.Context, value string, filePath string, gcsScriptURL *url.URL) error { // Create or download files. if gcsScriptURL != nil { file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) if err != nil { return fmt.Errorf("error opening temp file: %v", err) } if err := downloadScript(ctx, value, file); err != nil { file.Close() return err } if err := file.Close(); err != nil { return fmt.Errorf("error closing temp file: %v", err) } } else { // Trim leading spaces and newlines. value = strings.TrimLeft(value, " \n\v\f\t\r") if err := ioutil.WriteFile(filePath, []byte(value), 0755); err != nil { return fmt.Errorf("error writing temp file: %v", err) } } return nil } func setupAndRunScript(ctx context.Context, metadataKey string, value string) error { // Make sure that the URL is valid for URL startup scripts var gcsScriptURL *url.URL if strings.HasSuffix(metadataKey, "-url") { var err error gcsScriptURL, err = url.Parse(strings.TrimSpace(value)) if err != nil { return err } } // Make temp directory. tmpDir, err := ioutil.TempDir(config.Section("MetadataScripts").Key("run_dir").String(), "metadata-scripts") if err != nil { return err } defer os.RemoveAll(tmpDir) tmpFile := filepath.Join(tmpDir, metadataKey) if runtime.GOOS == "windows" { tmpFile = normalizeFilePathForWindows(tmpFile, metadataKey, gcsScriptURL) } writeScriptToFile(ctx, value, tmpFile, gcsScriptURL) return runScript(tmpFile, metadataKey) } // Craft the command to run. func runScript(filePath string, metadataKey string) error { var cmd *exec.Cmd if strings.HasSuffix(filePath, ".ps1") { cmd = exec.Command("powershell.exe", append(powerShellArgs, filePath)...) } else { if runtime.GOOS == "windows" { cmd = exec.Command(filePath) } else { cmd = exec.Command(config.Section("MetadataScripts").Key("default_shell").MustString("/bin/bash"), "-c", filePath) } } return runCmd(cmd, metadataKey) } func runCmd(c *exec.Cmd, name string) error { pr, pw, err := os.Pipe() if err != nil { return err } defer pr.Close() c.Stdout = pw c.Stderr = pw if err := c.Start(); err != nil { return err } pw.Close() in := bufio.NewScanner(pr) for { if !in.Scan() { if err := in.Err(); err != nil { logger.Errorf("error while communicating with %q script: %v", name, err) } break } logger.Log(logger.LogEntry{ Message: fmt.Sprintf("%s: %s", name, in.Text()), CallDepth: 3, Severity: logger.Info, }) } pr.Close() return c.Wait() } // getWantedKeys returns the list of keys to check for a given type of script and OS. func getWantedKeys(args []string, os string) ([]string, error) { if len(args) != 2 { return nil, errUsage } prefix := args[1] switch prefix { case "specialize": prefix = "sysprep-specialize" case "startup", "shutdown": if os == "windows" { prefix = "windows-" + prefix } if !config.Section("MetadataScripts").Key(prefix).MustBool(true) { return nil, fmt.Errorf("%s scripts disabled in instance config", prefix) } default: return nil, errUsage } var mdkeys []string var suffixes []string if os == "windows" { suffixes = []string{"ps1", "cmd", "bat", "url"} } else { suffixes = []string{"url"} // The 'bare' startup-script or shutdown-script key, not supported on Windows. mdkeys = append(mdkeys, fmt.Sprintf("%s-script", prefix)) } for _, suffix := range suffixes { mdkeys = append(mdkeys, fmt.Sprintf("%s-script-%s", prefix, suffix)) } return mdkeys, nil } func parseMetadata(md map[string]string, wanted []string) map[string]string { found := make(map[string]string) for _, key := range wanted { val, ok := md[key] if !ok || val == "" { continue } found[key] = val } return found } // getExistingKeys returns the wanted keys that are set in metadata. func getExistingKeys(wanted []string) (map[string]string, error) { for _, attrs := range []string{"/instance/attributes", "/project/attributes"} { md, err := getMetadataAttributes(attrs) if err != nil { return nil, err } if found := parseMetadata(md, wanted); len(found) != 0 { return found, nil } } return nil, nil } func logFormatWindows(e logger.LogEntry) string { now := time.Now().Format("2006/01/02 15:04:05") // 2006/01/02 15:04:05 GCEMetadataScripts This is a log message. return fmt.Sprintf("%s %s: %s", now, programName, e.Message) } func parseConfig(file string) (*ini.File, error) { // Priority: file.cfg, file.cfg.distro, file.cfg.template cfg, err := ini.LoadSources(ini.LoadOptions{Loose: true, Insensitive: true}, file, file+".distro", file+".template") if err != nil { return nil, err } return cfg, nil } func main() { ctx := context.Background() opts := logger.LogOpts{LoggerName: programName} cfgfile := configPath if runtime.GOOS == "windows" { cfgfile = winConfigPath opts.Writers = []io.Writer{&utils.SerialPort{Port: "COM1"}, os.Stdout} opts.FormatFunction = logFormatWindows } else { opts.Writers = []io.Writer{os.Stdout} opts.FormatFunction = func(e logger.LogEntry) string { return e.Message } // Local logging is syslog; we will just use stdout in Linux. opts.DisableLocalLogging = true } var err error config, err = parseConfig(cfgfile) if err != nil && !os.IsNotExist(err) { fmt.Printf("Error parsing instance config %s: %s\n", cfgfile, err.Error()) } // The keys to check vary based on the argument and the OS. Also functions to validate arguments. wantedKeys, err := getWantedKeys(os.Args, runtime.GOOS) if err != nil { fmt.Printf("%s\n", err.Error()) os.Exit(2) } projectID, err := getMetadataKey("/project/project-id") if err == nil { opts.ProjectName = projectID } logger.Init(ctx, opts) logger.Infof("Starting %s scripts (version %s).", os.Args[1], version) scripts, err := getExistingKeys(wantedKeys) if err != nil { logger.Fatalf(err.Error()) } if len(scripts) == 0 { logger.Infof("No %s scripts to run.", os.Args[1]) return } for _, wantedKey := range wantedKeys { value, ok := scripts[wantedKey] if !ok { continue } logger.Infof("Found %s in metadata.", wantedKey) if err := setupAndRunScript(ctx, wantedKey, value); err != nil { logger.Infof("%s %s", wantedKey, err) continue } logger.Infof("%s exit status 0", wantedKey) } logger.Infof("Finished running %s scripts.", os.Args[1]) } guest-agent-20220622.00/google_metadata_script_runner/main_test.go000066400000000000000000000145221425470135300250270ustar00rootroot00000000000000// 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. package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "os" "reflect" "testing" ) func TestMain(m *testing.M) { config, _ = parseConfig("") os.Exit(m.Run()) } func TestGetWantedArgs(t *testing.T) { getWantedTests := []struct { arg string os string want []string }{ { "specialize", "windows", []string{ "sysprep-specialize-script-ps1", "sysprep-specialize-script-cmd", "sysprep-specialize-script-bat", "sysprep-specialize-script-url", }, }, { "startup", "windows", []string{ "windows-startup-script-ps1", "windows-startup-script-cmd", "windows-startup-script-bat", "windows-startup-script-url", }, }, { "shutdown", "windows", []string{ "windows-shutdown-script-ps1", "windows-shutdown-script-cmd", "windows-shutdown-script-bat", "windows-shutdown-script-url", }, }, { "startup", "linux", []string{ "startup-script", "startup-script-url", }, }, { "shutdown", "linux", []string{ "shutdown-script", "shutdown-script-url", }, }, } for _, tt := range getWantedTests { got, err := getWantedKeys([]string{"", tt.arg}, tt.os) if err != nil { t.Fatalf("validateArgs returned error: %v", err) } if !reflect.DeepEqual(got, tt.want) { t.Errorf("returned slice does not match expected one: got %v, want %v", got, tt.want) } _, err = getWantedKeys([]string{""}, "") if err == nil { t.Errorf("0 args should produce an error") } _, err = getWantedKeys([]string{"", "", ""}, "") if err == nil { t.Errorf("3 args should produce an error") } } } func TestGetExistingKeys(t *testing.T) { wantedKeys := []string{ "sysprep-specialize-script-cmd", "sysprep-specialize-script-ps1", "sysprep-specialize-script-bat", "sysprep-specialize-script-url", } md := map[string]string{ "sysprep-specialize-script-cmd": "cmd", "startup-script-cmd": "cmd", "shutdown-script-ps1": "ps1", "sysprep-specialize-script-url": "url", "sysprep-specialize-script-ps1": "ps1", "key": "value", "sysprep-specialize-script-bat": "bat", } want := map[string]string{ "sysprep-specialize-script-ps1": "ps1", "sysprep-specialize-script-cmd": "cmd", "sysprep-specialize-script-bat": "bat", "sysprep-specialize-script-url": "url", } got := parseMetadata(md, wantedKeys) if !reflect.DeepEqual(got, want) { t.Errorf("parsed metadata does not match expectation, got: %v, want: %v", got, want) } } func TestParseGCS(t *testing.T) { matchTests := []struct { path, bucket, object string }{ {"gs://bucket/object", "bucket", "object"}, {"gs://bucket/some/object", "bucket", "some/object"}, {"http://bucket.storage.googleapis.com/object", "bucket", "object"}, {"https://bucket.storage.googleapis.com/object", "bucket", "object"}, {"https://bucket.storage.googleapis.com/some/object", "bucket", "some/object"}, {"http://storage.googleapis.com/bucket/object", "bucket", "object"}, {"http://commondatastorage.googleapis.com/bucket/object", "bucket", "object"}, {"https://storage.googleapis.com/bucket/object", "bucket", "object"}, {"https://commondatastorage.googleapis.com/bucket/object", "bucket", "object"}, {"https://storage.googleapis.com/bucket/some/object", "bucket", "some/object"}, {"https://commondatastorage.googleapis.com/bucket/some/object", "bucket", "some/object"}, } for _, tt := range matchTests { bucket, object := parseGCS(tt.path) if bucket != tt.bucket { t.Errorf("returned bucket does not match expected one for %q:\n got %q, want: %q", tt.path, bucket, tt.bucket) } if object != tt.object { t.Errorf("returned object does not match expected one for %q\n: got %q, want: %q", tt.path, object, tt.object) } } } func TestGetMetadata(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"key1":"value1","key2":"value2"}`) })) defer ts.Close() metadataURL = ts.URL metadataHang = "" want := map[string]string{"key1": "value1", "key2": "value2"} got, err := getMetadataAttributes("") if err != nil { t.Fatalf("error running getMetadataAttributes: %v", err) } if !reflect.DeepEqual(got, want) { t.Errorf("metadata does not match expectation, got: %q, want: %q", got, want) } } func TestNormalizeFilePathForWindows(t *testing.T) { tmpFilePath := "C:/Temp/file" testCases := []struct { metadataKey string gcsScriptURLPath string want string }{ { metadataKey: "windows-startup-script-url", gcsScriptURLPath: "gs://gcs-bucket/binary.exe", want: "C:/Temp/file.exe", }, { metadataKey: "windows-startup-script-url", gcsScriptURLPath: "gs://gcs-bucket/binary", want: "C:/Temp/file", }, { metadataKey: "windows-startup-script-ps1", gcsScriptURLPath: "gs://gcs-bucket/binary.ps1", want: "C:/Temp/file.ps1", }, { metadataKey: "windows-startup-script-ps1", gcsScriptURLPath: "gs://gcs-bucket/binary", want: "C:/Temp/file.ps1", }, { metadataKey: "windows-startup-script-bat", gcsScriptURLPath: "gs://gcs-bucket/binary.bat", want: "C:/Temp/file.bat", }, { metadataKey: "windows-startup-script-cmd", gcsScriptURLPath: "gs://gcs-bucket/binary.cmd", want: "C:/Temp/file.cmd", }, } for _, tc := range testCases { url := url.URL{ Path: tc.gcsScriptURLPath, } got := normalizeFilePathForWindows(tmpFilePath, tc.metadataKey, &url) if got != tc.want { t.Errorf("Return didn't match expected output for inputs:\n fileName: %s, metadataKey: %s, gcsScriptUrl: %s\n Expected: %s\n Got: %s", tmpFilePath, tc.metadataKey, tc.gcsScriptURLPath, tc.want, got) } } } guest-agent-20220622.00/instance_configs.cfg000066400000000000000000000014661425470135300204240ustar00rootroot00000000000000[Accounts] deprovision_remove = false gpasswd_add_cmd = gpasswd -a {user} {group} gpasswd_remove_cmd = gpasswd -d {user} {group} groupadd_cmd = groupadd {group} groups = adm,dip,docker,lxd,plugdev,video useradd_cmd = useradd -m -s /bin/bash -p * {user} userdel_cmd = userdel -r {user} [Daemons] accounts_daemon = true clock_skew_daemon = true network_daemon = true [InstanceSetup] host_key_types = ecdsa,ed25519,rsa network_enabled = true optimize_local_ssd = true set_boto_config = true set_host_keys = true set_multiqueue = true [IpForwarding] ethernet_proto_id = 66 ip_aliases = true target_instance_ips = true [MetadataScripts] default_shell = /bin/bash run_dir = shutdown = true startup = true [NetworkInterfaces] dhclient_script = /sbin/google-dhclient-script dhcp_command = ip_forwarding = true setup = true guest-agent-20220622.00/packaging/000077500000000000000000000000001425470135300163445ustar00rootroot00000000000000guest-agent-20220622.00/packaging/debian/000077500000000000000000000000001425470135300175665ustar00rootroot00000000000000guest-agent-20220622.00/packaging/debian/changelog000066400000000000000000000002701425470135300214370ustar00rootroot00000000000000google-guest-agent (1:20191204.00-g1) stable; urgency=medium * Extend windows agent for multi platform. -- Google Cloud Team Wed, 04 Dec 2019 12:00:00 +0000 guest-agent-20220622.00/packaging/debian/compat000066400000000000000000000000031425470135300207650ustar00rootroot0000000000000010 guest-agent-20220622.00/packaging/debian/control000066400000000000000000000007151425470135300211740ustar00rootroot00000000000000Source: google-guest-agent Maintainer: Google Cloud Team Section: misc Priority: optional Standards-Version: 3.9.8 Build-Depends: debhelper (>= 9.20160709), dh-golang (>= 1.1), golang-go Package: google-guest-agent Architecture: any Depends: ${misc:Depends} Conflicts: python-google-compute-engine, python3-google-compute-engine Description: Google Compute Engine Guest Agent Contains the guest agent and metadata script runner binaries. guest-agent-20220622.00/packaging/debian/copyright000066400000000000000000000017261425470135300215270ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: google-compute-engine Upstream-Contact: gc-team@google.com Files: * Copyright: Copyright 2017 Google Inc. License: Apache-2.0 Files: debian/* Copyright: Copyright 2017 Google Inc. License: Apache-2.0 License: Apache-2.0 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. . On Debian systems, the complete text of the Apache version 2.0 license can be found in "/usr/share/common-licenses/Apache-2.0". guest-agent-20220622.00/packaging/debian/google-guest-agent.docs000066400000000000000000000000261425470135300241330ustar00rootroot00000000000000THIRD_PARTY_LICENSES/ guest-agent-20220622.00/packaging/debian/postinst000066400000000000000000000003151425470135300213730ustar00rootroot00000000000000#!/bin/sh -e if [ "$1" = "configure" ] ; then if [ ! -f /etc/default/instance_configs.cfg ]; then cp -a "/usr/share/${DPKG_MAINTSCRIPT_PACKAGE}/instance_configs.cfg" /etc/default/ fi fi #DEBHELPER# guest-agent-20220622.00/packaging/debian/postrm000066400000000000000000000002321425470135300210320ustar00rootroot00000000000000#!/bin/sh -e if [ "$1" = "purge" ] ; then if [ -f /etc/default/instance_configs.cfg ]; then rm /etc/default/instance_configs.cfg fi fi #DEBHELPER# guest-agent-20220622.00/packaging/debian/rules000077500000000000000000000031631425470135300206510ustar00rootroot00000000000000#!/usr/bin/make -f export PATH := /tmp/go/bin:$(PATH) export SHELL := env PATH=$(PATH) /bin/bash export DH_OPTIONS export DH_GOPKG := github.com/GoogleCloudPlatform/guest-agent export CGO_ENABLED := 0 export GOCACHE := /tmp/.cache export GOPROXY := https://proxy.golang.org export GO111MODULE := on export GOPATH := /usr/share/gocode export DH_GOLANG_BUILDPKG := github.com/GoogleCloudPlatform/guest-agent/google_guest_agent github.com/GoogleCloudPlatform/guest-agent/google_metadata_script_runner %: dh $@ --buildsystem=golang --with=golang,systemd override_dh_auto_install: # Binary-only package. dh_auto_install -- --no-source install -d debian/google-guest-agent/usr/share/google-guest-agent install -p -m 0644 instance_configs.cfg debian/google-guest-agent/usr/share/google-guest-agent override_dh_golang: # We don't use any packaged dependencies, so skip dh_golang step. override_dh_auto_build: dh_auto_build -O--buildsystem=golang -- -ldflags="-s -w -X main.version=$(VERSION)" -mod=readonly override_dh_installinit: # We don't ship sysvinit files or need script changes for them. override_dh_systemd_enable: install -d debian/google-guest-agent/lib/systemd/system install -p -m 0644 *.service debian/google-guest-agent/lib/systemd/system/ install -d debian/google-guest-agent/lib/systemd/system-preset install -p -m 0644 *.preset debian/google-guest-agent/lib/systemd/system-preset/ dh_systemd_enable google-guest-agent.service google-startup-scripts.service google-shutdown-scripts.service override_dh_systemd_start: # Only perform start/stop actions for the guest agent. dh_systemd_start google-guest-agent.service guest-agent-20220622.00/packaging/debian/source/000077500000000000000000000000001425470135300210665ustar00rootroot00000000000000guest-agent-20220622.00/packaging/debian/source/format000066400000000000000000000000141425470135300222740ustar00rootroot000000000000003.0 (quilt) guest-agent-20220622.00/packaging/googet/000077500000000000000000000000001425470135300176305ustar00rootroot00000000000000guest-agent-20220622.00/packaging/googet/agent_install.ps1000066400000000000000000000043261425470135300231060ustar00rootroot00000000000000# 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. $name = 'GCEAgent' $path = '"C:\Program Files\Google\Compute Engine\agent\GCEWindowsAgent.exe"' $display_name = 'Google Compute Engine Agent' $description = 'Google Compute Engine Agent' $initial_config = @' # GCE Instance Configuration # For details on what can be configured, see: # https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#configure-windows-features # [accountManager] # disable=false # [addressManager] # disable=false '@ function Set-ServiceConfig { # Restart service after 1s, then 2s. Reset error counter after 60s. sc.exe failure $name reset= 60 actions= restart/1000/restart/2000 # Set dependency and delayed start cmd.exe /c "sc.exe config ${name} depend= `"samss`" start= delayed-auto binpath= \`"${path}\`"" # Create trigger to start the service on first IP address sc.exe triggerinfo $name start/networkon } try { if (-not (Get-Service $name -ErrorAction SilentlyContinue)) { New-Service -Name $name ` -DisplayName $display_name ` -BinaryPathName $path ` -StartupType Automatic ` -Description $description } else { Set-Service -Name $name ` -DisplayName $display_name ` -Description $description } Set-ServiceConfig $config = "${env:ProgramFiles}\Google\Compute Engine\instance_configs.cfg" if (-not (Test-Path $config)) { $initial_config | Set-Content -Path $config -Encoding ASCII } Restart-Service $name -Verbose } catch { Write-Output $_.InvocationInfo.PositionMessage Write-Output "Install failed: $($_.Exception.Message)" exit 1 } guest-agent-20220622.00/packaging/googet/agent_uninstall.ps1000066400000000000000000000012271425470135300234460ustar00rootroot00000000000000# 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. Stop-Service GCEAgent -Verbose & sc.exe delete GCEAgent guest-agent-20220622.00/packaging/googet/google-compute-engine-metadata-scripts.goospec000066400000000000000000000042601425470135300306470ustar00rootroot00000000000000{ "name": "google-compute-engine-metadata-scripts", "version": "{{.version}}.0@1", "arch": "x86_64", "authors": "Google Inc.", "license": "http://www.apache.org/licenses/LICENSE-2.0", "description": "Google Compute Engine metadata scripts", "source": "https://github.com/GoogleCloudPlatform/guest-agent/", "files": { "google_metadata_script_runner/GCEMetadataScripts.exe": "/Google/Compute Engine/metadata_scripts/GCEMetadataScripts.exe", "packaging/googet/run_shutdown_scripts.cmd": "/Google/Compute Engine/metadata_scripts/run_shutdown_scripts.cmd", "packaging/googet/run_startup_scripts.cmd": "/Google/Compute Engine/metadata_scripts/run_startup_scripts.cmd", "THIRD_PARTY_LICENSES": "/Google/Compute Engine/THIRD_PARTY_LICENSES/", "LICENSE": "/Google/Compute Engine/metadata_scripts/LICENSE.txt" }, "install": { "path": "packaging/googet/metadata_scripts_install.ps1" }, "uninstall": { "path": "packaging/googet/metadata_scripts_uninstall.ps1" }, "releaseNotes": [ "4.3.0 - Use guest logging library.", "4.2.0 - Retry forever if metadata server is unavailable.", " - Honor project level metadata scripts.", "4.1.6 - Add additional retry logic to account for slow network startup and transient GCS errors.", "4.1.3 - Add a sleep in case DNS is not started at boot.", "4.1.0 - Add startup and shutdown settings.", "4.0.0 - Rewrite GCEMetadataScripts in Go.", "3.5.0 - Separate metadata-scripts into its own package." ], "sources": [{ "include": [ "google_metadata_script_runner/GCEMetadataScripts.exe", "packaging/googet/run_shutdown_scripts.cmd", "packaging/googet/run_startup_scripts.cmd", "packaging/googet/metadata_scripts_install.ps1", "packaging/googet/metadata_scripts_uninstall.ps1", "THIRD_PARTY_LICENSES/**", "LICENSE" ] }], "build": { "linux": "/bin/bash", "linuxArgs": ["-c", "GOOS=windows /tmp/go/bin/go build -ldflags '-X main.version={{.version}}' -mod=readonly -o ./google_metadata_script_runner/GCEMetadataScripts.exe ./google_metadata_script_runner"] } } guest-agent-20220622.00/packaging/googet/google-compute-engine-windows.goospec000066400000000000000000000040611425470135300270730ustar00rootroot00000000000000{ "name": "google-compute-engine-windows", "version": "{{.version}}.0@1", "arch": "x86_64", "authors": "Google Inc.", "license": "http://www.apache.org/licenses/LICENSE-2.0", "description": "Google Compute Engine Windows agent", "source": "https://github.com/GoogleCloudPlatform/guest-agent/", "files": { "GCEWindowsAgent.exe": "/Google/Compute Engine/agent/GCEWindowsAgent.exe", "GCEAuthorizedKeysCommand.exe": "/Google/Compute Engine/agent/GCEAuthorizedKeysCommand.exe", "THIRD_PARTY_LICENSES": "/Google/Compute Engine/THIRD_PARTY_LICENSES/", "LICENSE": "/Google/Compute Engine/agent/LICENSE.txt" }, "install": { "path": "packaging/googet/agent_install.ps1" }, "uninstall": { "path": "packaging/googet/agent_uninstall.ps1" }, "releaseNotes": [ "4.7.0 - Use guest logging library.", "4.6.0 - Support for internal IP address load balancing.", "4.5.2 - When resetting a users password only lookup the user on the local system", "4.5.0 - Allow specfiying hash function for password reset, sha1, sha256, or sha512", "4.4.0 - Run updates on every metadata return, including timeout", " - Add individual manager timeout to address manager", "4.3.0 - Add diagnostics support for optional log extraction", "4.2.3 - Don't error on lack of config file, only run update on updated metadata", " - Make DNS and Network errors more user friendly", "4.2.0 - Use *UnicastIpAddressEntry functions for managing forwarded IPs", "4.1.0 - Add configuration file", "4.0.0 - Rewrite GCEAgent in Go", "3.5.0 - Pull the Windows agent out into its own package" ], "sources": [{ "include": [ "GCEWindowsAgent.exe", "GCEAuthorizedKeysCommand.exe", "packaging/googet/agent_install.ps1", "packaging/googet/agent_uninstall.ps1", "THIRD_PARTY_LICENSES/**", "LICENSE" ] }], "build": { "linux": "/bin/bash", "linuxArgs": ["-c", "./packaging/googet/windows_agent_build.sh {{.version}}"] } } guest-agent-20220622.00/packaging/googet/metadata_scripts_install.ps1000066400000000000000000000040721425470135300253350ustar00rootroot00000000000000# 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. $install_dir = "${env:ProgramFiles}\Google\Compute Engine\metadata_scripts" $machine_env = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' $path = (Get-ItemProperty $machine_env).Path if ($path -notlike "*${install_dir}*") { Set-ItemProperty $machine_env -Name 'Path' -Value ($path + ";${install_dir}") } $run_startup_scripts = "${install_dir}\run_startup_scripts.cmd" $service = New-Object -ComObject("Schedule.Service") $service.Connect() $task = $service.NewTask(0) $task.Settings.Enabled = $true $task.Settings.AllowDemandStart = $true $task.Settings.Priority = 5 $action = $task.Actions.Create(0) $action.Path = "`"$run_startup_scripts`"" $trigger = $task.Triggers.Create(8) $folder = $service.GetFolder('\') $folder.RegisterTaskDefinition('GCEStartup',$task,6,'System',$null,5) | Out-Null $gpt_ini = "${env:SystemRoot}\System32\GroupPolicy\gpt.ini" $scripts_ini = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\scripts.ini" if ((Test-Path $gpt_ini) -or (Test-Path $scripts_ini)) { return } New-Item -Type Directory -Path "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts" -ErrorAction SilentlyContinue @' [General] gPCMachineExtensionNames= [{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}] Version=1 '@ | Set-Content -Path $gpt_ini -Encoding ASCII @' [Shutdown] 0CmdLine=C:\Program Files\Google\Compute Engine\metadata_scripts\run_shutdown_scripts.cmd 0Parameters= '@ | Set-Content -Path $scripts_ini -Encoding ASCII guest-agent-20220622.00/packaging/googet/metadata_scripts_uninstall.ps1000066400000000000000000000017221425470135300256770ustar00rootroot00000000000000# 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. $install_dir = "${env:ProgramFiles}\Google\Compute Engine\metadata_scripts" $machine_env = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' $path = (Get-ItemProperty $machine_env).Path if ($path -like "*${install_dir}*") { Set-ItemProperty $machine_env -Name 'Path' -Value $path.Replace(";$install_dir", '') } & schtasks /delete /tn GCEStartup /f guest-agent-20220622.00/packaging/googet/run_shutdown_scripts.cmd000066400000000000000000000014631425470135300246270ustar00rootroot00000000000000@echo off REM Copyright 2015 Google Inc. All Rights Reserved. REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM Run shutdown scripts that should happen as soon as the instance REM begins to power down "C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe" "shutdown" guest-agent-20220622.00/packaging/googet/run_startup_scripts.cmd000066400000000000000000000017311425470135300244540ustar00rootroot00000000000000@echo off REM Copyright 2015 Google Inc. All Rights Reserved. REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM Run startup scripts that should happen late at boot. REM A scheduled task may only run for up to three days before termination. REM We execute the startup script asynchronously so it may run without REM this three day maximum runtime limitation. start "" "C:\Program Files\Google\Compute Engine\metadata_scripts\GCEMetadataScripts.exe" "startup" guest-agent-20220622.00/packaging/googet/windows_agent_build.sh000077500000000000000000000006301425470135300242150ustar00rootroot00000000000000#!/bin/bash # Should be run from the guest-agent directory. # Usage: # ./packaging/googet/windows_agent_build.sh version=$1 GOOS=windows /tmp/go/bin/go build -ldflags "-X main.version=$version" -mod=readonly -o GCEWindowsAgent.exe ./google_guest_agent GOOS=windows /tmp/go/bin/go build -ldflags "-X main.version=$version" -mod=readonly -o GCEAuthorizedKeysCommand.exe ./google_authorized_keys guest-agent-20220622.00/packaging/google-guest-agent.spec000066400000000000000000000120431425470135300227150ustar00rootroot00000000000000# Copyright 2018 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. # Don't build debuginfo packages. %define debug_package %{nil} Name: google-guest-agent Epoch: 1 Version: %{_version} Release: g1%{?dist} Summary: Google Compute Engine guest agent. License: ASL 2.0 Url: https://cloud.google.com/compute/docs/images/guest-environment Source0: %{name}_%{version}.orig.tar.gz BuildArch: %{_arch} %if ! 0%{?el6} BuildRequires: systemd %endif Obsoletes: python-google-compute-engine, python3-google-compute-engine %description Contains the Google guest agent binary. %prep %autosetup %build for bin in google_guest_agent google_metadata_script_runner; do pushd "$bin" GOPATH=%{_gopath} CGO_ENABLED=0 %{_go} build -ldflags="-s -w -X main.version=%{_version}" -mod=readonly popd done %install install -d "%{buildroot}/%{_docdir}/%{name}" cp -r THIRD_PARTY_LICENSES "%buildroot/%_docdir/%name/THIRD_PARTY_LICENSES" install -d %{buildroot}%{_bindir} install -p -m 0755 google_guest_agent/google_guest_agent %{buildroot}%{_bindir}/google_guest_agent install -p -m 0755 google_metadata_script_runner/google_metadata_script_runner %{buildroot}%{_bindir}/google_metadata_script_runner install -d %{buildroot}/usr/share/google-guest-agent install -p -m 0644 instance_configs.cfg %{buildroot}/usr/share/google-guest-agent/instance_configs.cfg %if 0%{?el6} install -d %{buildroot}/etc/init install -p -m 0644 %{name}.conf %{buildroot}/etc/init/ install -p -m 0644 google-startup-scripts.conf %{buildroot}/etc/init/ install -p -m 0644 google-shutdown-scripts.conf %{buildroot}/etc/init/ %else install -d %{buildroot}%{_unitdir} install -d %{buildroot}%{_presetdir} install -p -m 0644 %{name}.service %{buildroot}%{_unitdir} install -p -m 0644 google-startup-scripts.service %{buildroot}%{_unitdir} install -p -m 0644 google-shutdown-scripts.service %{buildroot}%{_unitdir} install -p -m 0644 90-%{name}.preset %{buildroot}%{_presetdir}/90-%{name}.preset %endif %files %{_docdir}/%{name} %defattr(-,root,root,-) /usr/share/google-guest-agent/instance_configs.cfg %{_bindir}/google_guest_agent %{_bindir}/google_metadata_script_runner %if 0%{?el6} /etc/init/%{name}.conf /etc/init/google-startup-scripts.conf /etc/init/google-shutdown-scripts.conf %else %{_unitdir}/%{name}.service %{_unitdir}/google-startup-scripts.service %{_unitdir}/google-shutdown-scripts.service %{_presetdir}/90-%{name}.preset %endif %if ! 0%{?el6} %post if [ $1 -eq 1 ]; then # Initial installation # Install instance configs if not already present. if [ ! -f /etc/default/instance_configs.cfg ]; then cp -a /usr/share/google-guest-agent/instance_configs.cfg /etc/default/ fi # Use enable instead of preset because preset is not supported in # chroots. systemctl enable google-guest-agent.service >/dev/null 2>&1 || : systemctl enable google-startup-scripts.service >/dev/null 2>&1 || : systemctl enable google-shutdown-scripts.service >/dev/null 2>&1 || : if [ -d /run/systemd/system ]; then systemctl daemon-reload >/dev/null 2>&1 || : systemctl start google-guest-agent.service >/dev/null 2>&1 || : fi else # Package upgrade if [ -d /run/systemd/system ]; then systemctl try-restart google-guest-agent.service >/dev/null 2>&1 || : fi fi %preun if [ $1 -eq 0 ]; then # Package removal, not upgrade systemctl --no-reload disable google-guest-agent.service >/dev/null 2>&1 || : systemctl --no-reload disable google-startup-scripts.service >/dev/null 2>&1 || : systemctl --no-reload disable google-shutdown-scripts.service >/dev/null 2>&1 || : if [ -d /run/systemd/system ]; then systemctl stop google-guest-agent.service >/dev/null 2>&1 || : fi fi %postun if [ $1 -eq 0 ]; then # Package removal, not upgrade if [ -f /etc/default/instance_configs.cfg ]; then rm /etc/default/instance_configs.cfg fi if [ -d /run/systemd/system ]; then systemctl daemon-reload >/dev/null 2>&1 || : fi fi %else # EL6 %post if [ $1 -eq 1 ]; then # Install instance configs if not already present. if [ ! -f /etc/default/instance_configs.cfg ]; then cp -a /usr/share/google-guest-agent/instance_configs.cfg /etc/default/ fi # Initial installation initctl start google-guest-agent >/dev/null 2>&1 || : else # Upgrade initctl restart google-guest-agent >/dev/null 2>&1 || : fi %preun if [ $1 -eq 0 ]; then # Package removal, not upgrade initctl stop google-guest-agent >/dev/null 2>&1 || : fi %postun if [ $1 -eq 0 ]; then # Package removal, not upgrade if [ -f /etc/default/instance_configs.cfg ]; then rm /etc/default/instance_configs.cfg fi fi %endif guest-agent-20220622.00/utils/000077500000000000000000000000001425470135300155605ustar00rootroot00000000000000guest-agent-20220622.00/utils/main.go000066400000000000000000000054501425470135300170370ustar00rootroot00000000000000// Copyright 2022 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. // Utilities for Google Guest Agent and Google Authorized Keys package utils import ( "encoding/json" "errors" "strings" "time" "github.com/tarm/serial" ) //ContainsString checks for the presence of a string in a slice. func ContainsString(s string, ss []string) bool { for _, a := range ss { if a == s { return true } } return false } type sshKeyData struct { ExpireOn string UserName string } //CheckExpired takes a time string and determines if it represents a time in the past. func CheckExpired(expireOn string) (bool, error) { t, err := time.Parse(time.RFC3339, expireOn) if err != nil { t2, err2 := time.Parse("2006-01-02T15:04:05-0700", expireOn) if err2 != nil { return true, err //Return RFC3339 error } t = t2 } return t.Before(time.Now()), nil } //GetUserKey takes a string and determines if it is a valid SSH key and returns //the user and key if valid, nil otherwise. func GetUserKey(rawKey string) (string, string, error) { key := strings.Trim(rawKey, " ") if key == "" { return "", "", errors.New("Invalid ssh key entry - empty key") } idx := strings.Index(key, ":") if idx == -1 { return "", "", errors.New("Invalid ssh key entry - unrecognized format") } user := key[:idx] if user == "" { return "", "", errors.New("Invalid ssh key entry - user missing") } fields := strings.SplitN(key, " ", 4) if len(fields) == 3 && fields[2] == "google-ssh" { // expiring key without expiration format. return "", "", errors.New("Invalid ssh key entry - expiration missing") } if len(fields) > 3 { lkey := sshKeyData{} if err := json.Unmarshal([]byte(fields[3]), &lkey); err != nil { // invalid expiration format. return "", "", err } expired, err := CheckExpired(lkey.ExpireOn) if err != nil { return "", "", err } if expired { return "", "", errors.New("Invalid ssh key entry - expired key") } } return user, key[idx+1:], nil } //SerialPort is a type for writing to a named serial port. type SerialPort struct { Port string } func (s *SerialPort) Write(b []byte) (int, error) { c := &serial.Config{Name: s.Port, Baud: 115200} p, err := serial.OpenPort(c) if err != nil { return 0, err } defer p.Close() return p.Write(b) } guest-agent-20220622.00/utils/main_test.go000066400000000000000000000041071425470135300200740ustar00rootroot00000000000000// Copyright 2022 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. package utils import ( "testing" ) func TestContainsString(t *testing.T) { table := []struct { a string slice []string want bool }{ {"a", []string{"a", "b"}, true}, {"c", []string{"a", "b"}, false}, } for _, tt := range table { if got, want := ContainsString(tt.a, tt.slice), tt.want; got != want { t.Errorf("containsString(%s, %v) incorrect return: got %v, want %t", tt.a, tt.slice, got, want) } } } func TestGetUserKey(t *testing.T) { table := []struct { key string user string keyVal string haserr bool }{ {`usera:ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2095-04-23T12:34:56+0000"}`, "usera", `ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2095-04-23T12:34:56+0000"}`, false}, {`usera:ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"2021-04-23T12:34:56+0000"}`, "", "", true}, {`usera:ssh-rsa AAAA1234 google-ssh {"userName":"usera@example.com","expireOn":"Apri 4, 2056"}`, "", "", true}, {`usera:ssh-rsa AAAA1234 google-ssh`, "", "", true}, {" ", "", "", true}, {"ssh-rsa AAAA1234", "", "", true}, {":ssh-rsa AAAA1234", "", "", true}, } for _, tt := range table { u, k, err := GetUserKey(tt.key) e := err != nil if u != tt.user || k != tt.keyVal || e != tt.haserr { t.Errorf("GetUserKey(%s) incorrect return: got user: %s, key: %s, error: %v - want user %s, key: %s, error: %v", tt.key, u, k, e, tt.user, tt.keyVal, tt.haserr) } } }